diff options
Diffstat (limited to 'scene')
135 files changed, 3939 insertions, 886 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index fff9c47d4d..75a1723e04 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -369,12 +369,11 @@ void Area2D::set_monitoring(bool p_enable) { monitoring = p_enable; if (monitoring) { - PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_body_inout); - PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_area_inout); - + PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), callable_mp(this, &Area2D::_body_inout)); + PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), callable_mp(this, &Area2D::_area_inout)); } else { - PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), nullptr, StringName()); - PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), nullptr, StringName()); + PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), Callable()); + PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), Callable()); _clear_monitoring(); } } @@ -530,9 +529,6 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area2D::set_audio_bus_override); ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area2D::is_overriding_audio_bus); - ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout); - ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"))); diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 18a50ae098..24da2ce9ce 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -78,7 +78,6 @@ void AudioStreamPlayer2D::_notification(int p_what) { Vector<Ref<AudioStreamPlayback>> playbacks_to_remove; for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) { - emit_signal(SNAME("finished")); playbacks_to_remove.push_back(playback); } } @@ -91,6 +90,9 @@ void AudioStreamPlayer2D::_notification(int p_what) { active.clear(); set_physics_process_internal(false); } + if (!playbacks_to_remove.is_empty()) { + emit_signal(SNAME("finished")); + } } while (stream_playbacks.size() > max_polyphony) { @@ -110,7 +112,13 @@ StringName AudioStreamPlayer2D::_get_actual_bus() { PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS]; - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); + PhysicsDirectSpaceState2D::PointParameters point_params; + point_params.position = global_pos; + point_params.collision_mask = area_mask; + point_params.collide_with_bodies = false; + point_params.collide_with_areas = true; + + int areas = space_state->intersect_point(point_params, sr, MAX_INTERSECT_AREAS); for (int i = 0; i < areas; i++) { Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider); diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 8195d98f55..b4ee85120e 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -660,7 +660,7 @@ bool Camera2D::is_margin_drawing_enabled() const { void Camera2D::_validate_property(PropertyInfo &property) const { if (!smoothing_enabled && property.name == "smoothing_speed") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 28facd09ce..4b348f12e6 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -50,7 +50,9 @@ void CollisionObject2D::_notification(int p_what) { } if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { - RID space = get_world_2d()->get_space(); + Ref<World2D> world_ref = get_world_2d(); + ERR_FAIL_COND(!world_ref.is_valid()); + RID space = world_ref->get_space(); if (area) { PhysicsServer2D::get_singleton()->area_set_space(rid, space); } else { diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index bf26ec1f20..80c17b6e88 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1168,7 +1168,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_color(material->get_color()); - Ref<GradientTexture> gt = material->get_color_ramp(); + Ref<GradientTexture1D> gt = material->get_color_ramp(); if (gt.is_valid()) { set_color_ramp(gt->get_gradient()); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 6950fefdbe..f1f4d1b769 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -284,7 +284,7 @@ TypedArray<String> GPUParticles2D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); + warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); } if (process_material.is_null()) { diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 1853b3428c..66c0f979ae 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -221,7 +221,7 @@ real_t Light2D::get_shadow_smooth() const { void Light2D::_validate_property(PropertyInfo &property) const { if (!shadow && (property.name == "shadow_color" || property.name == "shadow_filter" || property.name == "shadow_filter_smooth" || property.name == "shadow_item_cull_mask")) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index a8a2639ccf..05d77f8224 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -128,9 +128,9 @@ void LineBuilder::build() { _interpolate_color = gradient != nullptr; bool retrieve_curve = curve != nullptr; bool distance_required = _interpolate_color || - retrieve_curve || - texture_mode == Line2D::LINE_TEXTURE_TILE || - texture_mode == Line2D::LINE_TEXTURE_STRETCH; + retrieve_curve || + texture_mode == Line2D::LINE_TEXTURE_TILE || + texture_mode == Line2D::LINE_TEXTURE_STRETCH; if (distance_required) { total_distance = calculate_total_distance(points); //Adjust totalDistance. diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 0a105826c0..8802a1098a 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -34,19 +34,41 @@ #include "servers/navigation_server_2d.h" void NavigationObstacle2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius); + ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated); + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius"); +} + +void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "radius") { + if (estimate_radius) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } } void NavigationObstacle2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + initialize_agent(); + parent_node2d = Object::cast_to<Node2D>(get_parent()); + if (parent_node2d != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map()); + } set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { + parent_node2d = nullptr; set_physics_process_internal(false); } break; case NOTIFICATION_PARENTED: { parent_node2d = Object::cast_to<Node2D>(get_parent()); - update_agent_shape(); + reevaluate_agent_radius(); } break; case NOTIFICATION_UNPARENTED: { parent_node2d = nullptr; @@ -78,7 +100,22 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const { return warnings; } -void NavigationObstacle2D::update_agent_shape() { +void NavigationObstacle2D::initialize_agent() { + NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0); + NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0); + NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0); +} + +void NavigationObstacle2D::reevaluate_agent_radius() { + if (!estimate_radius) { + NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); + } else if (parent_node2d) { + NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius()); + } +} + +real_t NavigationObstacle2D::estimate_agent_radius() const { if (parent_node2d) { // Estimate the radius of this physics body real_t radius = 0.0; @@ -101,15 +138,21 @@ void NavigationObstacle2D::update_agent_shape() { Vector2 s = parent_node2d->get_global_scale(); radius *= MAX(s.x, s.y); - if (radius == 0.0) { - radius = 1.0; // Never a 0 radius + if (radius > 0.0) { + return radius; } - - // Initialize the Agent as an object - NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); - NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0); - NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0); - NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); - NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0); } + return 1.0; // Never a 0 radius +} + +void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) { + estimate_radius = p_estimate_radius; + notify_property_list_changed(); + reevaluate_agent_radius(); +} + +void NavigationObstacle2D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0."); + radius = p_radius; + reevaluate_agent_radius(); } diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 9cffc2c0c3..a5603f059f 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -40,8 +40,12 @@ class NavigationObstacle2D : public Node { Node2D *parent_node2d = nullptr; RID agent; + bool estimate_radius = true; + real_t radius = 1.0; + protected: static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const override; void _notification(int p_what); public: @@ -52,10 +56,21 @@ public: return agent; } + void set_estimate_radius(bool p_estimate_radius); + bool is_radius_estimated() const { + return estimate_radius; + } + void set_radius(real_t p_radius); + real_t get_radius() const { + return radius; + } + TypedArray<String> get_configuration_warnings() const override; private: - void update_agent_shape(); + void initialize_agent(); + void reevaluate_agent_radius(); + real_t estimate_agent_radius() const; }; #endif diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index cbf0d50c4e..bdb6cda5af 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -346,9 +346,9 @@ void NavigationPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); } void NavigationRegion2D::set_enabled(bool p_enabled) { diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 41288d646f..a43d498a62 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -684,6 +684,24 @@ real_t RigidDynamicBody2D::get_gravity_scale() const { return gravity_scale; } +void RigidDynamicBody2D::set_linear_damp_mode(DampMode p_mode) { + linear_damp_mode = p_mode; + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); +} + +RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_linear_damp_mode() const { + return linear_damp_mode; +} + +void RigidDynamicBody2D::set_angular_damp_mode(DampMode p_mode) { + angular_damp_mode = p_mode; + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); +} + +RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_angular_damp_mode() const { + return angular_damp_mode; +} + void RigidDynamicBody2D::set_linear_damp(real_t p_linear_damp) { ERR_FAIL_COND(p_linear_damp < -1); linear_damp = p_linear_damp; @@ -916,6 +934,12 @@ void RigidDynamicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody2D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody2D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody2D::set_linear_damp_mode); + ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody2D::get_linear_damp_mode); + + ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody2D::set_angular_damp_mode); + ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody2D::get_angular_damp_mode); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody2D::set_linear_damp); ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody2D::get_linear_damp); @@ -992,9 +1016,11 @@ void RigidDynamicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_GROUP("Angular", "angular_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); ADD_GROUP("Applied Forces", "applied_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force"); @@ -1012,6 +1038,9 @@ void RigidDynamicBody2D::_bind_methods() { BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); + BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE); + BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE); + BIND_ENUM_CONSTANT(CCD_MODE_DISABLED); BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY); BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); @@ -1020,7 +1049,7 @@ void RigidDynamicBody2D::_bind_methods() { void RigidDynamicBody2D::_validate_property(PropertyInfo &property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { if (property.name == "center_of_mass") { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } } @@ -1101,7 +1130,7 @@ bool CharacterBody2D::move_and_slide() { } if (motion_mode == MOTION_MODE_GROUNDED) { - _move_and_slide_grounded(delta, was_on_floor, current_platform_velocity); + _move_and_slide_grounded(delta, was_on_floor); } else { _move_and_slide_free(delta); } @@ -1122,14 +1151,11 @@ bool CharacterBody2D::move_and_slide() { return motion_results.size() > 0; } -void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { +void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { Vector2 motion = motion_velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); Vector2 prev_floor_normal = floor_normal; - RID prev_platform_rid = platform_rid; - ObjectID prev_platform_object_id = platform_object_id; - int prev_platform_layer = platform_layer; platform_rid = RID(); platform_object_id = ObjectID(); @@ -1202,12 +1228,8 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo gt.elements[2] -= result.travel; set_global_transform(gt); } - on_floor = true; - platform_rid = prev_platform_rid; - platform_object_id = prev_platform_object_id; - platform_layer = prev_platform_layer; - platform_velocity = p_prev_platform_velocity; - floor_normal = prev_floor_normal; + // Determines if you are on the ground. + _snap_on_floor(true, false); motion_velocity = Vector2(); last_motion = Vector2(); motion = Vector2(); @@ -1693,9 +1715,9 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_GROUP("Free Mode", "free_mode_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle"); @@ -1722,11 +1744,11 @@ void CharacterBody2D::_bind_methods() { void CharacterBody2D::_validate_property(PropertyInfo &property) const { if (motion_mode == MOTION_MODE_FREE) { if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } else { if (property.name == "free_mode_min_slide_angle") { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index d1f52b33f2..2abce4b0a5 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -127,6 +127,11 @@ public: CENTER_OF_MASS_MODE_CUSTOM, }; + enum DampMode { + DAMP_MODE_COMBINE, + DAMP_MODE_REPLACE, + }; + enum CCDMode { CCD_MODE_DISABLED, CCD_MODE_CAST_RAY, @@ -146,8 +151,12 @@ private: Ref<PhysicsMaterial> physics_material_override; real_t gravity_scale = 1.0; - real_t linear_damp = -1.0; - real_t angular_damp = -1.0; + + DampMode linear_damp_mode = DAMP_MODE_COMBINE; + DampMode angular_damp_mode = DAMP_MODE_COMBINE; + + real_t linear_damp = 0.0; + real_t angular_damp = 0.0; Vector2 linear_velocity; real_t angular_velocity = 0.0; @@ -241,6 +250,12 @@ public: void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; + void set_linear_damp_mode(DampMode p_mode); + DampMode get_linear_damp_mode() const; + + void set_angular_damp_mode(DampMode p_mode); + DampMode get_angular_damp_mode() const; + void set_linear_damp(real_t p_linear_damp); real_t get_linear_damp() const; @@ -300,6 +315,7 @@ private: VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::DampMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); class CharacterBody2D : public PhysicsBody2D { @@ -416,7 +432,7 @@ private: MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; void _move_and_slide_free(double p_delta); - void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); + void _move_and_slide_grounded(double p_delta, bool p_was_on_floor); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); Ref<KinematicCollision2D> _get_last_slide_collision(); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 7366be5a7d..f00959bbb6 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -92,7 +92,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler void Polygon2D::_validate_property(PropertyInfo &property) const { if (!invert && property.name == "invert_border") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -554,7 +554,9 @@ void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { Array Polygon2D::_get_bones() const { Array bones; for (int i = 0; i < get_bone_count(); i++) { - bones.push_back(get_bone_path(i)); + // Convert path property to String to avoid errors due to invalid node path in editor, + // because it's relative to the Skeleton2D node and not Polygon2D. + bones.push_back(String(get_bone_path(i))); bones.push_back(get_bone_weights(i)); } return bones; @@ -564,7 +566,8 @@ void Polygon2D::_set_bones(const Array &p_bones) { ERR_FAIL_COND(p_bones.size() & 1); clear_bones(); for (int i = 0; i < p_bones.size(); i += 2) { - add_bone(p_bones[i], p_bones[i + 1]); + // Convert back from String to NodePath. + add_bone(NodePath(p_bones[i]), p_bones[i + 1]); } } @@ -659,7 +662,7 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones"); ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count"); } diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 0a8e9e2a58..7af91f3a8d 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -192,7 +192,16 @@ void RayCast2D::_update_raycast_state() { PhysicsDirectSpaceState2D::RayResult rr; bool prev_collision_state = collided; - if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) { + + PhysicsDirectSpaceState2D::RayParameters ray_params; + ray_params.from = gt.get_origin(); + ray_params.to = gt.xform(to); + ray_params.exclude = exclude; + ray_params.collision_mask = collision_mask; + ray_params.collide_with_bodies = collide_with_bodies; + ray_params.collide_with_areas = collide_with_areas; + + if (dss->intersect_ray(ray_params, rr)) { collided = true; against = rr.collider_id; collision_point = rr.position; diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 5761f19a53..b2302d09db 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -385,7 +385,7 @@ void Sprite2D::_validate_property(PropertyInfo &property) const { } if (!region_enabled && (property.name == "region_rect" || property.name == "region_filter_clip")) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index b546eaefa6..8af7637ffd 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,6 +34,324 @@ #include "servers/navigation_server_2d.h" +Map<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const { + Map<Vector2i, TileSet::CellNeighbor> output; + Ref<TileSet> tile_set = tile_map->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), output); + + TileSet::TileShape shape = tile_set->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (bit) { + case 0: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + break; + default: + ERR_FAIL_V(output); + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (bit) { + case 0: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + break; + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + // Half offset shapes. + TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (bit) { + case 0: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + switch (bit) { + case 0: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } + } + return output; +} + +TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) { + // The way we build the constraint make it easy to detect conflicting constraints. + tile_map = p_tile_map; + + Ref<TileSet> tile_set = tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + TileSet::TileShape shape = tile_set->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 0; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 0; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 0; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 0; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else { + // Half-offset shapes + TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 0; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 0; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 0; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 0; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 0; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } + } + terrain = p_terrain; +} + Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) { // Transform to stacked layout. Vector2i output = p_coords; @@ -168,7 +486,6 @@ int TileMap::get_selected_layer() const { void TileMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - pending_update = true; _clear_internals(); _recreate_internals(); } break; @@ -305,8 +622,8 @@ String TileMap::get_layer_name(int p_layer) const { void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].enabled = p_enabled; - _clear_internals(); - _recreate_internals(); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); emit_signal(SNAME("changed")); update_configuration_warnings(); @@ -320,8 +637,8 @@ bool TileMap::is_layer_enabled(int p_layer) const { void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].modulate = p_modulate; - _clear_internals(); - _recreate_internals(); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); emit_signal(SNAME("changed")); } @@ -333,8 +650,8 @@ Color TileMap::get_layer_modulate(int p_layer) const { void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; - _clear_internals(); - _recreate_internals(); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); emit_signal(SNAME("changed")); update_configuration_warnings(); @@ -348,8 +665,8 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const { void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_origin = p_y_sort_origin; - _clear_internals(); - _recreate_internals(); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); emit_signal(SNAME("changed")); } @@ -361,8 +678,8 @@ int TileMap::get_layer_y_sort_origin(int p_layer) const { void TileMap::set_layer_z_index(int p_layer, int p_z_index) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].z_index = p_z_index; - _clear_internals(); - _recreate_internals(); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); emit_signal(SNAME("changed")); update_configuration_warnings(); @@ -486,8 +803,10 @@ void TileMap::_update_dirty_quadrants() { } for (unsigned int layer = 0; layer < layers.size(); layer++) { + SelfList<TileMapQuadrant>::List &dirty_quadrant_list = layers[layer].dirty_quadrant_list; + // Update the coords cache. - for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { q->self()->map_to_world.clear(); q->self()->world_to_map.clear(); for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { @@ -498,15 +817,18 @@ void TileMap::_update_dirty_quadrants() { } } + // Find TileData that need a runtime modification. + _build_runtime_update_tile_data(dirty_quadrant_list); + // Call the update_dirty_quadrant method on plugins. - _rendering_update_dirty_quadrants(layers[layer].dirty_quadrant_list); - _physics_update_dirty_quadrants(layers[layer].dirty_quadrant_list); - _navigation_update_dirty_quadrants(layers[layer].dirty_quadrant_list); - _scenes_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _rendering_update_dirty_quadrants(dirty_quadrant_list); + _physics_update_dirty_quadrants(dirty_quadrant_list); + _navigation_update_dirty_quadrants(dirty_quadrant_list); + _scenes_update_dirty_quadrants(dirty_quadrant_list); // Redraw the debug canvas_items. RenderingServer *rs = RenderingServer::get_singleton(); - for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { rs->canvas_item_clear(q->self()->debug_canvas_item); Transform2D xform; xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer))); @@ -519,8 +841,13 @@ void TileMap::_update_dirty_quadrants() { } // Clear the list - while (layers[layer].dirty_quadrant_list.first()) { - layers[layer].dirty_quadrant_list.remove(layers[layer].dirty_quadrant_list.first()); + while (dirty_quadrant_list.first()) { + // Clear the runtime tile data. + for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) { + memdelete(kv.value); + } + + dirty_quadrant_list.remove(dirty_quadrant_list.first()); } } @@ -529,37 +856,43 @@ void TileMap::_update_dirty_quadrants() { _recompute_rect_cache(); } -void TileMap::_recreate_internals() { - for (unsigned int layer = 0; layer < layers.size(); layer++) { - // Make sure that _clear_internals() was called prior. - ERR_FAIL_COND_MSG(layers[layer].quadrant_map.size() > 0, "TileMap layer " + itos(layer) + " had a non-empty quadrant map."); - - if (!layers[layer].enabled) { - continue; - } +void TileMap::_recreate_layer_internals(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); - // Upadate the layer internals. - _rendering_update_layer(layer); + // Make sure that _clear_internals() was called prior. + ERR_FAIL_COND_MSG(layers[p_layer].quadrant_map.size() > 0, "TileMap layer " + itos(p_layer) + " had a non-empty quadrant map."); - // Recreate the quadrants. - const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map; - for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { - Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E.key.x, E.key.y)); + if (!layers[p_layer].enabled) { + return; + } - Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk); - if (!Q) { - Q = _create_quadrant(layer, qk); - layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); - } + // Upadate the layer internals. + _rendering_update_layer(p_layer); - Vector2i pk = E.key; - Q->get().cells.insert(pk); + // Recreate the quadrants. + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y)); - _make_quadrant_dirty(Q); + Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk); + if (!Q) { + Q = _create_quadrant(p_layer, qk); + layers[p_layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); } + + Vector2i pk = E.key; + Q->get().cells.insert(pk); + + _make_quadrant_dirty(Q); } - _update_dirty_quadrants(); + _queue_update_dirty_quadrants(); +} + +void TileMap::_recreate_internals() { + for (unsigned int layer = 0; layer < layers.size(); layer++) { + _recreate_layer_internals(layer); + } } void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { @@ -782,7 +1115,13 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { // Get the tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + const TileData *tile_data; + if (q.runtime_tile_data_cache.has(E_cell.value)) { + tile_data = q.runtime_tile_data_cache[E_cell.value]; + } else { + tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + } + Ref<ShaderMaterial> mat = tile_data->get_material(); int z_index = tile_data->get_z_index(); @@ -829,7 +1168,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } // Drawing the tile in the canvas item. - draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); + draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate, tile_data); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -946,7 +1285,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation) { +void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override) { ERR_FAIL_COND(!p_tile_set.is_valid()); ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); @@ -972,7 +1311,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe } // Get tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + const TileData *tile_data = p_tile_data_override ? p_tile_data_override : Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); // Get the tile modulation. Color modulate = tile_data->get_modulate() * p_modulation; @@ -1131,8 +1470,12 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - + const TileData *tile_data; + if (q.runtime_tile_data_cache.has(E_cell->get())) { + tile_data = q.runtime_tile_data_cache[E_cell->get()]; + } else { + tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + } for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) { Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); @@ -1324,7 +1667,12 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + const TileData *tile_data; + if (q.runtime_tile_data_cache.has(E_cell->get())) { + tile_data = q.runtime_tile_data_cache[E_cell->get()]; + } else { + tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + } q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { @@ -1408,7 +1756,12 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + const TileData *tile_data; + if (p_quadrant->runtime_tile_data_cache.has(E_cell->get())) { + tile_data = p_quadrant->runtime_tile_data_cache[E_cell->get()]; + } else { + tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + } Transform2D xform; xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); @@ -1784,6 +2137,242 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat } } +Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints) { + if (!tile_set.is_valid()) { + return Set<TileSet::TerrainsPattern>(); + } + + // Returns all tiles compatible with the given constraints. + Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns; + for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) { + int valid = true; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) { + // Check if the bit is compatible with the constraints. + TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit)); + Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint); + if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { + valid = false; + break; + } + } + } + + if (valid) { + compatible_terrain_tile_patterns.insert(terrain_pattern); + } + } + + return compatible_terrain_tile_patterns; +} + +Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const { + if (!tile_set.is_valid()) { + return Set<TerrainConstraint>(); + } + + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<TerrainConstraint>()); + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Set<TerrainConstraint>()); + + // Build a set of dummy constraints get the constrained points. + Set<TerrainConstraint> dummy_constraints; + for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) { + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides. + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) { + dummy_constraints.insert(TerrainConstraint(this, E->get(), bit, -1)); + } + } + } + + // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it. + Set<TerrainConstraint> constraints; + for (Set<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) { + TerrainConstraint c = E->get(); + + Map<int, int> terrain_count; + + // Count the number of occurrences per terrain. + Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); + for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) { + if (!p_to_replace.has(E_overlapping.key)) { + TileData *neighbor_tile_data = nullptr; + TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key); + if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) { + Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id); + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile)); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + neighbor_tile_data = tile_data; + } + } + } + + int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1; + if (!p_ignore_empty_terrains || terrain >= 0) { + if (!terrain_count.has(terrain)) { + terrain_count[terrain] = 0; + } + terrain_count[terrain] += 1; + } + } + } + + // Get the terrain with the max number of occurrences. + int max = 0; + int max_terrain = -1; + for (const KeyValue<int, int> &E_terrain_count : terrain_count) { + if (E_terrain_count.value > max) { + max = E_terrain_count.value; + max_terrain = E_terrain_count.key; + } + } + + // Set the adequate terrain. + if (max > 0) { + c.set_terrain(max_terrain); + constraints.insert(c); + } + } + + return constraints; +} + +Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { + if (!tile_set.is_valid()) { + return Set<TerrainConstraint>(); + } + + // Compute the constraints needed from the surrounding tiles. + Set<TerrainConstraint> output; + for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor side = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) { + TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain(side)); + output.insert(c); + } + } + + return output; +} + +Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints) { + if (!tile_set.is_valid()) { + return Map<Vector2i, TileSet::TerrainsPattern>(); + } + + // Copy the constraints set. + Set<TerrainConstraint> constraints = p_constraints; + + // Compute all acceptable patterns for each cell. + Map<Vector2i, Set<TileSet::TerrainsPattern>> per_cell_acceptable_tiles; + for (Vector2i cell : p_to_replace) { + per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints); + } + + // Output map. + Map<Vector2i, TileSet::TerrainsPattern> output; + + // Add all positions to a set. + Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace); + while (!to_replace.is_empty()) { + // Compute the minimum number of tile possibilities for each cell. + int min_nb_possibilities = 100000000; + for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { + min_nb_possibilities = MIN(min_nb_possibilities, E.value.size()); + } + + // Get the set of possible cells to fill, out of the most constrained ones. + LocalVector<Vector2i> to_choose_from; + for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { + if (E.value.size() == min_nb_possibilities) { + to_choose_from.push_back(E.key); + } + } + + // Randomly a cell to fill out of the most constrained. + Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)]; + + // Get the list of acceptable pattens for the given cell. + Set<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace]; + if (valid_tiles.is_empty()) { + break; // No possibilities :/ + } + + // Out of the possible patterns, prioritize the one which have the least amount of different terrains. + LocalVector<TileSet::TerrainsPattern> valid_tiles_with_least_amount_of_terrains; + int min_terrain_count = 10000; + LocalVector<int> terrains_counts; + int pattern_index = 0; + for (const TileSet::TerrainsPattern &pattern : valid_tiles) { + Set<int> terrains; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor side = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) { + terrains.insert(pattern.get_terrain(side)); + } + } + min_terrain_count = MIN(min_terrain_count, terrains.size()); + terrains_counts.push_back(terrains.size()); + pattern_index++; + } + pattern_index = 0; + for (const TileSet::TerrainsPattern &pattern : valid_tiles) { + if (terrains_counts[pattern_index] == min_terrain_count) { + valid_tiles_with_least_amount_of_terrains.push_back(pattern); + } + pattern_index++; + } + + // Randomly select a pattern out of the remaining ones. + TileSet::TerrainsPattern selected_terrain_tile_pattern = valid_tiles_with_least_amount_of_terrains[Math::random(0, valid_tiles_with_least_amount_of_terrains.size() - 1)]; + + // Set the selected cell into the output. + output[selected_cell_to_replace] = selected_terrain_tile_pattern; + to_replace.erase(selected_cell_to_replace); + per_cell_acceptable_tiles.erase(selected_cell_to_replace); + + // Add the new constraints from the added tiles. + Set<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern); + for (Set<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) { + constraints.insert(E_constraint->get()); + } + + // Compute valid tiles again for neighbors. + for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor side = TileSet::CellNeighbor(i); + if (is_existing_neighbor(side)) { + Vector2i neighbor = get_neighbor_cell(selected_cell_to_replace, side); + if (to_replace.has(neighbor)) { + per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, neighbor, constraints); + } + } + } + } + return output; +} + +void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains) { + ERR_FAIL_COND(!tile_set.is_valid()); + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); + + Set<Vector2i> coords_set; + for (int i = 0; i < p_coords_array.size(); i++) { + coords_set.insert(p_coords_array[i]); + } + + Set<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains); + + Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints); + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) { + TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile); + } +} + TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell()); const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; @@ -1849,6 +2438,17 @@ void TileMap::clear() { used_rect_cache_dirty = true; } +void TileMap::force_update(int p_layer) { + if (p_layer >= 0) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + _clear_layer_internals(p_layer); + _recreate_layer_internals(p_layer); + } else { + _clear_internals(); + _recreate_internals(); + } +} + void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(format > FORMAT_3); @@ -1958,6 +2558,44 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const { return data; } +void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + if (GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) { + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + // Iterate over the cells of the quadrant. + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + TileMapCell c = get_cell(q.layer, E_cell.value, true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + bool ret = false; + if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + // Create the runtime TileData. + TileData *tile_data_runtime_use = tile_data->duplicate(); + tile_data->set_allow_transform(true); + q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use; + + GDVIRTUAL_CALL(_tile_data_runtime_update, q.layer, E_cell.value, tile_data_runtime_use); + } + } + } + } + q_list_element = q_list_element->next(); + } + } +} + #ifdef TOOLS_ENABLED Rect2 TileMap::_edit_get_rect() const { // Return the visible rect of the tilemap @@ -2072,7 +2710,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { } void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (unsigned int i = 0; i < layers.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE)); @@ -2081,7 +2719,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE)); - p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } } @@ -2335,38 +2973,38 @@ bool TileMap::is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const TileSet::TileShape shape = tile_set->get_tile_shape(); if (shape == TileSet::TILE_SHAPE_SQUARE) { return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; } else { if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; } else { return p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || - p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; } } } @@ -2411,7 +3049,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(is_offset ? 0 : -1, 1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-1, 0); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(is_offset ? 0 : -1, -1); @@ -2435,7 +3073,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(1, is_offset ? 0 : -1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(0, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, is_offset ? 0 : -1); @@ -2462,7 +3100,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(is_offset ? -1 : 0, 1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-1, 0); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(is_offset ? -1 : 0, -1); @@ -2486,7 +3124,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(1, is_offset ? -1 : 0); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(0, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, is_offset ? -1 : 0); @@ -2513,7 +3151,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(-1, 1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-1, 0); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(0, -1); @@ -2536,7 +3174,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(1, -1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(0, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, 0); @@ -2561,7 +3199,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(-1, 1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-2, 1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, 0); @@ -2584,7 +3222,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(1, -1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(1, -2); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(0, -1); @@ -2613,7 +3251,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(-1, 0); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-1, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(0, -1); @@ -2636,7 +3274,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(0, -1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(-1, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, 0); @@ -2661,7 +3299,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { return p_coords + Vector2i(0, 1); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { return p_coords + Vector2i(-1, 1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(-1, 0); @@ -2684,7 +3322,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { return p_coords + Vector2i(1, 0); } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || - (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { return p_coords + Vector2i(1, -1); } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { return p_coords + Vector2i(0, -1); @@ -2989,10 +3627,14 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern); ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern); + ClassDB::bind_method(D_METHOD("set_cells_from_surrounding_terrains", "layer", "cells", "terrain_set", "ignore_empty_terrains"), &TileMap::set_cells_from_surrounding_terrains, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); + ClassDB::bind_method(D_METHOD("force_update", "layer"), &TileMap::force_update, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); @@ -3010,6 +3652,9 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update); + GDVIRTUAL_BIND(_use_tile_data_runtime_update, "layer", "coords"); + GDVIRTUAL_BIND(_tile_data_runtime_update, "layer", "coords", "tile_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable"); @@ -3030,7 +3675,7 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); _tile_set_changed_deferred_update_needed = true; - call_deferred("_tile_set_changed_deferred_update"); + call_deferred(SNAME("_tile_set_changed_deferred_update")); } void TileMap::_tile_set_changed_deferred_update() { @@ -3052,5 +3697,6 @@ TileMap::~TileMap() { if (tile_set.is_valid()) { tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } + _clear_internals(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index e1f38a314c..f260422290 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -79,6 +79,9 @@ struct TileMapQuadrant { // Scenes. Map<Vector2i, String> scenes; + // Runtime TileData cache. + Map<Vector2i, TileData *> runtime_tile_data_cache; + void operator=(const TileMapQuadrant &q) { layer = q.layer; coords = q.coords; @@ -109,6 +112,43 @@ class TileMap : public Node2D { GDCLASS(TileMap, Node2D); public: + class TerrainConstraint { + private: + const TileMap *tile_map; + Vector2i base_cell_coords = Vector2i(); + int bit = -1; + int terrain = -1; + + public: + bool operator<(const TerrainConstraint &p_other) const { + if (base_cell_coords == p_other.base_cell_coords) { + return bit < p_other.bit; + } + return base_cell_coords < p_other.base_cell_coords; + } + + String to_string() const { + return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain); + } + + Vector2i get_base_cell_coords() const { + return base_cell_coords; + } + + Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const; + + void set_terrain(int p_terrain) { + terrain = p_terrain; + } + + int get_terrain() const { + return terrain; + } + + TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); + TerrainConstraint() {} + }; + enum VisibilityMode { VISIBILITY_MODE_DEFAULT, VISIBILITY_MODE_FORCE_SHOW, @@ -174,6 +214,7 @@ private: void _update_dirty_quadrants(); + void _recreate_layer_internals(int p_layer); void _recreate_internals(); void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q); @@ -209,10 +250,15 @@ private: void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant); void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + // Terrains. + Set<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints); + // Set and get tiles from data arrays. void _set_tile_data(int p_layer, const Vector<int> &p_data); Vector<int> _get_tile_data(int p_layer) const; + void _build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _tile_set_changed(); bool _tile_set_changed_deferred_update_needed = false; void _tile_set_changed_deferred_update(); @@ -242,7 +288,7 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; - static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr); // Layers management. int get_layers_count() const; @@ -267,21 +313,30 @@ public: void set_collision_animatable(bool p_enabled); bool is_collision_animatable() const; + // Debug visibility modes. void set_collision_visibility_mode(VisibilityMode p_show_collision); VisibilityMode get_collision_visibility_mode(); void set_navigation_visibility_mode(VisibilityMode p_show_navigation); VisibilityMode get_navigation_visibility_mode(); + // Cells accessors. void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE); int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + // Patterns. Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern); void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern); + // Terrains. + Set<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed. + Set<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed. + Map<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints); // Not exposed. + void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true); + // Not exposed to users TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer); @@ -312,13 +367,21 @@ public: // Fixing a nclearing methods. void fix_invalid_tiles(); + // Clears tiles from a given layer void clear_layer(int p_layer); void clear(); - // Helpers + // Force a TileMap update + void force_update(int p_layer = -1); + + // Helpers? TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords); void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D()); + // Virtual function to modify the TileData at runtime + GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i); + GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *); + // Configuration warnings. TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index d411525707..e459c42e8c 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -334,11 +334,11 @@ void Area3D::set_monitoring(bool p_enable) { monitoring = p_enable; if (monitoring) { - PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_body_inout); - PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_area_inout); + PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), callable_mp(this, &Area3D::_body_inout)); + PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), callable_mp(this, &Area3D::_area_inout)); } else { - PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), nullptr, StringName()); - PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), nullptr, StringName()); + PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), Callable()); _clear_monitoring(); } } @@ -580,6 +580,8 @@ void Area3D::_validate_property(PropertyInfo &property) const { property.hint_string = options; } + + CollisionObject3D::_validate_property(property); } void Area3D::_bind_methods() { @@ -628,9 +630,6 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("overlaps_body", "body"), &Area3D::overlaps_body); ClassDB::bind_method(D_METHOD("overlaps_area", "area"), &Area3D::overlaps_area); - ClassDB::bind_method(D_METHOD("_body_inout"), &Area3D::_body_inout); - ClassDB::bind_method(D_METHOD("_area_inout"), &Area3D::_area_inout); - 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); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 44a685f506..34f748b197 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -292,7 +292,6 @@ void AudioStreamPlayer3D::_notification(int p_what) { Vector<Ref<AudioStreamPlayback>> playbacks_to_remove; for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) { - emit_signal(SNAME("finished")); playbacks_to_remove.push_back(playback); } } @@ -305,6 +304,9 @@ void AudioStreamPlayer3D::_notification(int p_what) { active.clear(); set_physics_process_internal(false); } + if (!playbacks_to_remove.is_empty()) { + emit_signal(SNAME("finished")); + } } while (stream_playbacks.size() > max_polyphony) { @@ -325,7 +327,13 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() { PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); + PhysicsDirectSpaceState3D::PointParameters point_params; + point_params.position = global_pos; + point_params.collision_mask = area_mask; + point_params.collide_with_bodies = false; + point_params.collide_with_areas = true; + + int areas = space_state->intersect_point(point_params, sr, MAX_INTERSECT_AREAS); for (int i = 0; i < areas; i++) { if (!sr[i].collider) { @@ -638,6 +646,8 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { property.hint_string = options; } + + Node3D::_validate_property(property); } void AudioStreamPlayer3D::_bus_layout_changed() { diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 8e89f4fc54..5dc7382197 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -58,6 +58,8 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { property.hint_string = ""; } } + + Node3D::_validate_property(property); } bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 588d2b5018..cc5b7078e3 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -60,17 +60,19 @@ void Camera3D::_update_camera_mode() { void Camera3D::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "fov") { if (mode != PROJECTION_PERSPECTIVE) { - p_property.usage = PROPERTY_USAGE_NOEDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } else if (p_property.name == "size") { if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) { - p_property.usage = PROPERTY_USAGE_NOEDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } else if (p_property.name == "frustum_offset") { if (mode != PROJECTION_FRUSTUM) { - p_property.usage = PROPERTY_USAGE_NOEDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + + Node3D::_validate_property(p_property); } void Camera3D::_update_camera() { diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index fd891a5e13..a166a05c71 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -64,7 +64,9 @@ void CollisionObject3D::_notification(int p_what) { } if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { - RID space = get_world_3d()->get_space(); + Ref<World3D> world_ref = get_world_3d(); + ERR_FAIL_COND(!world_ref.is_valid()); + RID space = world_ref->get_space(); if (area) { PhysicsServer3D::get_singleton()->area_set_space(rid, space); } else { diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 55feb02fce..d347d24c2c 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -213,8 +213,7 @@ TypedArray<String> CPUParticles3D::get_configuration_warnings() const { warnings.push_back(TTR("Nothing is visible because no mesh has been assigned.")); } - 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 (!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())) { warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } @@ -532,6 +531,8 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name.begins_with("scale_curve_") && !split_scale) { property.usage = PROPERTY_USAGE_NONE; } + + Node3D::_validate_property(property); } static uint32_t idhash(uint32_t x) { @@ -1327,7 +1328,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_color(material->get_color()); - Ref<GradientTexture> gt = material->get_color_ramp(); + Ref<GradientTexture1D> gt = material->get_color_ramp(); if (gt.is_valid()) { set_color_ramp(gt->get_gradient()); } diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index c94a99a203..c9f634a5e5 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -158,8 +158,9 @@ Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const { 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; + property.usage = PROPERTY_USAGE_NO_EDITOR; } + VisualInstance3D::_validate_property(property); } TypedArray<String> Decal::get_configuration_warnings() const { diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp new file mode 100644 index 0000000000..694defd7dc --- /dev/null +++ b/scene/3d/fog_volume.cpp @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* fog_volume.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 "fog_volume.h" + +/////////////////////////// + +void FogVolume::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &FogVolume::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &FogVolume::get_extents); + ClassDB::bind_method(D_METHOD("set_shape", "shape"), &FogVolume::set_shape); + ClassDB::bind_method(D_METHOD("get_shape"), &FogVolume::get_shape); + ClassDB::bind_method(D_METHOD("set_material", "material"), &FogVolume::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &FogVolume::get_material); + + 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, "shape", PROPERTY_HINT_ENUM, "Ellipsoid,Box,World"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "FogMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void FogVolume::_validate_property(PropertyInfo &property) const { + if (property.name == "extents" && shape == RS::FOG_VOLUME_SHAPE_WORLD) { + property.usage = PROPERTY_USAGE_NONE; + return; + } + VisualInstance3D::_validate_property(property); +} + +void FogVolume::set_extents(const Vector3 &p_extents) { + extents = p_extents; + extents.x = MAX(0.0, extents.x); + extents.y = MAX(0.0, extents.y); + extents.z = MAX(0.0, extents.z); + RS::get_singleton()->fog_volume_set_extents(_get_volume(), extents); + update_gizmos(); +} + +Vector3 FogVolume::get_extents() const { + return extents; +} + +void FogVolume::set_shape(RS::FogVolumeShape p_type) { + shape = p_type; + RS::get_singleton()->fog_volume_set_shape(_get_volume(), shape); + RS::get_singleton()->instance_set_ignore_culling(get_instance(), shape == RS::FOG_VOLUME_SHAPE_WORLD); + update_gizmos(); + notify_property_list_changed(); +} + +RS::FogVolumeShape FogVolume::get_shape() const { + return shape; +} + +void FogVolume::set_material(const Ref<Material> &p_material) { + material = p_material; + RID material_rid; + if (material.is_valid()) { + material_rid = material->get_rid(); + } + RS::get_singleton()->fog_volume_set_material(_get_volume(), material_rid); + update_gizmos(); +} + +Ref<Material> FogVolume::get_material() const { + return material; +} + +AABB FogVolume::get_aabb() const { + if (shape != RS::FOG_VOLUME_SHAPE_WORLD) { + return AABB(-extents, extents * 2); + } + return AABB(); +} + +TypedArray<String> FogVolume::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment(); + + if (environment.is_valid() && !environment->is_volumetric_fog_enabled()) { + warnings.push_back(("Fog Volumes need volumetric fog to be enabled in the scene's Environment in order to be visible.")); + } + + return warnings; +} + +FogVolume::FogVolume() { + volume = RS::get_singleton()->fog_volume_create(); + RS::get_singleton()->fog_volume_set_shape(volume, RS::FOG_VOLUME_SHAPE_BOX); + set_base(volume); +} + +FogVolume::~FogVolume() { + RS::get_singleton()->free(volume); +} diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h new file mode 100644 index 0000000000..0807fb22e6 --- /dev/null +++ b/scene/3d/fog_volume.h @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* fog_volume.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 FOG_VOLUME_H +#define FOG_VOLUME_H + +#include "core/templates/rid.h" +#include "scene/3d/visual_instance_3d.h" +#include "scene/main/node.h" +#include "scene/main/viewport.h" +#include "scene/resources/material.h" + +class FogVolume : public VisualInstance3D { + GDCLASS(FogVolume, VisualInstance3D); + + Vector3 extents = Vector3(1, 1, 1); + Ref<Material> material; + RS::FogVolumeShape shape = RS::FOG_VOLUME_SHAPE_BOX; + + RID volume; + +protected: + _FORCE_INLINE_ RID _get_volume() { return volume; } + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + +public: + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + void set_shape(RS::FogVolumeShape p_type); + RS::FogVolumeShape get_shape() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + virtual AABB get_aabb() const override; + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } + TypedArray<String> get_configuration_warnings() const override; + + FogVolume(); + ~FogVolume(); +}; + +#endif // FOG_VOLUME_H diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index ea6242b669..b35a45576f 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -277,7 +277,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - 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.")); + warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); } bool meshes_found = false; @@ -388,6 +388,8 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const { return; } } + + GeometryInstance3D::_validate_property(property); } 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) { diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 9127168c58..6ac9364b1a 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -288,15 +288,12 @@ void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, cons 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)); + (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); } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index c787ba5087..5d9ae019c2 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -197,24 +197,14 @@ 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 = 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 = PROPERTY_USAGE_NONE; + property.usage = PROPERTY_USAGE_NO_EDITOR; } if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") { + // Angular distance is only used in DirectionalLight3D. property.usage = PROPERTY_USAGE_NONE; } + VisualInstance3D::_validate_property(property); } void Light3D::_bind_methods() { @@ -361,6 +351,7 @@ Light3D::~Light3D() { void DirectionalLight3D::set_shadow_mode(ShadowMode p_mode) { shadow_mode = p_mode; RS::get_singleton()->light_directional_set_shadow_mode(light, RS::LightDirectionalShadowMode(p_mode)); + notify_property_list_changed(); } DirectionalLight3D::ShadowMode DirectionalLight3D::get_shadow_mode() const { @@ -385,6 +376,25 @@ bool DirectionalLight3D::is_sky_only() const { return sky_only; } +void DirectionalLight3D::_validate_property(PropertyInfo &property) const { + if (shadow_mode == SHADOW_ORTHOGONAL && (property.name == "directional_shadow_split_1" || property.name == "directional_shadow_blend_splits")) { + // Split 2 and split blending are only used with the PSSM 2 Splits and PSSM 4 Splits shadow modes. + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if ((shadow_mode == SHADOW_ORTHOGONAL || shadow_mode == SHADOW_PARALLEL_2_SPLITS) && (property.name == "directional_shadow_split_2" || property.name == "directional_shadow_split_3")) { + // Splits 3 and 4 are only used with the PSSM 4 Splits shadow mode. + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (property.name == "light_size" || property.name == "light_projector" || property.name == "light_specular") { + // Not implemented in DirectionalLight3D (`light_size` is replaced by `light_angular_distance`). + property.usage = PROPERTY_USAGE_NONE; + } + + Light3D::_validate_property(property); +} + 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); @@ -400,8 +410,8 @@ void DirectionalLight3D::_bind_methods() { 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); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET); 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_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START); 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); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index f788c323f7..a9f5ce27b4 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -152,6 +152,7 @@ private: protected: static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; public: void set_shadow_mode(ShadowMode p_mode); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index b56f0fd145..1b5d4ad243 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -200,9 +200,9 @@ void LightmapGIData::_bind_methods() { 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"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); } LightmapGIData::LightmapGIData() { @@ -1370,6 +1370,7 @@ void LightmapGI::_validate_property(PropertyInfo &property) const { if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { property.usage = PROPERTY_USAGE_NONE; } + VisualInstance3D::_validate_property(property); } void LightmapGI::_bind_methods() { diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index c148f95461..ae686143e4 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -244,7 +244,7 @@ Node *MeshInstance3D::create_trimesh_collision_node() { StaticBody3D *static_body = memnew(StaticBody3D); CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(shape); - static_body->add_child(cshape); + static_body->add_child(cshape, true); return static_body; } @@ -253,7 +253,7 @@ void MeshInstance3D::create_trimesh_collision() { ERR_FAIL_COND(!static_body); static_body->set_name(String(get_name()) + "_col"); - add_child(static_body); + add_child(static_body, true); if (get_owner()) { CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(0)); static_body->set_owner(get_owner()); @@ -274,7 +274,7 @@ Node *MeshInstance3D::create_convex_collision_node(bool p_clean, bool p_simplify StaticBody3D *static_body = memnew(StaticBody3D); CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(shape); - static_body->add_child(cshape); + static_body->add_child(cshape, true); return static_body; } @@ -283,7 +283,7 @@ void MeshInstance3D::create_convex_collision(bool p_clean, bool p_simplify) { ERR_FAIL_COND(!static_body); static_body->set_name(String(get_name()) + "_col"); - add_child(static_body); + add_child(static_body, true); if (get_owner()) { CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(0)); static_body->set_owner(get_owner()); @@ -306,7 +306,7 @@ Node *MeshInstance3D::create_multiple_convex_collisions_node() { for (int i = 0; i < shapes.size(); i++) { CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(shapes[i]); - static_body->add_child(cshape); + static_body->add_child(cshape, true); } return static_body; } @@ -316,7 +316,7 @@ void MeshInstance3D::create_multiple_convex_collisions() { ERR_FAIL_COND(!static_body); static_body->set_name(String(get_name()) + "_col"); - add_child(static_body); + add_child(static_body, true); if (get_owner()) { static_body->set_owner(get_owner()); int count = static_body->get_child_count(); @@ -460,7 +460,7 @@ void MeshInstance3D::create_debug_tangents() { MeshInstance3D *mi = memnew(MeshInstance3D); mi->set_mesh(am); mi->set_name("DebugTangents"); - add_child(mi); + add_child(mi, true); #ifdef TOOLS_ENABLED if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) { diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 20ffc3b00e..f9fff802e0 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -35,19 +35,41 @@ #include "servers/navigation_server_3d.h" void NavigationObstacle3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius); + ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated); + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle3D::get_radius); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_radius", "get_radius"); +} + +void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "radius") { + if (estimate_radius) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } } void NavigationObstacle3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + initialize_agent(); + parent_node3d = Object::cast_to<Node3D>(get_parent()); + if (parent_node3d != 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(), parent_node3d->get_world_3d()->get_navigation_map()); + } set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { + parent_node3d = nullptr; set_physics_process_internal(false); } break; case NOTIFICATION_PARENTED: { parent_node3d = Object::cast_to<Node3D>(get_parent()); - update_agent_shape(); + reevaluate_agent_radius(); } break; case NOTIFICATION_UNPARENTED: { parent_node3d = nullptr; @@ -86,7 +108,22 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { return warnings; } -void NavigationObstacle3D::update_agent_shape() { +void NavigationObstacle3D::initialize_agent() { + 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_max_speed(agent, 0.0); +} + +void NavigationObstacle3D::reevaluate_agent_radius() { + if (!estimate_radius) { + NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); + } else if (parent_node3d) { + NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius()); + } +} + +real_t NavigationObstacle3D::estimate_agent_radius() const { if (parent_node3d) { // Estimate the radius of this physics body real_t radius = 0.0; @@ -110,15 +147,21 @@ void NavigationObstacle3D::update_agent_shape() { 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) { + return 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); } + return 1.0; // Never a 0 radius +} + +void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) { + estimate_radius = p_estimate_radius; + notify_property_list_changed(); + reevaluate_agent_radius(); +} + +void NavigationObstacle3D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0."); + radius = p_radius; + reevaluate_agent_radius(); } diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index ab0b158303..12c813ab08 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -39,8 +39,12 @@ class NavigationObstacle3D : public Node { Node3D *parent_node3d = nullptr; RID agent; + bool estimate_radius = true; + real_t radius = 1.0; + protected: static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const override; void _notification(int p_what); public: @@ -51,10 +55,21 @@ public: return agent; } + void set_estimate_radius(bool p_estimate_radius); + bool is_radius_estimated() const { + return estimate_radius; + } + void set_radius(real_t p_radius); + real_t get_radius() const { + return radius; + } + TypedArray<String> get_configuration_warnings() const override; private: - void update_agent_shape(); + void initialize_agent(); + void reevaluate_agent_radius(); + real_t estimate_agent_radius() const; }; #endif diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 5293dd3f0a..b7b88c7135 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -44,7 +44,7 @@ definition of invalidation: global is invalid 1) If a node sets a LOCAL, it produces an invalidation of everything above - a) If above is invalid, don't keep invalidating upwards + . 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 often, and using affine inverses is slow @@ -56,7 +56,7 @@ definition of invalidation: NONE dirty, LOCAL dirty, GLOBAL dirty 1) If a node sets a LOCAL, it must climb the tree and set it as GLOBAL dirty - a) marking GLOBALs as dirty up all the tree must be done always + . a) marking GLOBALs as dirty up all the tree must be done always 2) If a node sets a GLOBAL, it marks local as dirty, and that's all? //is clearing the dirty state correct in this case? @@ -94,11 +94,6 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { return; } - /* - if (data.dirty&DIRTY_GLOBAL) - return; //already dirty - */ - data.children_lock++; for (Node3D *&E : data.children) { @@ -220,6 +215,13 @@ void Node3D::_notification(int p_what) { } } +void Node3D::set_basis(const Basis &p_basis) { + set_transform(Transform3D(p_basis, data.local_transform.origin)); +} +void Node3D::set_quaternion(const Quaternion &p_quaternion) { + set_transform(Transform3D(Basis(p_quaternion), data.local_transform.origin)); +} + void Node3D::set_transform(const Transform3D &p_transform) { data.local_transform = p_transform; data.dirty |= DIRTY_VECTORS; @@ -229,11 +231,17 @@ void Node3D::set_transform(const Transform3D &p_transform) { } } +Basis Node3D::get_basis() const { + return get_transform().basis; +} +Quaternion Node3D::get_quaternion() const { + return Quaternion(get_transform().basis); +} + 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; + Transform3D xform = (data.parent && !data.top_level_active) + ? data.parent->get_global_transform().affine_inverse() * p_transform + : p_transform; set_transform(xform); } @@ -308,6 +316,45 @@ void Node3D::set_position(const Vector3 &p_position) { } } +void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { + if (data.rotation_edit_mode == p_mode) { + return; + } + data.rotation_edit_mode = p_mode; + notify_property_list_changed(); +} + +Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const { + return data.rotation_edit_mode; +} + +void Node3D::set_rotation_order(RotationOrder p_order) { + Basis::EulerOrder order = Basis::EulerOrder(p_order); + + if (data.rotation_order == order) { + return; + } + + ERR_FAIL_INDEX(int32_t(order), 6); + + if (data.dirty & DIRTY_VECTORS) { + data.rotation = data.local_transform.basis.get_euler_normalized(order); + data.scale = data.local_transform.basis.get_scale(); + data.dirty &= ~DIRTY_VECTORS; + } else { + data.rotation = Basis::from_euler(data.rotation, data.rotation_order).get_euler_normalized(order); + } + + data.rotation_order = order; + //changing rotation order should not affect transform + + notify_property_list_changed(); //will change rotation +} + +Node3D::RotationOrder Node3D::get_rotation_order() const { + return RotationOrder(data.rotation_order); +} + void Node3D::set_rotation(const Vector3 &p_euler_rad) { if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); @@ -324,7 +371,7 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { void Node3D::set_scale(const Vector3 &p_scale) { if (data.dirty & DIRTY_VECTORS) { - data.rotation = data.local_transform.basis.get_rotation(); + data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); data.dirty &= ~DIRTY_VECTORS; } @@ -343,7 +390,7 @@ Vector3 Node3D::get_position() const { Vector3 Node3D::get_rotation() const { if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); - data.rotation = data.local_transform.basis.get_rotation(); + data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); data.dirty &= ~DIRTY_VECTORS; } @@ -354,7 +401,7 @@ Vector3 Node3D::get_rotation() const { Vector3 Node3D::get_scale() const { if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); - data.rotation = data.local_transform.basis.get_rotation(); + data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); data.dirty &= ~DIRTY_VECTORS; } @@ -780,6 +827,24 @@ NodePath Node3D::get_visibility_parent() const { return visibility_parent_path; } +void Node3D::_validate_property(PropertyInfo &property) const { + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_BASIS && property.name == "basis") { + property.usage = 0; + } + if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && property.name == "scale") { + property.usage = 0; + } + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_QUATERNION && property.name == "quaternion") { + property.usage = 0; + } + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation") { + property.usage = 0; + } + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation_order") { + property.usage = 0; + } +} + 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); @@ -787,8 +852,16 @@ void Node3D::_bind_methods() { 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_order", "order"), &Node3D::set_rotation_order); + ClassDB::bind_method(D_METHOD("get_rotation_order"), &Node3D::get_rotation_order); + ClassDB::bind_method(D_METHOD("set_rotation_edit_mode", "edit_mode"), &Node3D::set_rotation_edit_mode); + ClassDB::bind_method(D_METHOD("get_rotation_edit_mode"), &Node3D::get_rotation_edit_mode); 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_quaternion", "quaternion"), &Node3D::set_quaternion); + ClassDB::bind_method(D_METHOD("get_quaternion"), &Node3D::get_quaternion); + ClassDB::bind_method(D_METHOD("set_basis", "basis"), &Node3D::set_basis); + ClassDB::bind_method(D_METHOD("get_basis"), &Node3D::get_basis); 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_node_3d"), &Node3D::get_parent_node_3d); @@ -848,15 +921,29 @@ void Node3D::_bind_methods() { BIND_CONSTANT(NOTIFICATION_EXIT_WORLD); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); + BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_EULER); + BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION); + BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_BASIS); + + BIND_ENUM_CONSTANT(ROTATION_ORDER_XYZ); + BIND_ENUM_CONSTANT(ROTATION_ORDER_XZY); + BIND_ENUM_CONSTANT(ROTATION_ORDER_YXZ); + BIND_ENUM_CONSTANT(ROTATION_ORDER_YZX); + BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY); + BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX); + //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::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::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion"); + ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); - ADD_GROUP("Matrix", ""); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index d6dcdd96fe..3e21eb12be 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -51,6 +51,23 @@ class Node3D : public Node { GDCLASS(Node3D, Node); OBJ_CATEGORY("3D"); +public: + enum RotationEditMode { + ROTATION_EDIT_MODE_EULER, + ROTATION_EDIT_MODE_QUATERNION, + ROTATION_EDIT_MODE_BASIS, + }; + + enum RotationOrder { + ROTATION_ORDER_XYZ, + ROTATION_ORDER_XZY, + ROTATION_ORDER_YXZ, + ROTATION_ORDER_YZX, + ROTATION_ORDER_ZXY, + ROTATION_ORDER_ZYX + }; + +private: enum TransformDirty { DIRTY_NONE = 0, DIRTY_VECTORS = 1, @@ -63,8 +80,10 @@ class Node3D : public Node { struct Data { mutable Transform3D global_transform; mutable Transform3D local_transform; + mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ; mutable Vector3 rotation; mutable Vector3 scale = Vector3(1, 1, 1); + mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER; mutable int dirty = DIRTY_NONE; @@ -116,6 +135,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + public: enum { NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, @@ -130,17 +151,28 @@ public: Ref<World3D> get_world_3d() const; void set_position(const Vector3 &p_position); + + void set_rotation_edit_mode(RotationEditMode p_mode); + RotationEditMode get_rotation_edit_mode() const; + + void set_rotation_order(RotationOrder p_order); void set_rotation(const Vector3 &p_euler_rad); void set_scale(const Vector3 &p_scale); Vector3 get_position() const; + + RotationOrder get_rotation_order() const; Vector3 get_rotation() const; Vector3 get_scale() const; void set_transform(const Transform3D &p_transform); + void set_basis(const Basis &p_basis); + void set_quaternion(const Quaternion &p_quaternion); void set_global_transform(const Transform3D &p_transform); Transform3D get_transform() const; + Basis get_basis() const; + Quaternion get_quaternion() const; Transform3D get_global_transform() const; #ifdef TOOLS_ENABLED @@ -214,4 +246,7 @@ public: Node3D(); }; +VARIANT_ENUM_CAST(Node3D::RotationEditMode) +VARIANT_ENUM_CAST(Node3D::RotationOrder) + #endif // NODE_3D_H diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index f3e174c01b..6c548edc74 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -130,8 +130,8 @@ void Occluder3D::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices"); } Occluder3D::Occluder3D() { diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 9ea37e4bfa..a0eac76e39 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -248,6 +248,7 @@ void PathFollow3D::_validate_property(PropertyInfo &property) const { property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater"; } + Node3D::_validate_property(property); } TypedArray<String> PathFollow3D::get_configuration_warnings() const { diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 8cb348d6e3..d0506227b4 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -758,8 +758,26 @@ real_t RigidDynamicBody3D::get_gravity_scale() const { return gravity_scale; } +void RigidDynamicBody3D::set_linear_damp_mode(DampMode p_mode) { + linear_damp_mode = p_mode; + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); +} + +RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_linear_damp_mode() const { + return linear_damp_mode; +} + +void RigidDynamicBody3D::set_angular_damp_mode(DampMode p_mode) { + angular_damp_mode = p_mode; + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); +} + +RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_angular_damp_mode() const { + return angular_damp_mode; +} + void RigidDynamicBody3D::set_linear_damp(real_t p_linear_damp) { - ERR_FAIL_COND(p_linear_damp < -1); + ERR_FAIL_COND(p_linear_damp < 0.0); linear_damp = p_linear_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp); } @@ -769,7 +787,7 @@ real_t RigidDynamicBody3D::get_linear_damp() const { } void RigidDynamicBody3D::set_angular_damp(real_t p_angular_damp) { - ERR_FAIL_COND(p_angular_damp < -1); + ERR_FAIL_COND(p_angular_damp < 0.0); angular_damp = p_angular_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } @@ -970,6 +988,12 @@ void RigidDynamicBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody3D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody3D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody3D::set_linear_damp_mode); + ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody3D::get_linear_damp_mode); + + ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody3D::set_angular_damp_mode); + ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody3D::get_angular_damp_mode); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody3D::set_linear_damp); ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody3D::get_linear_damp); @@ -1035,10 +1059,12 @@ void RigidDynamicBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); 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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_GROUP("Angular", "angular_"); 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_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); 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_index"), PropertyInfo(Variant::INT, "local_shape_index"))); 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_index"), PropertyInfo(Variant::INT, "local_shape_index"))); @@ -1051,14 +1077,18 @@ void RigidDynamicBody3D::_bind_methods() { BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); + + BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE); + BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE); } void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { if (property.name == "center_of_mass") { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } + PhysicsBody3D::_validate_property(property); } RigidDynamicBody3D::RigidDynamicBody3D() : @@ -1903,8 +1933,8 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_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, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_GROUP("Free Mode", "free_mode_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); ADD_GROUP("Floor", "floor_"); @@ -1930,9 +1960,10 @@ void CharacterBody3D::_bind_methods() { void CharacterBody3D::_validate_property(PropertyInfo &property) const { if (motion_mode == MOTION_MODE_FREE) { if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } + PhysicsBody3D::_validate_property(property); } CharacterBody3D::CharacterBody3D() : @@ -2823,6 +2854,12 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone3D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone3D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &PhysicalBone3D::set_linear_damp_mode); + ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &PhysicalBone3D::get_linear_damp_mode); + + ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &PhysicalBone3D::set_angular_damp_mode); + ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &PhysicalBone3D::get_angular_damp_mode); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &PhysicalBone3D::set_linear_damp); ClassDB::bind_method(D_METHOD("get_linear_damp"), &PhysicalBone3D::get_linear_damp); @@ -2843,10 +2880,15 @@ void PhysicalBone3D::_bind_methods() { 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"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); - 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::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode"); + 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::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode"); + 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::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE); + BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE); + BIND_ENUM_CONSTANT(JOINT_TYPE_NONE); BIND_ENUM_CONSTANT(JOINT_TYPE_PIN); BIND_ENUM_CONSTANT(JOINT_TYPE_CONE); @@ -3060,7 +3102,7 @@ void PhysicalBone3D::set_joint_rotation(const Vector3 &p_euler_rad) { } Vector3 PhysicalBone3D::get_joint_rotation() const { - return joint_offset.basis.get_rotation(); + return joint_offset.basis.get_euler_normalized(); } const Transform3D &PhysicalBone3D::get_body_offset() const { @@ -3144,8 +3186,27 @@ real_t PhysicalBone3D::get_gravity_scale() const { return gravity_scale; } +void PhysicalBone3D::set_linear_damp_mode(DampMode p_mode) { + linear_damp_mode = p_mode; + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); +} + +PhysicalBone3D::DampMode PhysicalBone3D::get_linear_damp_mode() const { + return linear_damp_mode; +} + +void PhysicalBone3D::set_angular_damp_mode(DampMode p_mode) { + angular_damp_mode = p_mode; + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); +} + +PhysicalBone3D::DampMode PhysicalBone3D::get_angular_damp_mode() const { + return angular_damp_mode; +} + void PhysicalBone3D::set_linear_damp(real_t p_linear_damp) { - ERR_FAIL_COND(p_linear_damp < -1); + ERR_FAIL_COND(p_linear_damp < 0); + linear_damp = p_linear_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp); } @@ -3155,7 +3216,8 @@ real_t PhysicalBone3D::get_linear_damp() const { } void PhysicalBone3D::set_angular_damp(real_t p_angular_damp) { - ERR_FAIL_COND(p_angular_damp < -1); + ERR_FAIL_COND(p_angular_damp < 0); + angular_damp = p_angular_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 5677df730c..2ea796d335 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -143,6 +143,11 @@ public: CENTER_OF_MASS_MODE_CUSTOM, }; + enum DampMode { + DAMP_MODE_COMBINE, + DAMP_MODE_REPLACE, + }; + private: bool can_sleep = true; bool lock_rotation = false; @@ -160,8 +165,12 @@ private: Vector3 angular_velocity; Basis inverse_inertia_tensor; real_t gravity_scale = 1.0; - real_t linear_damp = -1.0; - real_t angular_damp = -1.0; + + DampMode linear_damp_mode = DAMP_MODE_COMBINE; + DampMode angular_damp_mode = DAMP_MODE_COMBINE; + + real_t linear_damp = 0.0; + real_t angular_damp = 0.0; bool sleeping = false; bool ccd = false; @@ -265,6 +274,12 @@ public: void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; + void set_linear_damp_mode(DampMode p_mode); + DampMode get_linear_damp_mode() const; + + void set_angular_damp_mode(DampMode p_mode); + DampMode get_angular_damp_mode() const; + void set_linear_damp(real_t p_linear_damp); real_t get_linear_damp() const; @@ -310,6 +325,7 @@ private: VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::DampMode); class KinematicCollision3D; @@ -494,6 +510,11 @@ class PhysicalBone3D : public PhysicsBody3D { GDCLASS(PhysicalBone3D, PhysicsBody3D); public: + enum DampMode { + DAMP_MODE_COMBINE, + DAMP_MODE_REPLACE, + }; + enum JointType { JOINT_TYPE_NONE, JOINT_TYPE_PIN, @@ -632,10 +653,14 @@ private: 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; + DampMode linear_damp_mode = DAMP_MODE_COMBINE; + DampMode angular_damp_mode = DAMP_MODE_COMBINE; + + real_t linear_damp = 0.0; + real_t angular_damp = 0.0; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -698,6 +723,12 @@ public: void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; + void set_linear_damp_mode(DampMode p_mode); + DampMode get_linear_damp_mode() const; + + void set_angular_damp_mode(DampMode p_mode); + DampMode get_angular_damp_mode() const; + void set_linear_damp(real_t p_linear_damp); real_t get_linear_damp() const; @@ -725,5 +756,6 @@ private: }; VARIANT_ENUM_CAST(PhysicalBone3D::JointType); +VARIANT_ENUM_CAST(PhysicalBone3D::DampMode); #endif // PHYSICS_BODY__H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index fd4c6e7416..646f659996 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -212,9 +212,16 @@ void RayCast3D::_update_raycast_state() { to = Vector3(0, 0.01, 0); } - PhysicsDirectSpaceState3D::RayResult rr; + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = gt.get_origin(); + ray_params.to = gt.xform(to); + ray_params.exclude = exclude; + ray_params.collision_mask = collision_mask; + ray_params.collide_with_bodies = collide_with_bodies; + ray_params.collide_with_areas = collide_with_areas; - if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) { + PhysicsDirectSpaceState3D::RayResult rr; + if (dss->intersect_ray(ray_params, rr)) { collided = true; against = rr.collider_id; collision_point = rr.position; diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index 719dbedd94..40d4d822c9 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -185,9 +185,10 @@ Vector<Face3> ReflectionProbe::get_faces(uint32_t p_usage_flags) const { void ReflectionProbe::_validate_property(PropertyInfo &property) const { if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") { if (ambient_mode != AMBIENT_COLOR) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } + VisualInstance3D::_validate_property(property); } void ReflectionProbe::_bind_methods() { diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index d5fb1fa6ab..d890609e23 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -68,7 +68,7 @@ void RemoteTransform3D::_update_remote() { Transform3D our_trans = get_global_transform(); if (update_remote_rotation) { - n->set_rotation(our_trans.basis.get_rotation()); + n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order()))); } if (update_remote_scale) { @@ -90,7 +90,7 @@ void RemoteTransform3D::_update_remote() { Transform3D our_trans = get_transform(); if (update_remote_rotation) { - n->set_rotation(our_trans.basis.get_rotation()); + n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order()))); } if (update_remote_scale) { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index e3744ad5e9..d4d3c0ebe1 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -155,13 +155,13 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < bones.size(); i++) { 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::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::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } #ifndef _3D_DISABLED diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 2e788051f4..1498955ec0 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -351,6 +351,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &property) const { property.hint_string = ""; } } + + Node::_validate_property(property); } void SkeletonIK3D::_bind_methods() { diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 4cf08e7c99..ccb25bcd4c 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -137,8 +137,7 @@ class SkeletonIK3D : public Node { FabrikInverseKinematic::Task *task = nullptr; protected: - virtual void - _validate_property(PropertyInfo &property) const override; + virtual void _validate_property(PropertyInfo &property) const override; static void _bind_methods(); virtual void _notification(int p_what); diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 116cab19b1..0b5af8823b 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -149,10 +149,24 @@ void SpringArm3D::process_spring() { //use camera rotation, but spring arm position Transform3D base_transform = camera->get_global_transform(); base_transform.origin = get_global_transform().origin; - get_world_3d()->get_direct_space_state()->cast_motion(camera->get_pyramid_shape_rid(), base_transform, motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask); + + PhysicsDirectSpaceState3D::ShapeParameters shape_params; + shape_params.shape_rid = camera->get_pyramid_shape_rid(); + shape_params.transform = base_transform; + shape_params.motion = motion; + shape_params.exclude = excluded_objects; + shape_params.collision_mask = mask; + + get_world_3d()->get_direct_space_state()->cast_motion(shape_params, motion_delta, motion_delta_unsafe); } else { + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = get_global_transform().origin; + ray_params.to = get_global_transform().origin + motion; + ray_params.exclude = excluded_objects; + ray_params.collision_mask = mask; + 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); + bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(ray_params, r); if (intersected) { real_t dist = get_global_transform().origin.distance_to(r.position); dist -= margin; @@ -160,7 +174,14 @@ void SpringArm3D::process_spring() { } } } else { - get_world_3d()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask); + PhysicsDirectSpaceState3D::ShapeParameters shape_params; + shape_params.shape_rid = shape->get_rid(); + shape_params.transform = get_global_transform(); + shape_params.motion = motion; + shape_params.exclude = excluded_objects; + shape_params.collision_mask = mask; + + get_world_3d()->get_direct_space_state()->cast_motion(shape_params, motion_delta, motion_delta_unsafe); } current_spring_length = spring_length * motion_delta; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 349a534680..90af70e7c2 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -743,6 +743,8 @@ void Sprite3D::_validate_property(PropertyInfo &property) const { if (property.name == "frame_coords") { property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } + + SpriteBase3D::_validate_property(property); } void Sprite3D::_bind_methods() { @@ -1015,6 +1017,8 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { } property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } + + SpriteBase3D::_validate_property(property); } void AnimatedSprite3D::_notification(int p_what) { diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 6761fdd944..90db093137 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -124,7 +124,7 @@ void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) { Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin; chassis_velocity_at_contactPoint = s->get_linear_velocity() + - (s->get_angular_velocity()).cross(relpos); // * mPos); + (s->get_angular_velocity()).cross(relpos); // * mPos); real_t projVel = m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); if (project >= real_t(-0.1)) { @@ -407,7 +407,13 @@ 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, get_collision_mask()); + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = source; + ray_params.to = target; + ray_params.exclude = exclude; + ray_params.collision_mask = get_collision_mask(); + + bool col = ss->intersect_ray(ray_params, rr); wheel.m_raycastInfo.m_groundObject = nullptr; @@ -444,7 +450,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { //chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); chassis_velocity_at_contactPoint = s->get_linear_velocity() + - (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin); // * mPos); + (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin); // * mPos); real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); @@ -771,7 +777,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { VehicleWheel3D &wheelInfo = *wheels[wheel]; Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS - - s->get_transform().origin; + s->get_transform().origin; if (m_forwardImpulse[wheel] != real_t(0.)) { s->apply_impulse(m_forwardWS[wheel] * (m_forwardImpulse[wheel]), rel_pos); diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 73c2887983..d407592376 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -152,9 +152,18 @@ Ref<Material> GeometryInstance3D::get_material_override() const { return material_override; } +void GeometryInstance3D::set_transparecy(float p_transparency) { + transparency = CLAMP(p_transparency, 0.0f, 1.0f); + RS::get_singleton()->instance_geometry_set_transparency(get_instance(), transparency); +} + +float GeometryInstance3D::get_transparency() const { + return transparency; +} + 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); + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode); update_configuration_warnings(); } @@ -164,7 +173,7 @@ float GeometryInstance3D::get_visibility_range_begin() const { 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); + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode); update_configuration_warnings(); } @@ -174,7 +183,7 @@ float GeometryInstance3D::get_visibility_range_end() const { 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); + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode); } float GeometryInstance3D::get_visibility_range_begin_margin() const { @@ -183,13 +192,22 @@ float GeometryInstance3D::get_visibility_range_begin_margin() const { 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); + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode); } float GeometryInstance3D::get_visibility_range_end_margin() const { return visibility_range_end_margin; } +void GeometryInstance3D::set_visibility_range_fade_mode(VisibilityRangeFadeMode p_mode) { + visibility_range_fade_mode = p_mode; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode); +} + +GeometryInstance3D::VisibilityRangeFadeMode GeometryInstance3D::get_visibility_range_fade_mode() const { + return visibility_range_fade_mode; +} + void GeometryInstance3D::_notification(int p_what) { } @@ -375,6 +393,9 @@ void GeometryInstance3D::_bind_methods() { 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_transparency", "transparency"), &GeometryInstance3D::set_transparecy); + ClassDB::bind_method(D_METHOD("get_transparency"), &GeometryInstance3D::get_transparency); + 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); @@ -387,6 +408,9 @@ void GeometryInstance3D::_bind_methods() { 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_visibility_range_fade_mode", "mode"), &GeometryInstance3D::set_visibility_range_fade_mode); + ClassDB::bind_method(D_METHOD("get_visibility_range_fade_mode"), &GeometryInstance3D::get_visibility_range_fade_mode); + 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); @@ -408,6 +432,7 @@ void GeometryInstance3D::_bind_methods() { ADD_GROUP("Geometry", ""); 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::FLOAT, "transparency", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_transparency", "get_transparency"); 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"); @@ -421,7 +446,7 @@ void GeometryInstance3D::_bind_methods() { 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_PROPERTY(PropertyInfo(Variant::INT, "visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), "set_visibility_range_fade_mode", "get_visibility_range_fade_mode"); //ADD_SIGNAL( MethodInfo("visibility_changed")); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); @@ -438,6 +463,10 @@ void GeometryInstance3D::_bind_methods() { BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_4X); BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_8X); BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_MAX); + + BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_DISABLED); + BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_SELF); + BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_DEPENDENCIES); } GeometryInstance3D::GeometryInstance3D() { diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 8d24e13d47..acdbc4666a 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -101,6 +101,12 @@ public: LIGHTMAP_SCALE_MAX, }; + enum VisibilityRangeFadeMode { + VISIBILITY_RANGE_FADE_DISABLED = RS::VISIBILITY_RANGE_FADE_DISABLED, + VISIBILITY_RANGE_FADE_SELF = RS::VISIBILITY_RANGE_FADE_SELF, + VISIBILITY_RANGE_FADE_DEPENDENCIES = RS::VISIBILITY_RANGE_FADE_DEPENDENCIES, + }; + private: ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON; Ref<Material> material_override; @@ -109,8 +115,9 @@ private: float visibility_range_end = 0.0; float visibility_range_begin_margin = 0.0; float visibility_range_end_margin = 0.0; + VisibilityRangeFadeMode visibility_range_fade_mode = VISIBILITY_RANGE_FADE_DISABLED; - Vector<NodePath> visibility_range_children; + float transparency = 0.0f; float lod_bias = 1.0; @@ -136,6 +143,9 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; + void set_transparecy(float p_transparency); + float get_transparency() const; + void set_visibility_range_begin(float p_dist); float get_visibility_range_begin() const; @@ -148,8 +158,8 @@ public: 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_visibility_range_fade_mode(VisibilityRangeFadeMode p_mode); + VisibilityRangeFadeMode get_visibility_range_fade_mode() const; void set_material_override(const Ref<Material> &p_material); Ref<Material> get_material_override() const; @@ -181,5 +191,6 @@ public: VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting); VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance3D::GIMode); +VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode); #endif diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 377abd5b38..c7108cbae0 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -225,7 +225,7 @@ void VoxelGIData::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_range", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_dynamic_range", "get_dynamic_range"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_energy", "get_energy"); @@ -403,7 +403,7 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { if (p_create_visual_debug) { MultiMeshInstance3D *mmi = memnew(MultiMeshInstance3D); mmi->set_multimesh(baker.create_debug_multimesh()); - add_child(mmi); + add_child(mmi, true); #ifdef TOOLS_ENABLED if (is_inside_tree() && get_tree()->get_edited_scene_root() == this) { mmi->set_owner(this); @@ -458,7 +458,7 @@ TypedArray<String> VoxelGI::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(TTR("VoxelGIs are not supported by the GLES2 video driver.\nUse a LightmapGI instead.")); + warnings.push_back(TTR("VoxelGIs are not supported by the OpenGL 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.")); } diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 9dbee58f0e..a16820cbdc 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -261,6 +261,8 @@ void XRNode3D::_validate_property(PropertyInfo &property) const { } property.hint_string = hint_string; } + + Node3D::_validate_property(property); } void XRNode3D::set_tracker(const StringName p_tracker_name) { diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 0167992baf..9a71e7bf55 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -81,14 +81,14 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label"); } void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 145e7c605b..b91a2869e4 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -639,21 +639,21 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_triangles"), &AnimationNodeBlendSpace2D::_update_triangles); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles"); for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); - ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } - ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NOEDITOR), "set_blend_mode", "get_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_x_label", "get_x_label"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_y_label", "get_y_label"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode"); ADD_SIGNAL(MethodInfo("triangles_updated")); BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 10a66386eb..d18e1a92d8 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1112,12 +1112,12 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons for (const StringName &E : names) { String name = E; if (name != "output") { - p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR)); } - p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } - p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } void AnimationNodeBlendTree::reset_state() { @@ -1152,7 +1152,7 @@ void AnimationNodeBlendTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset"); BIND_CONSTANT(CONNECTION_OK); BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 81ecb50ea0..b5d7a0555e 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -909,14 +909,14 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c names.sort_custom<StringName::AlphCompare>(); for (const StringName &name : names) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } - p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } void AnimationNodeStateMachine::reset_state() { diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 1f000d5f39..f91422ac1a 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -166,15 +166,17 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const { property.hint_string = hint; } + + Node::_validate_property(property); } void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> anim_names; for (const KeyValue<StringName, AnimationData> &E : animation_set) { - anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); if (E.value.next != StringName()) { - anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } } @@ -184,7 +186,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(E); } - p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } void AnimationPlayer::advance(float p_time) { @@ -1811,7 +1813,7 @@ void AnimationPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position"); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index ccb5fa9472..4b71fe7a01 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -425,8 +425,8 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter); ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_filter_enabled", "is_filter_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); GDVIRTUAL_BIND(_get_child_nodes); GDVIRTUAL_BIND(_get_parameter_list); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index c43b83747b..da933ae02d 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -291,7 +291,7 @@ bool Tween::step(float p_delta) { float temp_delta = rem_delta; // Turns to true if any Tweener returns true (i.e. is still not finished). step_active = tweener->step(temp_delta) || step_active; - step_delta = MIN(temp_delta, rem_delta); + step_delta = MIN(temp_delta, step_delta); } rem_delta = step_delta; @@ -626,7 +626,7 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel); ClassDB::bind_method(D_METHOD("chain"), &Tween::chain); - ClassDB::bind_method(D_METHOD("interpolate_value", "trans_type", "ease_type", "elapsed_time", "initial_value", "delta_value", "duration"), &Tween::interpolate_variant); + ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant); ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx"))); ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count"))); diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index d2fd65d3d9..43c4ce4c82 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -45,7 +45,6 @@ void AudioStreamPlayer::_notification(int p_what) { Vector<Ref<AudioStreamPlayback>> playbacks_to_remove; for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) { - emit_signal(SNAME("finished")); playbacks_to_remove.push_back(playback); } } @@ -58,6 +57,9 @@ void AudioStreamPlayer::_notification(int p_what) { active.clear(); set_process_internal(false); } + if (!playbacks_to_remove.is_empty()) { + emit_signal(SNAME("finished")); + } } if (p_what == NOTIFICATION_EXIT_TREE) { @@ -292,6 +294,8 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { property.hint_string = options; } + + Node::_validate_property(property); } void AudioStreamPlayer::_bus_layout_changed() { diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index cf2df4e1a2..cb9f13e970 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -354,7 +354,7 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control add_child(l, false, INTERNAL_MODE_FRONT); MarginContainer *mc = memnew(MarginContainer); mc->add_theme_constant_override("margin_left", 0); - mc->add_child(p_control); + mc->add_child(p_control, true); add_child(mc, false, INTERNAL_MODE_FRONT); if (p_expand) { mc->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index e253a27e66..9818c8f0cc 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -111,9 +111,18 @@ void Button::_notification(int p_what) { if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } - color = get_theme_color(SNAME("font_color")); - if (has_theme_color(SNAME("icon_normal_color"))) { - color_icon = get_theme_color(SNAME("icon_normal_color")); + + // Focus colors only take precedence over normal state. + if (has_focus()) { + color = get_theme_color(SNAME("font_focus_color")); + if (has_theme_color(SNAME("icon_focus_color"))) { + color_icon = get_theme_color(SNAME("icon_focus_color")); + } + } else { + color = get_theme_color(SNAME("font_color")); + if (has_theme_color(SNAME("icon_normal_color"))) { + color_icon = get_theme_color(SNAME("icon_normal_color")); + } } } break; case DRAW_HOVER_PRESSED: { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 77a1efd021..582c8e5860 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -73,8 +73,8 @@ Dictionary Control::_edit_get_state() const { void Control::_edit_set_state(const Dictionary &p_state) { ERR_FAIL_COND((p_state.size() <= 0) || - !p_state.has("rotation") || !p_state.has("scale") || - !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets")); + !p_state.has("rotation") || !p_state.has("scale") || + !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets")); Dictionary state = p_state; set_rotation(state["rotation"]); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 973b72973d..5f9f09fc50 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -51,26 +51,32 @@ VBoxContainer *FileDialog::get_vbox() { void FileDialog::_theme_changed() { Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button")); Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button")); + Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button")); Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); dir_up->add_theme_color_override("icon_normal_color", font_color); dir_up->add_theme_color_override("icon_hover_color", font_hover_color); + dir_up->add_theme_color_override("icon_focus_color", font_focus_color); dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); dir_prev->add_theme_color_override("icon_color_normal", font_color); dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); + dir_prev->add_theme_color_override("icon_focus_color", font_focus_color); dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); dir_next->add_theme_color_override("icon_color_normal", font_color); dir_next->add_theme_color_override("icon_color_hover", font_hover_color); + dir_next->add_theme_color_override("icon_focus_color", font_focus_color); dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); refresh->add_theme_color_override("icon_normal_color", font_color); refresh->add_theme_color_override("icon_hover_color", font_hover_color); + refresh->add_theme_color_override("icon_focus_color", font_focus_color); refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); show_hidden->add_theme_color_override("icon_normal_color", font_color); show_hidden->add_theme_color_override("icon_hover_color", font_hover_color); + show_hidden->add_theme_color_override("icon_focus_color", font_focus_color); show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color); } @@ -342,7 +348,7 @@ bool FileDialog::_is_open_should_be_disabled() { // Opening a file, but selected a folder? Forbidden. return ((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || // Flipped case, also forbidden. - (mode == FILE_MODE_OPEN_DIR && !d["dir"]); + (mode == FILE_MODE_OPEN_DIR && !d["dir"]); } void FileDialog::_go_up() { diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 5d024d3be7..e5b23d8006 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -39,6 +39,10 @@ GradientEdit::GradientEdit() { picker = memnew(ColorPicker); popup->add_child(picker); + gradient_cache.instantiate(); + preview_texture.instantiate(); + + preview_texture->set_width(1024); add_child(popup, false, INTERNAL_MODE_FRONT); } @@ -47,7 +51,7 @@ int GradientEdit::_get_point_from_pos(int x) { int total_w = get_size().width - get_size().height - draw_spacing; float min_distance = 1e20; for (int i = 0; i < points.size(); i++) { - //Check if we clicked at point + // Check if we clicked at point. float distance = ABS(x - points[i].offset * total_w); float min = (draw_point_width / 2 * 1.7); //make it easier to grab if (distance <= min && distance < min_distance) { @@ -94,14 +98,14 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> mb = p_event; - //Show color picker on double click. + // Show color picker on double click. if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_double_click() && mb->is_pressed()) { grabbed = _get_point_from_pos(mb->get_position().x); _show_color_picker(); accept_event(); } - //Delete point on right click + // Delete point on right click. if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) { grabbed = _get_point_from_pos(mb->get_position().x); if (grabbed != -1) { @@ -114,20 +118,20 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } } - //Hold alt key to duplicate selected color + // Hold alt key to duplicate selected color. if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) { int x = mb->get_position().x; grabbed = _get_point_from_pos(x); if (grabbed != -1) { int total_w = get_size().width - get_size().height - draw_spacing; - Gradient::Point newPoint = points[grabbed]; - newPoint.offset = CLAMP(x / float(total_w), 0, 1); + Gradient::Point new_point = points[grabbed]; + new_point.offset = CLAMP(x / float(total_w), 0, 1); - points.push_back(newPoint); + points.push_back(new_point); points.sort(); for (int i = 0; i < points.size(); ++i) { - if (points[i].offset == newPoint.offset) { + if (points[i].offset == new_point.offset) { grabbed = i; break; } @@ -138,7 +142,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } } - //select + // Select. if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) { update(); int x = mb->get_position().x; @@ -158,16 +162,16 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - //insert - Gradient::Point newPoint; - newPoint.offset = CLAMP(x / float(total_w), 0, 1); + // Insert point. + Gradient::Point new_point; + new_point.offset = CLAMP(x / float(total_w), 0, 1); Gradient::Point prev; Gradient::Point next; int pos = -1; for (int i = 0; i < points.size(); i++) { - if (points[i].offset < newPoint.offset) { + if (points[i].offset < new_point.offset) { pos = i; } } @@ -191,12 +195,12 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { prev = points[pos]; } - newPoint.color = prev.color.lerp(next.color, (newPoint.offset - prev.offset) / (next.offset - prev.offset)); + new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset)); - points.push_back(newPoint); + points.push_back(new_point); points.sort(); for (int i = 0; i < points.size(); i++) { - if (points[i].offset == newPoint.offset) { + if (points[i].offset == new_point.offset) { grabbed = i; break; } @@ -223,7 +227,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { float newofs = CLAMP(x / float(total_w), 0, 1); // Snap to "round" coordinates if holding Ctrl. - // Be more precise if holding Shift as well + // Be more precise if holding Shift as well. if (mm->is_ctrl_pressed()) { newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1); } else if (mm->is_shift_pressed()) { @@ -299,57 +303,22 @@ void GradientEdit::_notification(int p_what) { int h = get_size().y; if (w == 0 || h == 0) { - return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size + return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size. } int total_w = get_size().width - get_size().height - draw_spacing; - //Draw checker pattern for ramp + // Draw checker pattern for ramp. draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true); - //Draw color ramp - Gradient::Point prev; - prev.offset = 0; - if (points.size() == 0) { - prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points - } else { - prev.color = points[0].color; //Extend color of first point to the beginning. - } + // Draw color ramp. - for (int i = -1; i < points.size(); i++) { - Gradient::Point next; - //If there is no next point - if (i + 1 == points.size()) { - if (points.size() == 0) { - next.color = Color(0, 0, 0); //Draw black rectangle if we have no points - } else { - next.color = points[i].color; //Extend color of last point to the end. - } - next.offset = 1; - } else { - next = points[i + 1]; - } + gradient_cache->set_points(points); + gradient_cache->set_interpolation_mode(interpolation_mode); + preview_texture->set_gradient(gradient_cache); + draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h)); - if (prev.offset == next.offset) { - prev = next; - continue; - } - - Vector<Vector2> points; - Vector<Color> colors; - points.push_back(Vector2(prev.offset * total_w, h)); - points.push_back(Vector2(prev.offset * total_w, 0)); - points.push_back(Vector2(next.offset * total_w, 0)); - points.push_back(Vector2(next.offset * total_w, h)); - colors.push_back(prev.color); - colors.push_back(prev.color); - colors.push_back(next.color); - colors.push_back(next.color); - draw_primitive(points, colors, Vector<Point2>()); - prev = next; - } - - //Draw point markers + // Draw point markers. for (int i = 0; i < points.size(); i++) { Color col = points[i].color.inverted(); col.a = 0.9; @@ -383,7 +352,7 @@ void GradientEdit::_notification(int p_what) { draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6)); } - //Draw borders around color ramp if in focus + // Draw borders around color ramp if in focus. if (has_focus()) { draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6)); draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6)); @@ -448,12 +417,21 @@ void GradientEdit::set_points(Vector<Gradient::Point> &p_points) { } points.clear(); points = p_points; + points.sort(); } Vector<Gradient::Point> &GradientEdit::get_points() { return points; } +void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { + interpolation_mode = p_interp_mode; +} + +Gradient::InterpolationMode GradientEdit::get_interpolation_mode() { + return interpolation_mode; +} + void GradientEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("ramp_changed")); } diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index f3a39daaf6..66b60d87c7 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -45,6 +45,10 @@ class GradientEdit : public Control { bool grabbing = false; int grabbed = -1; Vector<Gradient::Point> points; + Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR; + + Ref<Gradient> gradient_cache; + Ref<GradientTexture1D> preview_texture; // Make sure to use the scaled value below. const int BASE_SPACING = 3; @@ -69,6 +73,9 @@ public: Vector<Color> get_colors() const; void set_points(Vector<Gradient::Point> &p_points); Vector<Gradient::Point> &get_points(); + void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode); + Gradient::InterpolationMode get_interpolation_mode(); + virtual Size2 get_minimum_size() const override; GradientEdit(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index d10ad90c1f..8470003b3e 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "item_list.h" + #include "core/config/project_settings.h" #include "core/os/os.h" #include "core/string/translation.h" @@ -55,16 +56,8 @@ void ItemList::_shape(int p_idx) { int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bool p_selectable) { Item item; item.icon = p_texture; - item.icon_transposed = false; - item.icon_region = Rect2i(); - item.icon_modulate = Color(1, 1, 1, 1); item.text = p_item; - item.text_buf.instantiate(); item.selectable = p_selectable; - item.selected = false; - item.disabled = false; - item.tooltip_enabled = true; - item.custom_bg = Color(0, 0, 0, 0); items.push_back(item); int item_id = items.size() - 1; @@ -72,27 +65,20 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo update(); shape_changed = true; + notify_property_list_changed(); return item_id; } int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) { Item item; item.icon = p_item; - item.icon_transposed = false; - item.icon_region = Rect2i(); - item.icon_modulate = Color(1, 1, 1, 1); - //item.text=p_item; - item.text_buf.instantiate(); item.selectable = p_selectable; - item.selected = false; - item.disabled = false; - item.tooltip_enabled = true; - item.custom_bg = Color(0, 0, 0, 0); items.push_back(item); int item_id = items.size() - 1; update(); shape_changed = true; + notify_property_list_changed(); return item_id; } @@ -400,6 +386,15 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) { update(); shape_changed = true; + notify_property_list_changed(); +} + +void ItemList::set_item_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + items.resize(p_count); + update(); + shape_changed = true; + notify_property_list_changed(); } int ItemList::get_item_count() const { @@ -416,6 +411,7 @@ void ItemList::remove_item(int p_idx) { update(); shape_changed = true; defer_select_single = -1; + notify_property_list_changed(); } void ItemList::clear() { @@ -425,6 +421,7 @@ void ItemList::clear() { update(); shape_changed = true; defer_select_single = -1; + notify_property_list_changed(); } void ItemList::set_fixed_column_width(int p_size) { @@ -1446,32 +1443,6 @@ bool ItemList::is_anything_selected() { return false; } -void ItemList::_set_items(const Array &p_items) { - ERR_FAIL_COND(p_items.size() % 3); - clear(); - - for (int i = 0; i < p_items.size(); i += 3) { - String text = p_items[i + 0]; - Ref<Texture2D> icon = p_items[i + 1]; - bool disabled = p_items[i + 2]; - - int idx = get_item_count(); - add_item(text, icon); - set_item_disabled(idx, disabled); - } -} - -Array ItemList::_get_items() const { - Array items; - for (int i = 0; i < get_item_count(); i++) { - items.push_back(get_item_text(i)); - items.push_back(get_item_icon(i)); - items.push_back(is_item_disabled(i)); - } - - return items; -} - Size2 ItemList::get_minimum_size() const { if (auto_height) { return Size2(0, auto_height_value); @@ -1508,6 +1479,74 @@ TextParagraph::OverrunBehavior ItemList::get_text_overrun_behavior() const { return text_overrun_behavior; } +bool ItemList::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { + int item_index = components[0].trim_prefix("item_").to_int(); + if (components[1] == "text") { + set_item_text(item_index, p_value); + return true; + } else if (components[1] == "icon") { + set_item_icon(item_index, p_value); + return true; + } else if (components[1] == "disabled") { + set_item_disabled(item_index, p_value); + return true; + } + } +#ifndef DISABLE_DEPRECATED + // Compatibility. + if (p_name == "items") { + Array arr = p_value; + ERR_FAIL_COND_V(arr.size() % 3, false); + clear(); + + for (int i = 0; i < arr.size(); i += 3) { + String text = arr[i + 0]; + Ref<Texture2D> icon = arr[i + 1]; + bool disabled = arr[i + 2]; + + int idx = get_item_count(); + add_item(text, icon); + set_item_disabled(idx, disabled); + } + } +#endif + return false; +} + +bool ItemList::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { + int item_index = components[0].trim_prefix("item_").to_int(); + if (components[1] == "text") { + r_ret = get_item_text(item_index); + return true; + } else if (components[1] == "icon") { + r_ret = get_item_icon(item_index); + return true; + } else if (components[1] == "disabled") { + r_ret = is_item_disabled(item_index); + return true; + } + } + return false; +} + +void ItemList::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < items.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i))); + + PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); + pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i)); + pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + } +} + void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("add_item", "text", "icon", "selectable"), &ItemList::add_item, DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("add_icon_item", "icon", "selectable"), &ItemList::add_icon_item, DEFVAL(true)); @@ -1567,6 +1606,7 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item); + ClassDB::bind_method(D_METHOD("set_item_count", "count"), &ItemList::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item); @@ -1614,20 +1654,16 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("get_v_scroll"), &ItemList::get_v_scroll); - ClassDB::bind_method(D_METHOD("_set_items"), &ItemList::_set_items); - ClassDB::bind_method(D_METHOD("_get_items"), &ItemList::_get_items); - ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); + ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_"); ADD_GROUP("Columns", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width"); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 148fa7ba9f..e780179e7b 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -54,7 +54,7 @@ private: Ref<Texture2D> icon; bool icon_transposed = false; Rect2i icon_region; - Color icon_modulate; + Color icon_modulate = Color(1, 1, 1, 1); Ref<Texture2D> tag_icon; String text; Ref<TextParagraph> text_buf; @@ -65,11 +65,11 @@ private: bool selectable = false; bool selected = false; bool disabled = false; - bool tooltip_enabled = false; + bool tooltip_enabled = true; Variant metadata; String tooltip; Color custom_fg; - Color custom_bg; + Color custom_bg = Color(0.0, 0.0, 0.0, 0.0); Rect2 rect_cache; Rect2 min_rect_cache; @@ -77,6 +77,10 @@ private: Size2 get_icon_size() const; bool operator<(const Item &p_another) const { return text < p_another.text; } + + Item() { + text_buf.instantiate(); + } }; int current = -1; @@ -119,14 +123,14 @@ private: bool do_autoscroll_to_bottom = false; - Array _get_items() const; - void _set_items(const Array &p_items); - void _scroll_changed(double); void _shape(int p_idx); protected: void _notification(int p_what); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); public: @@ -199,6 +203,7 @@ public: void move_item(int p_from_idx, int p_to_idx); + void set_item_count(int p_count); int get_item_count() const; void remove_item(int p_idx); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index dda1151273..0b85b4d3d2 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -920,6 +920,9 @@ void LineEdit::_notification(int p_what) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } + if (deselect_on_focus_loss_enabled) { + deselect(); + } } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (has_focus()) { @@ -1938,6 +1941,17 @@ bool LineEdit::is_selecting_enabled() const { return selecting_enabled; } +void LineEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + deselect_on_focus_loss_enabled = p_enabled; + if (p_enabled && selection.enabled && !has_focus()) { + deselect(); + } +} + +bool LineEdit::is_deselect_on_focus_loss_enabled() const { + return deselect_on_focus_loss_enabled; +} + void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) { if (right_icon == p_icon) { return; @@ -2121,7 +2135,7 @@ void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const { void LineEdit::_validate_property(PropertyInfo &property) const { if (!caret_blink_enabled && property.name == "caret_blink_speed") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -2196,6 +2210,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &LineEdit::is_middle_mouse_paste_enabled); ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled); ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled); + ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &LineEdit::set_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled); ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon); ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); @@ -2251,6 +2267,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 94179ce05b..3364e02e01 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -97,6 +97,7 @@ private: float full_width = 0.0; bool selecting_enabled = true; + bool deselect_on_focus_loss_enabled = true; bool context_menu_enabled = true; PopupMenu *menu = nullptr; @@ -325,6 +326,9 @@ public: void set_selecting_enabled(bool p_enabled); bool is_selecting_enabled() const; + void set_deselect_on_focus_loss_enabled(const bool p_enabled); + bool is_deselect_on_focus_loss_enabled() const; + void set_right_icon(const Ref<Texture2D> &p_icon); Ref<Texture2D> get_right_icon(); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 925e6f5b97..c3201186ea 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -168,7 +168,12 @@ void LinkButton::_notification(int p_what) { switch (get_draw_mode()) { case DRAW_NORMAL: { - color = get_theme_color(SNAME("font_color")); + if (has_focus()) { + color = get_theme_color(SNAME("font_focus_color")); + } else { + color = get_theme_color(SNAME("font_color")); + } + do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; } break; case DRAW_HOVER_PRESSED: diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index ceb2092e3a..39c7b04955 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -110,14 +110,6 @@ PopupMenu *MenuButton::get_popup() const { return popup; } -void MenuButton::_set_items(const Array &p_items) { - popup->set("items", p_items); -} - -Array MenuButton::_get_items() const { - return popup->get("items"); -} - void MenuButton::set_switch_on_hover(bool p_enabled) { switch_on_hover = p_enabled; } @@ -126,6 +118,16 @@ bool MenuButton::is_switch_on_hover() { return switch_on_hover; } +void MenuButton::set_item_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + popup->set_item_count(p_count); + notify_property_list_changed(); +} + +int MenuButton::get_item_count() const { + return popup->get_item_count(); +} + void MenuButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -146,16 +148,66 @@ void MenuButton::_notification(int p_what) { } } +bool MenuButton::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0] == "popup") { + bool valid; + popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid); + return valid; + } + return false; +} + +bool MenuButton::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0] == "popup") { + bool valid; + r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid); + return valid; + } + return false; +} + +void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < popup->get_item_count(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i))); + + PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); + pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"); + pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i)); + pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater"); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i)); + pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i)); + pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + } +} + void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); - ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items); - ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items); ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover); ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover); ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); + ClassDB::bind_method(D_METHOD("set_item_count", "count"), &MenuButton::set_item_count); + ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover"); + ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_"); ADD_SIGNAL(MethodInfo("about_to_popup")); } diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 730495b65d..455b7dc870 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -44,15 +44,15 @@ class MenuButton : public Button { Vector2i mouse_pos_adjusted; - Array _get_items() const; - void _set_items(const Array &p_items); - virtual void gui_input(const Ref<InputEvent> &p_event) override; void _popup_visibility_changed(bool p_visible); protected: void _notification(int p_what); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; @@ -64,6 +64,9 @@ public: bool is_switch_on_hover(); void set_disable_shortcuts(bool p_disabled); + void set_item_count(int p_count); + int get_item_count() const; + MenuButton(); ~MenuButton(); }; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 2adeb2d947..dcf3cfeb09 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -71,7 +71,11 @@ void OptionButton::_notification(int p_what) { clr = get_theme_color(SNAME("font_disabled_color")); break; default: - clr = get_theme_color(SNAME("font_color")); + if (has_focus()) { + clr = get_theme_color(SNAME("font_focus_color")); + } else { + clr = get_theme_color(SNAME("font_color")); + } } } @@ -328,7 +332,7 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); // "selected" property must come after "items", otherwise GH-10213 occurs. ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index"))); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index be05fd5a60..f54ff9228b 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -105,8 +105,6 @@ void Popup::_close_pressed() { _deinitialize_visible_parents(); call_deferred(SNAME("hide")); - - emit_signal(SNAME("cancelled")); } void Popup::set_as_minsize() { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index c0a559e624..54af9f3885 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -820,6 +820,7 @@ void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) { _shape_item(items.size() - 1); control->update(); child_controls_changed(); + notify_property_list_changed(); } void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { @@ -830,6 +831,7 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe _shape_item(items.size() - 1); control->update(); child_controls_changed(); + notify_property_list_changed(); } void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) { @@ -1271,6 +1273,14 @@ int PopupMenu::get_current_index() const { return mouse_over; } +void PopupMenu::set_item_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + items.resize(p_count); + control->update(); + child_controls_changed(); + notify_property_list_changed(); +} + int PopupMenu::get_item_count() const { return items.size(); } @@ -1420,27 +1430,7 @@ void PopupMenu::clear() { mouse_over = -1; control->update(); child_controls_changed(); -} - -Array PopupMenu::_get_items() const { - Array items; - for (int i = 0; i < get_item_count(); i++) { - items.push_back(get_item_text(i)); - items.push_back(get_item_icon(i)); - // For compatibility, use false/true for no/checkbox and integers for other values - int ct = this->items[i].checkable_type; - items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct)); - items.push_back(is_item_checked(i)); - items.push_back(is_item_disabled(i)); - - items.push_back(get_item_id(i)); - items.push_back(get_item_accelerator(i)); - items.push_back(get_item_metadata(i)); - items.push_back(get_item_submenu(i)); - items.push_back(is_item_separator(i)); - } - - return items; + notify_property_list_changed(); } void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) { @@ -1461,45 +1451,6 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) { } } -void PopupMenu::_set_items(const Array &p_items) { - ERR_FAIL_COND(p_items.size() % 10); - clear(); - - for (int i = 0; i < p_items.size(); i += 10) { - String text = p_items[i + 0]; - Ref<Texture2D> icon = p_items[i + 1]; - // For compatibility, use false/true for no/checkbox and integers for other values - bool checkable = p_items[i + 2]; - bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON; - bool checked = p_items[i + 3]; - bool disabled = p_items[i + 4]; - - int id = p_items[i + 5]; - int accel = p_items[i + 6]; - Variant meta = p_items[i + 7]; - String subm = p_items[i + 8]; - bool sep = p_items[i + 9]; - - int idx = get_item_count(); - add_item(text, id); - set_item_icon(idx, icon); - if (checkable) { - if (radio_checkable) { - set_item_as_radio_checkable(idx, true); - } else { - set_item_as_checkable(idx, true); - } - } - set_item_checked(idx, checked); - set_item_disabled(idx, disabled); - set_item_id(idx, id); - set_item_metadata(idx, meta); - set_item_as_separator(idx, sep); - set_item_accelerator(idx, accel); - set_item_submenu(idx, subm); - } -} - // Hide on item selection determines whether or not the popup will close after item selection void PopupMenu::set_hide_on_item_selection(bool p_enabled) { hide_on_item_selection = p_enabled; @@ -1581,6 +1532,145 @@ void PopupMenu::take_mouse_focus() { } } +bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { + int item_index = components[0].trim_prefix("item_").to_int(); + String property = components[1]; + if (property == "text") { + set_item_text(item_index, p_value); + return true; + } else if (property == "icon") { + set_item_icon(item_index, p_value); + return true; + } else if (property == "checkable") { + bool radio_checkable = (int)p_value == Item::CHECKABLE_TYPE_RADIO_BUTTON; + if (radio_checkable) { + set_item_as_radio_checkable(item_index, true); + } else { + bool checkable = p_value; + set_item_as_checkable(item_index, checkable); + } + return true; + } else if (property == "checked") { + set_item_checked(item_index, p_value); + return true; + } else if (property == "id") { + set_item_id(item_index, p_value); + return true; + } else if (components[1] == "disabled") { + set_item_disabled(item_index, p_value); + return true; + } else if (property == "separator") { + set_item_as_separator(item_index, p_value); + return true; + } + } +#ifndef DISABLE_DEPRECATED + // Compatibility. + if (p_name == "items") { + Array arr = p_value; + ERR_FAIL_COND_V(arr.size() % 10, false); + clear(); + + for (int i = 0; i < arr.size(); i += 10) { + String text = arr[i + 0]; + Ref<Texture2D> icon = arr[i + 1]; + // For compatibility, use false/true for no/checkbox and integers for other values + bool checkable = arr[i + 2]; + bool radio_checkable = (int)arr[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON; + bool checked = arr[i + 3]; + bool disabled = arr[i + 4]; + + int id = arr[i + 5]; + int accel = arr[i + 6]; + Variant meta = arr[i + 7]; + String subm = arr[i + 8]; + bool sep = arr[i + 9]; + + int idx = get_item_count(); + add_item(text, id); + set_item_icon(idx, icon); + if (checkable) { + if (radio_checkable) { + set_item_as_radio_checkable(idx, true); + } else { + set_item_as_checkable(idx, true); + } + } + set_item_checked(idx, checked); + set_item_disabled(idx, disabled); + set_item_id(idx, id); + set_item_metadata(idx, meta); + set_item_as_separator(idx, sep); + set_item_accelerator(idx, accel); + set_item_submenu(idx, subm); + } + } +#endif + return false; +} + +bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { + int item_index = components[0].trim_prefix("item_").to_int(); + String property = components[1]; + if (property == "text") { + r_ret = get_item_text(item_index); + return true; + } else if (property == "icon") { + r_ret = get_item_icon(item_index); + return true; + } else if (property == "checkable") { + r_ret = this->items[item_index].checkable_type; + return true; + } else if (property == "checked") { + r_ret = is_item_checked(item_index); + return true; + } else if (property == "id") { + r_ret = get_item_id(item_index); + return true; + } else if (components[1] == "disabled") { + r_ret = is_item_disabled(item_index); + return true; + } else if (property == "separator") { + r_ret = is_item_separator(item_index); + return true; + } + } + return false; +} + +void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < items.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i))); + + PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); + pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"); + pi.usage &= ~(!is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("item_%d/checked", i)); + pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater"); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i)); + pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + + pi = PropertyInfo(Variant::BOOL, vformat("item_%d/separator", i)); + pi.usage &= ~(!is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0); + p_list->push_back(pi); + } +} + void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0)); @@ -1643,6 +1733,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut); ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index); + ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item); @@ -1650,9 +1741,6 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear); - ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items); - ClassDB::bind_method(D_METHOD("_get_items"), &PopupMenu::_get_items); - ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection); ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection); @@ -1668,13 +1756,14 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search); ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search"); + ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_"); + ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index"))); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 428076c6da..80874ca1e0 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -117,9 +117,6 @@ class PopupMenu : public Popup { bool hide_on_multistate_item_selection = false; Vector2 moved; - Array _get_items() const; - void _set_items(const Array &p_items); - Map<Ref<Shortcut>, int> shortcut_refcount; void _ref_shortcut(Ref<Shortcut> p_sc); @@ -141,6 +138,9 @@ class PopupMenu : public Popup { protected: void _notification(int p_what); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); public: @@ -213,6 +213,8 @@ public: int get_item_state(int p_idx) const; int get_current_index() const; + + void set_item_count(int p_count); int get_item_count() const; bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 31767dd263..f1efbbda98 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1491,7 +1491,13 @@ void RichTextLabel::_notification(int p_what) { _update_fx(main, dt); update(); } - } + } break; + case NOTIFICATION_FOCUS_EXIT: { + if (deselect_on_focus_loss_enabled) { + selection.active = false; + update(); + } + } break; } } @@ -3609,6 +3615,14 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { } } +void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + deselect_on_focus_loss_enabled = p_enabled; + if (p_enabled && selection.active && !has_focus()) { + selection.active = false; + update(); + } +} + bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) { List<Item *>::Element *E = p_from; while (E != nullptr) { @@ -3858,6 +3872,10 @@ bool RichTextLabel::is_selection_enabled() const { return selection.enabled; } +bool RichTextLabel::is_deselect_on_focus_loss_enabled() const { + return deselect_on_focus_loss_enabled; +} + int RichTextLabel::get_selection_from() const { if (!selection.active || !selection.enabled) { return -1; @@ -4111,6 +4129,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled); ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled); + ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from); ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to); @@ -4162,6 +4183,8 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 48186ca8b8..f3c4c11cc8 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -278,12 +278,12 @@ private: uint64_t offset_random(int index) { return (_current_rng >> (index % 64)) | - (_current_rng << (64 - (index % 64))); + (_current_rng << (64 - (index % 64))); } uint64_t offset_previous_random(int index) { return (_previous_rng >> (index % 64)) | - (_previous_rng << (64 - (index % 64))); + (_previous_rng << (64 - (index % 64))); } }; @@ -399,6 +399,7 @@ private: }; Selection selection; + bool deselect_on_focus_loss_enabled = true; int visible_characters = -1; float percent_visible = 1.0; @@ -551,6 +552,8 @@ public: int get_selection_to() const; String get_selected_text() const; void selection_copy(); + void set_deselect_on_focus_loss_enabled(const bool p_enabled); + bool is_deselect_on_focus_loss_enabled() const; void parse_bbcode(const String &p_bbcode); void append_text(const String &p_bbcode); diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 78b58c773a..405fbdae75 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -175,7 +175,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (cb_hover != -1) { // pressed - emit_signal(SNAME("tab_closed"), cb_hover); + emit_signal(SNAME("tab_close_pressed"), cb_hover); } cb_pressing = false; @@ -222,7 +222,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } } - if (max_drawn_tab <= 0) { + if (tabs.is_empty()) { // Return early if there are no actual tabs to handle input for. return; } @@ -1172,7 +1172,7 @@ void TabBar::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to"))); ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 8cb3b23020..cb7a6c0978 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1364,6 +1364,10 @@ void TextEdit::_notification(int p_what) { if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } + + if (deselect_on_focus_loss_enabled) { + deselect(); + } } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (has_focus()) { @@ -3669,6 +3673,17 @@ bool TextEdit::is_selecting_enabled() const { return selecting_enabled; } +void TextEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + deselect_on_focus_loss_enabled = p_enabled; + if (p_enabled && selection.active && !has_focus()) { + deselect(); + } +} + +bool TextEdit::is_deselect_on_focus_loss_enabled() const { + return deselect_on_focus_loss_enabled; +} + void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } @@ -4716,6 +4731,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); + ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &TextEdit::set_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &TextEdit::is_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color); ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color); @@ -4875,9 +4893,9 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 8823e44c0d..23f80efce0 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -395,6 +395,7 @@ private: } selection; bool selecting_enabled = true; + bool deselect_on_focus_loss_enabled = true; Color font_selected_color = Color(1, 1, 1); Color selection_color = Color(1, 1, 1); @@ -753,6 +754,9 @@ public: void set_selecting_enabled(const bool p_enabled); bool is_selecting_enabled() const; + void set_deselect_on_focus_loss_enabled(const bool p_enabled); + bool is_deselect_on_focus_loss_enabled() const; + void set_override_selected_font_color(bool p_override_selected_font_color); bool is_overriding_selected_font_color() const; diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 26bff4494b..cd7534f73c 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -256,7 +256,7 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) { void CanvasLayer::_validate_property(PropertyInfo &property) const { if (!follow_viewport && property.name == "follow_viewport_scale") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 189aebb47d..3be73af861 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -908,17 +908,12 @@ void Node::set_name(const String &p_name) { } } -static bool node_hrcr = false; static SafeRefCount node_hrcr_count; void Node::init_node_hrcr() { node_hrcr_count.init(1); } -void Node::set_human_readable_collision_renaming(bool p_enabled) { - node_hrcr = p_enabled; -} - #ifdef TOOLS_ENABLED String Node::validate_child_name(Node *p_child) { StringName name = p_child->data.name; @@ -930,9 +925,8 @@ String Node::validate_child_name(Node *p_child) { void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { /* Make sure the name is unique */ - if (node_hrcr || p_force_human_readable) { + if (p_force_human_readable) { //this approach to autoset node names is human readable but very slow - //it's turned on while running in the editor StringName name = p_child->data.name; _generate_serial_child_name(p_child, name); @@ -1899,6 +1893,56 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const { return node; } +#ifdef TOOLS_ENABLED +void Node::set_property_pinned(const String &p_property, bool p_pinned) { + bool current_pinned = false; + bool has_pinned = has_meta("_edit_pinned_properties_"); + Array pinned; + String psa = get_property_store_alias(p_property); + if (has_pinned) { + pinned = get_meta("_edit_pinned_properties_"); + current_pinned = pinned.has(psa); + } + + if (current_pinned != p_pinned) { + if (p_pinned) { + pinned.append(psa); + if (!has_pinned) { + set_meta("_edit_pinned_properties_", pinned); + } + } else { + pinned.erase(psa); + if (pinned.is_empty()) { + remove_meta("_edit_pinned_properties_"); + } + } + } +} + +bool Node::is_property_pinned(const StringName &p_property) const { + if (!has_meta("_edit_pinned_properties_")) { + return false; + } + Array pinned = get_meta("_edit_pinned_properties_"); + String psa = get_property_store_alias(p_property); + return pinned.has(psa); +} + +StringName Node::get_property_store_alias(const StringName &p_property) const { + return p_property; +} +#endif + +void Node::get_storable_properties(Set<StringName> &r_storable_properties) const { + List<PropertyInfo> pi; + get_property_list(&pi); + for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { + if ((E->get().usage & PROPERTY_USAGE_STORAGE)) { + r_storable_properties.insert(E->get().name); + } + } +} + String Node::to_string() { if (get_script_instance()) { bool valid; @@ -2750,7 +2794,11 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); +#ifdef TOOLS_ENABLED + ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned); +#endif + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); { MethodInfo mi; diff --git a/scene/main/node.h b/scene/main/node.h index e59a7a390a..2dd32a2e1d 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -363,6 +363,13 @@ public: bool is_editable_instance(const Node *p_node) const; Node *get_deepest_editable_node(Node *p_start_node) const; +#ifdef TOOLS_ENABLED + void set_property_pinned(const String &p_property, bool p_pinned); + bool is_property_pinned(const StringName &p_property) const; + virtual StringName get_property_store_alias(const StringName &p_property) const; +#endif + void get_storable_properties(Set<StringName> &r_storable_properties) const; + virtual String to_string() override; /* NOTIFICATIONS */ @@ -437,7 +444,6 @@ public: void queue_delete(); //hacks for speed - static void set_human_readable_collision_renaming(bool p_enabled); static void init_node_hrcr(); void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index f4c90ee668..c44b55284d 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -147,7 +147,7 @@ void ResourcePreloader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_resource", "name"), &ResourcePreloader::get_resource); ClassDB::bind_method(D_METHOD("get_resource_list"), &ResourcePreloader::_get_resource_list); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources"); } ResourcePreloader::ResourcePreloader() { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 3280190250..31e8c20991 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -638,7 +638,13 @@ void Viewport::_process_picking() { Vector2 point = canvas_transform.affine_inverse().xform(pos); - int rc = ss2d->intersect_point_on_canvas(point, canvas_layer_id, res, 64, Set<RID>(), 0xFFFFFFFF, true, true, true); + PhysicsDirectSpaceState2D::PointParameters point_params; + point_params.position = point; + point_params.canvas_instance_id = canvas_layer_id; + point_params.collide_with_areas = true; + point_params.pick_point = true; + + int rc = ss2d->intersect_point(point_params, res, 64); for (int i = 0; i < rc; i++) { if (res[i].collider_id.is_valid() && res[i].collider) { CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider); @@ -715,10 +721,17 @@ void Viewport::_process_picking() { if (camera_3d) { Vector3 from = camera_3d->project_ray_origin(pos); Vector3 dir = camera_3d->project_ray_normal(pos); + real_t far = camera_3d->far; PhysicsDirectSpaceState3D *space = PhysicsServer3D::get_singleton()->space_get_direct_state(find_world_3d()->get_space()); if (space) { - bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true, true, true); + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = from; + ray_params.to = from + dir * far; + ray_params.collide_with_areas = true; + ray_params.pick_ray = true; + + bool col = space->intersect_ray(ray_params, result); ObjectID new_collider; if (col) { CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider); @@ -1246,10 +1259,10 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu Ref<InputEventMouseButton> mb = p_input; bool cant_stop_me_now = (mb.is_valid() && - (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN || - mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP || - mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || - mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT)); + (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT)); Ref<InputEventPanGesture> pn = p_input; cant_stop_me_now = pn.is_valid() || cant_stop_me_now; @@ -3860,7 +3873,7 @@ void SubViewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled"); ADD_GROUP("Render Target", "render_target_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,Always"), "set_update_mode", "get_update_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,When Parent Visible,Always"), "set_update_mode", "get_update_mode"); BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS); BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER); diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp new file mode 100644 index 0000000000..79821b8399 --- /dev/null +++ b/scene/property_utils.cpp @@ -0,0 +1,185 @@ +/*************************************************************************/ +/* property_utils.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 "property_utils.h" + +#include "core/config/engine.h" +#include "core/templates/local_vector.h" +#include "editor/editor_node.h" +#include "scene/resources/packed_scene.h" + +bool PropertyUtils::is_property_value_different(const Variant &p_a, const Variant &p_b) { + if (p_a.get_type() == Variant::FLOAT && p_b.get_type() == Variant::FLOAT) { + //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error + return !Math::is_equal_approx((float)p_a, (float)p_b); + } else { + // For our purposes, treating null object as NIL is the right thing to do + const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a; + const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b; + return a != b; + } +} + +Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) { + // This function obeys the way property values are set when an object is instantiated, + // which is the following (the latter wins): + // 1. Default value from builtin class + // 2. Default value from script exported variable (from the topmost script) + // 3. Value overrides from the instantiation/inheritance stack + + if (r_is_class_default) { + *r_is_class_default = false; + } + + Ref<Script> topmost_script; + + if (const Node *node = Object::cast_to<Node>(p_object)) { + // Check inheritance/instantiation ancestors + const Vector<SceneState::PackState> &states_stack = p_states_stack_cache ? *p_states_stack_cache : PropertyUtils::get_node_states_stack(node, p_owner); + for (int i = 0; i < states_stack.size(); ++i) { + const SceneState::PackState &ia = states_stack[i]; + bool found = false; + Variant value_in_ancestor = ia.state->get_property_value(ia.node, p_property, found); + if (found) { + return value_in_ancestor; + } + // Save script for later + bool has_script = false; + Variant script = ia.state->get_property_value(ia.node, SNAME("script"), has_script); + if (has_script) { + Ref<Script> scr = script; + if (scr.is_valid()) { + topmost_script = scr; + } + } + } + } + + // Let's see what default is set by the topmost script having a default, if any + if (topmost_script.is_null()) { + topmost_script = p_object->get_script(); + } + if (topmost_script.is_valid()) { + // Should be called in the editor only and not at runtime, + // otherwise it can cause problems because of missing instance state support + if (p_update_exports && Engine::get_singleton()->is_editor_hint()) { + topmost_script->update_exports(); + } + Variant default_value; + if (topmost_script->get_property_default_value(p_property, default_value)) { + return default_value; + } + } + + // Fall back to the default from the native class + if (r_is_class_default) { + *r_is_class_default = true; + } + return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property); +} + +// Like SceneState::PackState, but using a raw pointer to avoid the cost of +// updating the reference count during the internal work of the functions below +namespace { +struct _FastPackState { + SceneState *state = nullptr; + int node = -1; +}; +} // namespace + +static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const NodePath &p_path, LocalVector<_FastPackState> &r_states_stack) { + bool found = false; + + LocalVector<_FastPackState> inheritance_states; + + Ref<SceneState> state = p_state; + while (state.is_valid()) { + int node = state->find_node_by_path(p_path); + if (node >= 0) { + // This one has state for this node + inheritance_states.push_back({ state.ptr(), node }); + found = true; + } + state = state->get_base_scene_state(); + } + + for (int i = inheritance_states.size() - 1; i >= 0; --i) { + r_states_stack.push_back(inheritance_states[i]); + } + + return found; +} + +Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p_node, const Node *p_owner, bool *r_instantiated_by_owner) { + if (r_instantiated_by_owner) { + *r_instantiated_by_owner = true; + } + + LocalVector<_FastPackState> states_stack; + { + const Node *owner = p_owner; +#ifdef TOOLS_ENABLED + if (!p_owner && Engine::get_singleton()->is_editor_hint()) { + owner = EditorNode::get_singleton()->get_edited_scene(); + } +#endif + + const Node *n = p_node; + while (n) { + if (n == owner) { + const Ref<SceneState> &state = n->get_scene_inherited_state(); + if (_collect_inheritance_chain(state, n->get_path_to(p_node), states_stack)) { + if (r_instantiated_by_owner) { + *r_instantiated_by_owner = false; + } + } + break; + } else if (n->get_scene_file_path() != String()) { + const Ref<SceneState> &state = n->get_scene_instance_state(); + _collect_inheritance_chain(state, n->get_path_to(p_node), states_stack); + } + n = n->get_owner(); + } + } + + // Convert to the proper type for returning, inverting the vector on the go + // (it was more convenient to fill the vector in reverse order) + Vector<SceneState::PackState> states_stack_ret; + { + states_stack_ret.resize(states_stack.size()); + _FastPackState *ps = states_stack.ptr(); + for (int i = states_stack.size() - 1; i >= 0; --i) { + states_stack_ret.write[i].state.reference_ptr(ps->state); + states_stack_ret.write[i].node = ps->node; + ++ps; + } + } + return states_stack_ret; +} diff --git a/scene/property_utils.h b/scene/property_utils.h new file mode 100644 index 0000000000..fde9163548 --- /dev/null +++ b/scene/property_utils.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* property_utils.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 PROPERTY_UTILS_H +#define PROPERTY_UTILS_H + +#include "scene/main/node.h" +#include "scene/resources/packed_scene.h" + +class PropertyUtils { +public: + static bool is_property_value_different(const Variant &p_a, const Variant &p_b); + // Gets the most pure default value, the one that would be set when the node has just been instantiated + static Variant get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache = nullptr, bool p_update_exports = false, const Node *p_owner = nullptr, bool *r_is_class_default = nullptr); + + // Gets the instance/inheritance states of this node, in order of precedence, + // that is, from the topmost (the most able to override values) to the lowermost + // (Note that in nested instancing the one with the greatest precedence is the furthest + // in the tree, since every owner found while traversing towards the root gets a chance + // to override property values.) + static Vector<SceneState::PackState> get_node_states_stack(const Node *p_node, const Node *p_owner = nullptr, bool *r_instantiated_by_owner = nullptr); +}; + +#endif // PROPERTY_UTILS_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 33e0e2cad7..c193850a78 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -213,6 +213,7 @@ #include "scene/3d/collision_shape_3d.h" #include "scene/3d/cpu_particles_3d.h" #include "scene/3d/decal.h" +#include "scene/3d/fog_volume.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/3d/gpu_particles_collision_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" @@ -245,6 +246,7 @@ #include "scene/3d/world_environment.h" #include "scene/3d/xr_nodes.h" #include "scene/resources/environment.h" +#include "scene/resources/fog_material.h" #include "scene/resources/importer_mesh.h" #include "scene/resources/mesh_library.h" #endif @@ -520,6 +522,8 @@ void register_scene_types() { GDREGISTER_CLASS(VisibleOnScreenNotifier3D); GDREGISTER_CLASS(VisibleOnScreenEnabler3D); GDREGISTER_CLASS(WorldEnvironment); + GDREGISTER_CLASS(FogVolume); + GDREGISTER_CLASS(FogMaterial); GDREGISTER_CLASS(RemoteTransform3D); GDREGISTER_VIRTUAL_CLASS(Joint3D); @@ -787,7 +791,8 @@ void register_scene_types() { GDREGISTER_CLASS(MeshTexture); GDREGISTER_CLASS(CurveTexture); GDREGISTER_CLASS(CurveXYZTexture); - GDREGISTER_CLASS(GradientTexture); + GDREGISTER_CLASS(GradientTexture1D); + GDREGISTER_CLASS(GradientTexture2D); GDREGISTER_CLASS(ProxyTexture); GDREGISTER_CLASS(AnimatedTexture); GDREGISTER_CLASS(CameraTexture); @@ -917,6 +922,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("EditorSpatialGizmo", "EditorNode3DGizmo"); ClassDB::add_compatibility_class("EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin"); ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D"); + ClassDB::add_compatibility_class("GradientTexture", "GradientTexture1D"); ClassDB::add_compatibility_class("HeightMapShape", "HeightMapShape3D"); ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D"); ClassDB::add_compatibility_class("Joint", "Joint3D"); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index af2f4cbdb6..71d2774335 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -799,19 +799,19 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { void Animation::_get_property_list(List<PropertyInfo> *p_list) const { if (compression.enabled) { - p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } for (int i = 0; i < tracks.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); if (track_is_compressed(i)) { - p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } else { - p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } } } @@ -2319,10 +2319,11 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a real_t t2 = t * t; real_t t3 = t2 * t; - return 0.5f * ((p1 * 2.0f) + - (-p0 + p2) * t + - (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + - (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + return 0.5f * + ((p1 * 2.0f) + + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } else if ((vformat & (vformat - 1))) { return p_a; //can't interpolate, mix of types @@ -4688,7 +4689,7 @@ bool Animation::_blend_shape_interpolate_compressed(uint32_t p_compressed_track, template <uint32_t COMPONENTS> bool Animation::_fetch_compressed(uint32_t p_compressed_track, double p_time, Vector3i &r_current_value, double &r_current_time, Vector3i &r_next_value, double &r_next_time, uint32_t *key_index) const { ERR_FAIL_COND_V(!compression.enabled, false); - ERR_FAIL_INDEX_V(p_compressed_track, compression.bounds.size(), false); + ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false); p_time = CLAMP(p_time, 0, length); if (key_index) { *key_index = 0; @@ -4836,7 +4837,7 @@ bool Animation::_fetch_compressed(uint32_t p_compressed_track, double p_time, Ve template <uint32_t COMPONENTS> void Animation::_get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const { ERR_FAIL_COND(!compression.enabled); - ERR_FAIL_INDEX(p_compressed_track, compression.bounds.size()); + ERR_FAIL_UNSIGNED_INDEX(p_compressed_track, compression.bounds.size()); double frame_to_sec = 1.0 / double(compression.fps); uint32_t key_index = 0; @@ -4957,7 +4958,7 @@ float Animation::_uncompress_blend_shape(const Vector3i &p_value) const { template <uint32_t COMPONENTS> bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const { ERR_FAIL_COND_V(!compression.enabled, false); - ERR_FAIL_INDEX_V(p_compressed_track, compression.bounds.size(), false); + ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false); for (uint32_t i = 0; i < compression.pages.size(); i++) { const uint8_t *page_data = compression.pages[i].data.ptr(); diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index d018103e64..5cfc4a9d40 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -636,7 +636,7 @@ void AudioStreamSample::_bind_methods() { ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin"); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 49ed9dcb82..16f1507c34 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -674,7 +674,7 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0)); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } BitMap::BitMap() {} diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index b633196424..0df372ea1b 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -149,7 +149,7 @@ void CameraEffects::_validate_property(PropertyInfo &property) const { if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) || (!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) || (!override_exposure_enabled && property.name == "override_exposure")) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index 3fed700383..03854683bd 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -108,7 +108,7 @@ void ConcavePolygonShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_backface_collision_enabled", "enabled"), &ConcavePolygonShape3D::set_backface_collision_enabled); ClassDB::bind_method(D_METHOD("is_backface_collision_enabled"), &ConcavePolygonShape3D::is_backface_collision_enabled); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "backface_collision"), "set_backface_collision_enabled", "is_backface_collision_enabled"); } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 9dc76dcf44..b530a72033 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -522,7 +522,7 @@ void Curve::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED)); @@ -1006,7 +1006,7 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } Curve2D::Curve2D() { @@ -1699,7 +1699,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); ADD_GROUP("Up Vector", "up_vector_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled"); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index ed093d7ea3..f21a070133 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -147,6 +147,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Color control_font_lower_color = Color(0.63, 0.63, 0.63); Color control_font_low_color = Color(0.69, 0.69, 0.69); Color control_font_hover_color = Color(0.94, 0.94, 0.94); + Color control_font_focus_color = Color(0.94, 0.94, 0.94); Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2); Color control_font_pressed_color = Color(1, 1, 1); @@ -185,6 +186,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "Button", control_font_color); theme->set_color("font_pressed_color", "Button", control_font_pressed_color); theme->set_color("font_hover_color", "Button", control_font_hover_color); + theme->set_color("font_focus_color", "Button", control_font_focus_color); theme->set_color("font_hover_pressed_color", "Button", control_font_pressed_color); theme->set_color("font_disabled_color", "Button", control_font_disabled_color); theme->set_color("font_outline_color", "Button", Color(1, 1, 1)); @@ -193,6 +195,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("icon_pressed_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1)); + theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1)); theme->set_constant("hseparation", "Button", 2 * scale); @@ -207,6 +210,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "LinkButton", control_font_color); theme->set_color("font_pressed_color", "LinkButton", control_font_pressed_color); theme->set_color("font_hover_color", "LinkButton", control_font_hover_color); + theme->set_color("font_focus_color", "LinkButton", control_font_focus_color); theme->set_color("font_outline_color", "LinkButton", Color(1, 1, 1)); theme->set_constant("outline_size", "LinkButton", 0); @@ -245,6 +249,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "OptionButton", control_font_color); theme->set_color("font_pressed_color", "OptionButton", control_font_pressed_color); theme->set_color("font_hover_color", "OptionButton", control_font_hover_color); + theme->set_color("font_focus_color", "OptionButton", control_font_focus_color); theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color); theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1)); @@ -266,6 +271,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "MenuButton", control_font_color); theme->set_color("font_pressed_color", "MenuButton", control_font_pressed_color); theme->set_color("font_hover_color", "MenuButton", control_font_hover_color); + theme->set_color("font_focus_color", "MenuButton", control_font_focus_color); theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3)); theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1)); @@ -308,6 +314,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_pressed_color", "CheckBox", control_font_pressed_color); theme->set_color("font_hover_color", "CheckBox", control_font_hover_color); theme->set_color("font_hover_pressed_color", "CheckBox", control_font_pressed_color); + theme->set_color("font_focus_color", "CheckBox", control_font_focus_color); theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color); theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1)); @@ -347,6 +354,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_pressed_color", "CheckButton", control_font_pressed_color); theme->set_color("font_hover_color", "CheckButton", control_font_hover_color); theme->set_color("font_hover_pressed_color", "CheckButton", control_font_pressed_color); + theme->set_color("font_focus_color", "CheckButton", control_font_focus_color); theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color); theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1)); @@ -867,6 +875,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1)); theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1)); theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1)); + theme->set_color("font_focus_color", "ColorPickerButton", Color(1, 1, 1, 1)); theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); diff --git a/scene/resources/default_theme/overbright_indicator.png b/scene/resources/default_theme/overbright_indicator.png Binary files differindex 89f800c230..e13f15dd02 100644 --- a/scene/resources/default_theme/overbright_indicator.png +++ b/scene/resources/default_theme/overbright_indicator.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index 6a556c1112..57ff9a5325 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -50,6 +50,10 @@ static const unsigned char checked_disabled_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char checker_bg_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char close_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -218,10 +222,6 @@ static const unsigned char indeterminate_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; -static const unsigned char indeterminate_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x3c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0x9e, 0x9e, 0x9e, 0x8c, 0x93, 0x80, 0x95, 0x0, 0x0, 0x0, 0x14, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xb9, 0x8d, 0x1e, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xd1, 0xe, 0x80, 0x20, 0x8, 0x85, 0xe1, 0x4, 0xd4, 0x30, 0x51, 0xb6, 0xde, 0xff, 0x5d, 0x23, 0x97, 0xad, 0xa5, 0xad, 0xff, 0xf2, 0xbb, 0x90, 0xe3, 0xb2, 0xc, 0x39, 0x40, 0xf2, 0x2d, 0x42, 0x70, 0x6, 0x10, 0x58, 0x6b, 0x4b, 0x39, 0x80, 0x1, 0x72, 0x91, 0xdc, 0x92, 0xc2, 0x68, 0x40, 0x2a, 0xdb, 0x95, 0x28, 0x19, 0xf8, 0x9a, 0x3b, 0xe4, 0xea, 0xe7, 0xb0, 0xdf, 0x7d, 0xc1, 0xdf, 0x1b, 0xc3, 0xd9, 0xe7, 0xb0, 0x74, 0xe, 0x83, 0x98, 0xfa, 0xf4, 0x35, 0xc2, 0xec, 0x73, 0xaf, 0xe, 0x57, 0x20, 0x8, 0x2c, 0x1a, 0x56, 0xe5, 0x32, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; - static const unsigned char line_edit_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -275,7 +275,7 @@ static const unsigned char option_button_pressed_mirrored_png[] = { }; static const unsigned char overbright_indicator_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0xcd, 0x4b, 0xe, 0x80, 0x20, 0xc, 0x45, 0xd1, 0x9b, 0xea, 0xbe, 0x40, 0x97, 0x87, 0x8b, 0xae, 0x13, 0x34, 0x88, 0x1f, 0xa, 0x9d, 0xf8, 0x92, 0xe, 0xcf, 0x2d, 0x80, 0xda, 0x4f, 0x14, 0x26, 0x5, 0x49, 0x14, 0x53, 0xcb, 0x42, 0x58, 0xe, 0xbc, 0x51, 0xcd, 0x85, 0x9b, 0x81, 0x16, 0xfe, 0xc, 0x58, 0xf0, 0x6b, 0xc0, 0x8a, 0x1f, 0x3, 0x3d, 0xf8, 0x16, 0xe8, 0xc5, 0x97, 0x40, 0x81, 0x53, 0x9b, 0x55, 0x81, 0x91, 0xcf, 0x67, 0x20, 0xc6, 0x75, 0xe8, 0x73, 0x9e, 0xc, 0x7f, 0xce, 0x73, 0x61, 0x80, 0xd9, 0x83, 0x7f, 0xb0, 0x1d, 0xec, 0x30, 0xf0, 0x78, 0x5a, 0x7, 0xa8, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char panel_bg_png[] = { diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index be0d3a140e..4c25ed589b 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -782,7 +782,7 @@ void Environment::_update_fog() { // Volumetric Fog void Environment::_update_volumetric_fog() { - RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount); + RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_albedo, volumetric_fog_emission, volumetric_fog_emission_energy, volumetric_fog_anisotropy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount, volumetric_fog_ambient_inject); } void Environment::set_volumetric_fog_enabled(bool p_enable) { @@ -795,26 +795,39 @@ bool Environment::is_volumetric_fog_enabled() const { return volumetric_fog_enabled; } void Environment::set_volumetric_fog_density(float p_density) { - p_density = CLAMP(p_density, 0.0000001, 1.0); volumetric_fog_density = p_density; _update_volumetric_fog(); } float Environment::get_volumetric_fog_density() const { return volumetric_fog_density; } -void Environment::set_volumetric_fog_light(Color p_color) { - volumetric_fog_light = p_color; +void Environment::set_volumetric_fog_albedo(Color p_color) { + volumetric_fog_albedo = p_color; _update_volumetric_fog(); } -Color Environment::get_volumetric_fog_light() const { - return volumetric_fog_light; +Color Environment::get_volumetric_fog_albedo() const { + return volumetric_fog_albedo; } -void Environment::set_volumetric_fog_light_energy(float p_begin) { - volumetric_fog_light_energy = p_begin; +void Environment::set_volumetric_fog_emission(Color p_color) { + volumetric_fog_emission = p_color; _update_volumetric_fog(); } -float Environment::get_volumetric_fog_light_energy() const { - return volumetric_fog_light_energy; +Color Environment::get_volumetric_fog_emission() const { + return volumetric_fog_emission; +} +void Environment::set_volumetric_fog_emission_energy(float p_begin) { + volumetric_fog_emission_energy = p_begin; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_emission_energy() const { + return volumetric_fog_emission_energy; +} +void Environment::set_volumetric_fog_anisotropy(float p_anisotropy) { + volumetric_fog_anisotropy = p_anisotropy; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_anisotropy() const { + return volumetric_fog_anisotropy; } void Environment::set_volumetric_fog_length(float p_length) { volumetric_fog_length = p_length; @@ -824,6 +837,7 @@ float Environment::get_volumetric_fog_length() const { return volumetric_fog_length; } void Environment::set_volumetric_fog_detail_spread(float p_detail_spread) { + p_detail_spread = CLAMP(p_detail_spread, 0.5, 6.0); volumetric_fog_detail_spread = p_detail_spread; _update_volumetric_fog(); } @@ -838,6 +852,13 @@ void Environment::set_volumetric_fog_gi_inject(float p_gi_inject) { float Environment::get_volumetric_fog_gi_inject() const { return volumetric_fog_gi_inject; } +void Environment::set_volumetric_fog_ambient_inject(float p_ambient_inject) { + volumetric_fog_ambient_inject = p_ambient_inject; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_ambient_inject() const { + return volumetric_fog_ambient_inject; +} void Environment::set_volumetric_fog_temporal_reprojection_enabled(bool p_enable) { volumetric_fog_temporal_reproject = p_enable; @@ -896,7 +917,7 @@ float Environment::get_adjustment_saturation() const { void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correction) { adjustment_color_correction = p_color_correction; - Ref<GradientTexture> grad_tex = p_color_correction; + Ref<GradientTexture1D> grad_tex = p_color_correction; if (grad_tex.is_valid()) { if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) { grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment)); @@ -933,39 +954,39 @@ void Environment::_update_adjustment() { void Environment::_validate_property(PropertyInfo &property) const { if (property.name == "sky" || property.name == "sky_custom_fov" || property.name == "sky_rotation" || property.name == "ambient_light/sky_contribution") { if (bg_mode != BG_SKY && ambient_source != AMBIENT_SOURCE_SKY && reflection_source != REFLECTION_SOURCE_SKY) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "fog_aerial_perspective") { if (bg_mode != BG_SKY) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } if (property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } if (property.name == "background_color") { if (bg_mode != BG_COLOR && ambient_source != AMBIENT_SOURCE_COLOR) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "background_canvas_max_layer") { if (bg_mode != BG_CANVAS) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "background_camera_feed_id") { if (bg_mode != BG_CAMERA_FEED) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -997,7 +1018,7 @@ void Environment::_validate_property(PropertyInfo &property) const { String enabled = prefix + "enabled"; if (property.name.begins_with(prefix) && property.name != enabled && !bool(get(enabled))) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; return; } @@ -1010,7 +1031,7 @@ void Environment::_validate_property(PropertyInfo &property) const { String prefix = String(*prefixes); if (property.name.begins_with(prefix)) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; return; } @@ -1295,18 +1316,24 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); - ClassDB::bind_method(D_METHOD("set_volumetric_fog_light", "color"), &Environment::set_volumetric_fog_light); - ClassDB::bind_method(D_METHOD("get_volumetric_fog_light"), &Environment::get_volumetric_fog_light); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_emission", "color"), &Environment::set_volumetric_fog_emission); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_emission"), &Environment::get_volumetric_fog_emission); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_albedo", "color"), &Environment::set_volumetric_fog_albedo); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_albedo"), &Environment::get_volumetric_fog_albedo); ClassDB::bind_method(D_METHOD("set_volumetric_fog_density", "density"), &Environment::set_volumetric_fog_density); ClassDB::bind_method(D_METHOD("get_volumetric_fog_density"), &Environment::get_volumetric_fog_density); - ClassDB::bind_method(D_METHOD("set_volumetric_fog_light_energy", "begin"), &Environment::set_volumetric_fog_light_energy); - ClassDB::bind_method(D_METHOD("get_volumetric_fog_light_energy"), &Environment::get_volumetric_fog_light_energy); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_emission_energy", "begin"), &Environment::set_volumetric_fog_emission_energy); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_emission_energy"), &Environment::get_volumetric_fog_emission_energy); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_anisotropy", "anisotropy"), &Environment::set_volumetric_fog_anisotropy); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_anisotropy"), &Environment::get_volumetric_fog_anisotropy); ClassDB::bind_method(D_METHOD("set_volumetric_fog_length", "length"), &Environment::set_volumetric_fog_length); ClassDB::bind_method(D_METHOD("get_volumetric_fog_length"), &Environment::get_volumetric_fog_length); ClassDB::bind_method(D_METHOD("set_volumetric_fog_detail_spread", "detail_spread"), &Environment::set_volumetric_fog_detail_spread); ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread); ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject); ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_ambient_inject", "enabled"), &Environment::set_volumetric_fog_ambient_inject); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_ambient_inject"), &Environment::get_volumetric_fog_ambient_inject); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_enabled", "enabled"), &Environment::set_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_temporal_reprojection_enabled"), &Environment::is_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_amount", "temporal_reprojection_amount"), &Environment::set_volumetric_fog_temporal_reprojection_amount); @@ -1315,11 +1342,14 @@ void Environment::_bind_methods() { ADD_GROUP("Volumetric Fog", "volumetric_fog_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.00,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_albedo", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_albedo", "get_volumetric_fog_albedo"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_emission", "get_volumetric_fog_emission"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_emission_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_emission_energy", "get_volumetric_fog_emission_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.0,0.999,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount"); @@ -1381,11 +1411,6 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT); - - BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED); - BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_LOW); - BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM); - BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_HIGH); } Environment::Environment() { diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 46842754f4..024bef34de 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -90,13 +90,6 @@ public: GLOW_BLEND_MODE_MIX, }; - enum VolumetricFogShadowFilter { - VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED, - VOLUMETRIC_FOG_SHADOW_FILTER_LOW, - VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM, - VOLUMETRIC_FOG_SHADOW_FILTER_HIGH, - }; - private: RID environment; @@ -190,12 +183,15 @@ private: // Volumetric Fog bool volumetric_fog_enabled = false; - float volumetric_fog_density = 0.01; - Color volumetric_fog_light = Color(0.0, 0.0, 0.0); - float volumetric_fog_light_energy = 1.0; + float volumetric_fog_density = 0.05; + Color volumetric_fog_albedo = Color(1.0, 1.0, 1.0); + Color volumetric_fog_emission = Color(0.0, 0.0, 0.0); + float volumetric_fog_emission_energy = 1.0; + float volumetric_fog_anisotropy = 0.2; float volumetric_fog_length = 64.0; float volumetric_fog_detail_spread = 2.0; float volumetric_fog_gi_inject = 0.0; + float volumetric_fog_ambient_inject = false; bool volumetric_fog_temporal_reproject = true; float volumetric_fog_temporal_reproject_amount = 0.9; void _update_volumetric_fog(); @@ -375,16 +371,22 @@ public: bool is_volumetric_fog_enabled() const; void set_volumetric_fog_density(float p_density); float get_volumetric_fog_density() const; - void set_volumetric_fog_light(Color p_color); - Color get_volumetric_fog_light() const; - void set_volumetric_fog_light_energy(float p_begin); - float get_volumetric_fog_light_energy() const; + void set_volumetric_fog_albedo(Color p_color); + Color get_volumetric_fog_albedo() const; + void set_volumetric_fog_emission(Color p_color); + Color get_volumetric_fog_emission() const; + void set_volumetric_fog_emission_energy(float p_begin); + float get_volumetric_fog_emission_energy() const; + void set_volumetric_fog_anisotropy(float p_anisotropy); + float get_volumetric_fog_anisotropy() const; void set_volumetric_fog_length(float p_length); float get_volumetric_fog_length() const; void set_volumetric_fog_detail_spread(float p_detail_spread); float get_volumetric_fog_detail_spread() const; void set_volumetric_fog_gi_inject(float p_gi_inject); float get_volumetric_fog_gi_inject() const; + void set_volumetric_fog_ambient_inject(float p_ambient_inject); + float get_volumetric_fog_ambient_inject() const; void set_volumetric_fog_temporal_reprojection_enabled(bool p_enable); bool is_volumetric_fog_temporal_reprojection_enabled() const; void set_volumetric_fog_temporal_reprojection_amount(float p_amount); @@ -413,6 +415,5 @@ VARIANT_ENUM_CAST(Environment::ToneMapper) VARIANT_ENUM_CAST(Environment::SDFGICascades) VARIANT_ENUM_CAST(Environment::SDFGIYScale) VARIANT_ENUM_CAST(Environment::GlowBlendMode) -VARIANT_ENUM_CAST(Environment::VolumetricFogShadowFilter) #endif // ENVIRONMENT_H diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp new file mode 100644 index 0000000000..978aaca33d --- /dev/null +++ b/scene/resources/fog_material.cpp @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* fog_material.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 "fog_material.h" + +#include "core/version.h" + +Mutex FogMaterial::shader_mutex; +RID FogMaterial::shader; + +void FogMaterial::set_density(float p_density) { + density = p_density; + RS::get_singleton()->material_set_param(_get_material(), "density", density); +} + +float FogMaterial::get_density() const { + return density; +} + +void FogMaterial::set_albedo(Color p_albedo) { + albedo = p_albedo; + RS::get_singleton()->material_set_param(_get_material(), "albedo", albedo); +} + +Color FogMaterial::get_albedo() const { + return albedo; +} + +void FogMaterial::set_emission(Color p_emission) { + emission = p_emission; + RS::get_singleton()->material_set_param(_get_material(), "emission", emission); +} + +Color FogMaterial::get_emission() const { + return emission; +} + +void FogMaterial::set_height_falloff(float p_falloff) { + height_falloff = MAX(p_falloff, 0.0f); + RS::get_singleton()->material_set_param(_get_material(), "height_falloff", height_falloff); +} + +float FogMaterial::get_height_falloff() const { + return height_falloff; +} + +void FogMaterial::set_edge_fade(float p_edge_fade) { + edge_fade = MAX(p_edge_fade, 0.0f); + RS::get_singleton()->material_set_param(_get_material(), "edge_fade", edge_fade); +} + +float FogMaterial::get_edge_fade() const { + return edge_fade; +} + +void FogMaterial::set_density_texture(const Ref<Texture3D> &p_texture) { + density_texture = p_texture; + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "density_texture", tex_rid); +} + +Ref<Texture3D> FogMaterial::get_density_texture() const { + return density_texture; +} + +Shader::Mode FogMaterial::get_shader_mode() const { + return Shader::MODE_FOG; +} + +RID FogMaterial::get_shader_rid() const { + _update_shader(); + return shader; +} + +RID FogMaterial::get_rid() const { + _update_shader(); + if (!shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_set = true; + } + return _get_material(); +} + +void FogMaterial::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_density", "density"), &FogMaterial::set_density); + ClassDB::bind_method(D_METHOD("get_density"), &FogMaterial::get_density); + ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &FogMaterial::set_albedo); + ClassDB::bind_method(D_METHOD("get_albedo"), &FogMaterial::get_albedo); + ClassDB::bind_method(D_METHOD("set_emission", "emission"), &FogMaterial::set_emission); + ClassDB::bind_method(D_METHOD("get_emission"), &FogMaterial::get_emission); + ClassDB::bind_method(D_METHOD("set_height_falloff", "height_falloff"), &FogMaterial::set_height_falloff); + ClassDB::bind_method(D_METHOD("get_height_falloff"), &FogMaterial::get_height_falloff); + ClassDB::bind_method(D_METHOD("set_edge_fade", "edge_fade"), &FogMaterial::set_edge_fade); + ClassDB::bind_method(D_METHOD("get_edge_fade"), &FogMaterial::get_edge_fade); + ClassDB::bind_method(D_METHOD("set_density_texture", "density_texture"), &FogMaterial::set_density_texture); + ClassDB::bind_method(D_METHOD("get_density_texture"), &FogMaterial::get_density_texture); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, "0.0,16.0,0.0001,or_greater,or_lesser"), "set_density", "get_density"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo", PROPERTY_HINT_COLOR_NO_ALPHA), "set_albedo", "get_albedo"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height_falloff", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_height_falloff", "get_height_falloff"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_fade", PROPERTY_HINT_EXP_EASING), "set_edge_fade", "get_edge_fade"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "density_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_density_texture", "get_density_texture"); +} + +void FogMaterial::cleanup_shader() { + if (shader.is_valid()) { + RS::get_singleton()->free(shader); + } +} + +void FogMaterial::_update_shader() { + shader_mutex.lock(); + if (shader.is_null()) { + shader = RS::get_singleton()->shader_create(); + + // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). + RS::get_singleton()->shader_set_code(shader, R"( +// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s FogMaterial. + +shader_type fog; + +uniform float density : hint_range(0, 1, 0.0001) = 1.0; +uniform vec4 albedo : hint_color = vec4(1.0); +uniform vec4 emission : hint_color = vec4(0, 0, 0, 1); +uniform float height_falloff = 0.0; +uniform float edge_fade = 0.1; +uniform sampler3D density_texture: hint_white; + + +void fog() { + DENSITY = density * clamp(exp2(-height_falloff * (WORLD_POSITION.y - OBJECT_POSITION.y)), 0.0, 1.0); + DENSITY *= texture(density_texture, UVW).r; + DENSITY *= pow(clamp(-SDF / min(min(EXTENTS.x, EXTENTS.y), EXTENTS.z), 0.0, 1.0), edge_fade); + ALBEDO = albedo.rgb; + EMISSION = emission.rgb; +} +)"); + } + shader_mutex.unlock(); +} + +FogMaterial::FogMaterial() { + set_density(1.0); + set_albedo(Color(1, 1, 1, 1)); + set_emission(Color(0, 0, 0, 1)); + + set_height_falloff(0.0); + set_edge_fade(0.1); +} + +FogMaterial::~FogMaterial() { + RS::get_singleton()->material_set_shader(_get_material(), RID()); +} diff --git a/scene/resources/fog_material.h b/scene/resources/fog_material.h new file mode 100644 index 0000000000..e256bd4719 --- /dev/null +++ b/scene/resources/fog_material.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* fog_material.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 FOG_MATERIAL_H +#define FOG_MATERIAL_H + +#include "scene/resources/material.h" + +class FogMaterial : public Material { + GDCLASS(FogMaterial, Material); + +private: + float density = 1.0; + Color albedo = Color(1, 1, 1, 1); + Color emission = Color(0, 0, 0, 0); + + float height_falloff = 0.0; + + float edge_fade = 0.1; + + Ref<Texture3D> density_texture; + + static Mutex shader_mutex; + static RID shader; + static void _update_shader(); + mutable bool shader_set = false; + +protected: + static void _bind_methods(); + +public: + void set_density(float p_density); + float get_density() const; + + void set_albedo(Color p_color); + Color get_albedo() const; + + void set_emission(Color p_color); + Color get_emission() const; + + void set_height_falloff(float p_falloff); + float get_height_falloff() const; + + void set_edge_fade(float p_edge_fade); + float get_edge_fade() const; + + void set_density_texture(const Ref<Texture3D> &p_texture); + Ref<Texture3D> get_density_texture() const; + + virtual Shader::Mode get_shader_mode() const override; + virtual RID get_shader_rid() const override; + virtual RID get_rid() const override; + + static void cleanup_shader(); + + FogMaterial(); + virtual ~FogMaterial(); +}; + +#endif // FOG_MATERIAL_H diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index 7b9b942142..4559b4ce0a 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -51,6 +51,8 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset); ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset); + ClassDB::bind_method(D_METHOD("reverse"), &Gradient::reverse); + ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color); ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color); @@ -64,8 +66,18 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors); ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors); + ClassDB::bind_method(D_METHOD("set_interpolation_mode", "interpolation_mode"), &Gradient::set_interpolation_mode); + ClassDB::bind_method(D_METHOD("get_interpolation_mode"), &Gradient::get_interpolation_mode); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode"); + + ADD_GROUP("Raw data", ""); ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors"); + + BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_LINEAR); + BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CONSTANT); + BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CUBIC); } Vector<float> Gradient::get_offsets() const { @@ -86,6 +98,15 @@ Vector<Color> Gradient::get_colors() const { return colors; } +void Gradient::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { + interpolation_mode = p_interp_mode; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +Gradient::InterpolationMode Gradient::get_interpolation_mode() { + return interpolation_mode; +} + void Gradient::set_offsets(const Vector<float> &p_offsets) { points.resize(p_offsets.size()); for (int i = 0; i < points.size(); i++) { @@ -127,6 +148,15 @@ void Gradient::remove_point(int p_index) { emit_signal(CoreStringNames::get_singleton()->changed); } +void Gradient::reverse() { + for (int i = 0; i < points.size(); i++) { + points.write[i].offset = 1.0 - points[i].offset; + } + + _update_sorting(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + void Gradient::set_points(Vector<Gradient::Point> &p_points) { points = p_points; is_sorted = false; diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index cf5b179c45..eb438d0bba 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -38,6 +38,12 @@ class Gradient : public Resource { OBJ_SAVE_TYPE(Gradient); public: + enum InterpolationMode { + GRADIENT_INTERPOLATE_LINEAR, + GRADIENT_INTERPOLATE_CONSTANT, + GRADIENT_INTERPOLATE_CUBIC, + }; + struct Point { float offset = 0.0; Color color; @@ -49,6 +55,8 @@ public: private: Vector<Point> points; bool is_sorted = true; + InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR; + _FORCE_INLINE_ void _update_sorting() { if (!is_sorted) { points.sort(); @@ -65,9 +73,9 @@ public: void add_point(float p_offset, const Color &p_color); void remove_point(int p_index); - void set_points(Vector<Point> &p_points); Vector<Point> &get_points(); + void reverse(); void set_offset(int pos, const float offset); float get_offset(int pos); @@ -81,6 +89,13 @@ public: void set_colors(const Vector<Color> &p_colors); Vector<Color> get_colors() const; + void set_interpolation_mode(InterpolationMode p_interp_mode); + InterpolationMode get_interpolation_mode(); + + _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) { + return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0))); + } + _FORCE_INLINE_ Color get_color_at_offset(float p_offset) { if (points.is_empty()) { return Color(0, 0, 0, 1); @@ -88,7 +103,7 @@ public: _update_sorting(); - //binary search + // Binary search. int low = 0; int high = points.size() - 1; int middle = 0; @@ -111,7 +126,7 @@ public: } } - //return interpolated value + // Return interpolated value. if (points[middle].offset > p_offset) { middle--; } @@ -125,10 +140,44 @@ public: } const Point &pointFirst = points[first]; const Point &pointSecond = points[second]; - return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); + + switch (interpolation_mode) { + case GRADIENT_INTERPOLATE_LINEAR: { + return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); + } break; + case GRADIENT_INTERPOLATE_CONSTANT: { + return pointFirst.color; + } break; + case GRADIENT_INTERPOLATE_CUBIC: { + int p0 = first - 1; + int p3 = second + 1; + if (p3 >= points.size()) { + p3 = second; + } + if (p0 < 0) { + p0 = first; + } + const Point &pointP0 = points[p0]; + const Point &pointP3 = points[p3]; + + float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset); + float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x); + float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x); + float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x); + float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x); + + return Color(r, g, b, a); + } break; + default: { + // Fallback to linear interpolation. + return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); + } + } } int get_points_count() const; }; +VARIANT_ENUM_CAST(Gradient::InterpolationMode); + #endif // GRADIENT_H diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 076b8312b6..7afa4c91f0 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -1243,5 +1243,5 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint); ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data"); } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index abb3381c4e..8399b14a56 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1086,7 +1086,7 @@ void BaseMaterial3D::_update_shader() { code += " ALPHA = 1.0;\n"; } else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) { - code += " ALPHA = albedo.a * albedo_tex.a;\n"; + code += " ALPHA *= albedo.a * albedo_tex.a;\n"; } if (transparency == TRANSPARENCY_ALPHA_HASH) { code += " ALPHA_HASH_SCALE = alpha_hash_scale;\n"; @@ -1100,7 +1100,7 @@ void BaseMaterial3D::_update_shader() { if (proximity_fade_enabled) { code += " float depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n"; - code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n"; + code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex,1.0);\n"; code += " world_pos.xyz/=world_pos.w;\n"; code += " ALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n"; } @@ -1695,7 +1695,7 @@ BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const { void BaseMaterial3D::_validate_feature(const String &text, Feature feature, PropertyInfo &property) const { if (property.name.begins_with(text) && property.name != text + "_enabled" && !features[feature]) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -1729,23 +1729,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { } if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } if (property.name == "grow_amount" && !grow_enabled) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } // you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 7ffe0b03e1..51b4e1fbd8 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1842,8 +1842,8 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces); ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces"); ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh"); diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 04b2437ae8..9ecd8ec2f3 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -421,6 +421,7 @@ Vector<int> MeshDataTool::get_vertex_bones(int p_idx) const { void MeshDataTool::set_vertex_bones(int p_idx, const Vector<int> &p_bones) { ERR_FAIL_INDEX(p_idx, vertices.size()); + ERR_FAIL_COND(p_bones.size() != 4); vertices.write[p_idx].bones = p_bones; format |= Mesh::ARRAY_FORMAT_BONES; } @@ -432,6 +433,7 @@ Vector<float> MeshDataTool::get_vertex_weights(int p_idx) const { void MeshDataTool::set_vertex_weights(int p_idx, const Vector<float> &p_weights) { ERR_FAIL_INDEX(p_idx, vertices.size()); + ERR_FAIL_COND(p_weights.size() != 4); vertices.write[p_idx].weights = p_weights; format |= Mesh::ARRAY_FORMAT_WEIGHTS; } diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index d87056f55d..db091ec37b 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -477,8 +477,8 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons); ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 60cda637ca..f43715a463 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -34,10 +34,12 @@ #include "core/config/project_settings.h" #include "core/core_string_names.h" #include "core/io/resource_loader.h" +#include "editor/editor_inspector.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" +#include "scene/property_utils.h" #define PACKED_SCENE_VERSION 2 @@ -45,6 +47,30 @@ bool SceneState::can_instantiate() const { return nodes.size() > 0; } +static Array _sanitize_node_pinned_properties(Node *p_node) { + if (!p_node->has_meta("_edit_pinned_properties_")) { + return Array(); + } + Array pinned = p_node->get_meta("_edit_pinned_properties_"); + if (pinned.is_empty()) { + return Array(); + } + Set<StringName> storable_properties; + p_node->get_storable_properties(storable_properties); + int i = 0; + do { + if (storable_properties.has(pinned[i])) { + i++; + } else { + pinned.remove(i); + } + } while (i < pinned.size()); + if (pinned.is_empty()) { + p_node->remove_meta("_edit_pinned_properties_"); + } + return pinned; +} + Node *SceneState::instantiate(GenEditState p_edit_state) const { // nodes where instancing failed (because something is missing) List<Node *> stray_instances; @@ -227,7 +253,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else { Node *base = i == 0 ? node : ret_nodes[0]; - if (p_edit_state == GEN_EDIT_STATE_MAIN) { + if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) { //for the main scene, use the resource as is res->configure_for_local_scene(base, resources_local_to_scene); resources_local_to_scene[res] = res; @@ -291,6 +317,13 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + if (p_edit_state == GEN_EDIT_STATE_MAIN) { + _sanitize_node_pinned_properties(node); + } else { + node->remove_meta("_edit_pinned_properties_"); + } + ret_nodes[i] = node; if (node && gen_node_path_cache && ret_nodes[0]) { @@ -415,61 +448,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // with the instance states, we can query for identical properties/groups // and only save what has changed - List<PackState> pack_state_stack; - - bool instantiated_by_owner = true; - - { - Node *n = p_node; - - while (n) { - if (n == p_owner) { - Ref<SceneState> state = n->get_scene_inherited_state(); - if (state.is_valid()) { - int node = state->find_node_by_path(n->get_path_to(p_node)); - if (node >= 0) { - //this one has state for this node, save - PackState ps; - ps.node = node; - ps.state = state; - pack_state_stack.push_back(ps); - instantiated_by_owner = false; - } - } - - if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { - if (p_node->get_scene_instance_load_placeholder()) { - //it's a placeholder, use the placeholder path - nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); - nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; - } else { - //must instance ourselves - Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); - if (!instance.is_valid()) { - return ERR_CANT_OPEN; - } + bool instantiated_by_owner = false; + Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instantiated_by_owner); - nd.instance = _vm_get_variant(instance, variant_map); - } - } - n = nullptr; - } else { - if (n->get_scene_file_path() != String()) { - //is an instance - Ref<SceneState> state = n->get_scene_instance_state(); - if (state.is_valid()) { - int node = state->find_node_by_path(n->get_path_to(p_node)); - if (node >= 0) { - //this one has state for this node, save - PackState ps; - ps.node = node; - ps.state = state; - pack_state_stack.push_back(ps); - } - } - } - n = n->get_owner(); + if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { + if (p_node->get_scene_instance_load_placeholder()) { + //it's a placeholder, use the placeholder path + nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); + nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; + } else { + //must instance ourselves + Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); + if (!instance.is_valid()) { + return ERR_CANT_OPEN; } + + nd.instance = _vm_get_variant(instance, variant_map); } } @@ -478,88 +472,37 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map List<PropertyInfo> plist; p_node->get_property_list(&plist); - StringName type = p_node->get_class(); - Ref<Script> script = p_node->get_script(); - if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) { - // Should be called in the editor only and not at runtime, - // otherwise it can cause problems because of missing instance state support. - script->update_exports(); - } + Array pinned_props = _sanitize_node_pinned_properties(p_node); for (const PropertyInfo &E : plist) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - String name = E.name; - Variant value = p_node->get(E.name); - - bool isdefault = false; - Variant default_value = ClassDB::class_get_default_property_value(type, name); - - if (default_value.get_type() != Variant::NIL) { - isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value)); - } + Variant forced_value; - if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) { - isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value)); - } - // the version above makes more sense, because it does not rely on placeholder or usage flag - // in the script, just the default value function. - // if (E.usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) { - // isdefault = true; //is script default value - // } - - if (pack_state_stack.size()) { - // we are on part of an instantiated subscene - // or part of instantiated scene. - // only save what has been changed - // only save changed properties in instance - - if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E.name == "__meta__") { - //property has requested that no instance state is saved, sorry - //also, meta won't be overridden or saved + // If instance or inheriting, not saving if property requested so, or it's meta + if (states_stack.size()) { + if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { continue; } - - bool exists = false; - Variant original; - - for (List<PackState>::Element *F = pack_state_stack.back(); F; F = F->prev()) { - //check all levels of pack to see if the property exists somewhere - const PackState &ps = F->get(); - - original = ps.state->get_property_value(ps.node, E.name, exists); - if (exists) { - break; - } - } - - if (exists) { - //check if already exists and did not change - if (value.get_type() == Variant::FLOAT && original.get_type() == Variant::FLOAT) { - //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error - float a = value; - float b = original; - - if (Math::is_equal_approx(a, b)) { - continue; - } - } else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) { - continue; + // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list + if (E.name == "__meta__") { + if (pinned_props.size()) { + Dictionary meta_override; + meta_override["_edit_pinned_properties_"] = pinned_props; + forced_value = meta_override; } } + } - if (!exists && isdefault) { - //does not exist in original node, but it's the default value - //so safe to skip too. - continue; - } + StringName name = E.name; + Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value; - } else { - if (isdefault) { - //it's the default value, no point in saving it + if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) { + Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &states_stack, true); + if (!PropertyUtils::is_property_value_different(value, default_value)) { continue; } } @@ -585,10 +528,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map */ bool skip = false; - for (const PackState &F : pack_state_stack) { + for (const SceneState::PackState &ia : states_stack) { //check all levels of pack to see if the group was added somewhere - const PackState &ps = F; - if (ps.state->is_node_in_group(ps.node, gi.name)) { + if (ia.state->is_node_in_group(ia.node, gi.name)) { skip = true; break; } @@ -618,7 +560,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused - if (pack_state_stack.is_empty() && !is_editable_instance) { + if (states_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type nd.type = _nm_get_string(p_node->get_class(), name_map); } else { @@ -635,7 +577,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist save_node = save_node || p_node == p_owner; // owner is always saved - save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated + save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced int idx = nodes.size(); int parent_node = NO_PARENT_SAVED; @@ -932,7 +874,7 @@ void SceneState::clear() { base_scene_idx = -1; } -Ref<SceneState> SceneState::_get_base_scene_state() const { +Ref<SceneState> SceneState::get_base_scene_state() const { if (base_scene_idx >= 0) { Ref<PackedScene> ps = variants[base_scene_idx]; if (ps.is_valid()) { @@ -947,8 +889,8 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built."); if (!node_path_cache.has(p_node)) { - if (_get_base_scene_state().is_valid()) { - int idx = _get_base_scene_state()->find_node_by_path(p_node); + if (get_base_scene_state().is_valid()) { + int idx = get_base_scene_state()->find_node_by_path(p_node); if (idx != -1) { int rkey = _find_base_scene_node_remap_key(idx); if (rkey == -1) { @@ -963,11 +905,11 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { int nid = node_path_cache[p_node]; - if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { + if (get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { //for nodes that _do_ exist in current scene, still try to look for //the node in the instantiated scene, as a property may be missing //from the local one - int idx = _get_base_scene_state()->find_node_by_path(p_node); + int idx = get_base_scene_state()->find_node_by_path(p_node); if (idx != -1) { base_scene_node_remap[nid] = idx; } @@ -1007,7 +949,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property, //property not found, try on instance if (base_scene_node_remap.has(p_node)) { - return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found); + return get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found); } return Variant(); @@ -1026,7 +968,7 @@ bool SceneState::is_node_in_group(int p_node, const StringName &p_group) const { } if (base_scene_node_remap.has(p_node)) { - return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group); + return get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group); } return false; @@ -1065,7 +1007,7 @@ bool SceneState::is_connection(int p_node, const StringName &p_signal, int p_to_ } if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) { - return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method); + return get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method); } return false; @@ -1488,7 +1430,7 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p } } - ss = ss->_get_base_scene_state(); + ss = ss->get_base_scene_state(); } while (ss.is_valid()); return false; @@ -1610,6 +1552,7 @@ void SceneState::_bind_methods() { BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN); + BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED); } SceneState::SceneState() { @@ -1651,7 +1594,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { s->set_scene_instance_state(state); } - if (get_path() != "" && get_path().find("::") == -1) { + if (!is_built_in()) { s->set_scene_file_path(get_path()); } @@ -1701,6 +1644,7 @@ void PackedScene::_bind_methods() { BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN); + BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED); } PackedScene::PackedScene() { diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 55708f7914..a03da558e4 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -69,11 +69,6 @@ class SceneState : public RefCounted { Vector<int> groups; }; - struct PackState { - Ref<SceneState> state; - int node = -1; - }; - Vector<NodeData> nodes; struct ConnectionData { @@ -94,8 +89,6 @@ class SceneState : public RefCounted { uint64_t last_modified_time = 0; - _FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const; - static bool disable_placeholders; Vector<String> _get_node_groups(int p_idx) const; @@ -117,6 +110,12 @@ public: GEN_EDIT_STATE_DISABLED, GEN_EDIT_STATE_INSTANCE, GEN_EDIT_STATE_MAIN, + GEN_EDIT_STATE_MAIN_INHERITED, + }; + + struct PackState { + Ref<SceneState> state; + int node = -1; }; static void set_disable_placeholders(bool p_disable); @@ -139,6 +138,8 @@ public: bool can_instantiate() const; Node *instantiate(GenEditState p_edit_state) const; + Ref<SceneState> get_base_scene_state() const; + //unbuild API int get_node_count() const; @@ -207,6 +208,7 @@ public: GEN_EDIT_STATE_DISABLED, GEN_EDIT_STATE_INSTANCE, GEN_EDIT_STATE_MAIN, + GEN_EDIT_STATE_MAIN_INHERITED, }; Error pack(Node *p_scene); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index d9ec0bfd69..e77f5a0be7 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -1413,7 +1413,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_ramp", "get_color_ramp"); ADD_GROUP("Hue Variation", "hue_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 4dd3c874cb..ec2022ed2f 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -556,7 +556,7 @@ void PolygonPathFinder::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } PolygonPathFinder::PolygonPathFinder() { diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 77d915aef9..ea3b72af1b 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1492,7 +1492,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { } else { if (internal_resources.has(res)) { return "SubResource( \"" + internal_resources[res] + "\" )"; - } else if (res->get_path().length() && res->get_path().find("::") == -1) { + } else if (!res->is_built_in()) { if (res->get_path() == local_path) { //circular reference attempt return "null"; } @@ -1515,7 +1515,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, return; } - if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { + if (!p_main && (!bundle_resources) && !res->is_built_in()) { if (res->get_path() == local_path) { ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); return; @@ -1728,7 +1728,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { RES res = E->get(); - if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) { + if (E->next() && res->is_built_in()) { if (res->get_scene_unique_id() != "") { if (used_unique_ids.has(res->get_scene_unique_id())) { res->set_scene_unique_id(""); // Repeated. diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 242e20f3b0..9fab5791d6 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -49,6 +49,8 @@ void Shader::set_code(const String &p_code) { mode = MODE_PARTICLES; } else if (type == "sky") { mode = MODE_SKY; + } else if (type == "fog") { + mode = MODE_FOG; } else { mode = MODE_SPATIAL; } @@ -143,12 +145,13 @@ void Shader::_bind_methods() { ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_code", "get_code"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code"); BIND_ENUM_CONSTANT(MODE_SPATIAL); BIND_ENUM_CONSTANT(MODE_CANVAS_ITEM); BIND_ENUM_CONSTANT(MODE_PARTICLES); BIND_ENUM_CONSTANT(MODE_SKY); + BIND_ENUM_CONSTANT(MODE_FOG); } Shader::Shader() { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 6563181ca2..c0dc07b403 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -46,6 +46,7 @@ public: MODE_CANVAS_ITEM, MODE_PARTICLES, MODE_SKY, + MODE_FOG, MODE_MAX }; diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp index 84abc9d020..31045455a3 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -194,9 +194,13 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); PhysicsDirectSpaceState2D::RayResult ray_result; + PhysicsDirectSpaceState2D::RayParameters ray_params; + ray_params.from = operation_bone_trans.get_origin(); + ray_params.to = jiggle_data_chain[p_joint_idx].dynamic_position; + ray_params.collision_mask = collision_mask; + // Add exception support? - bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position, - ray_result, Set<RID>(), collision_mask); + bool ray_hit = space_state->intersect_ray(ray_params, ray_result); if (ray_hit) { jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position; diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp index 2da770f012..740937fc44 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -241,7 +241,7 @@ int SkeletonModification2DLookAt::get_bone_index() const { void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); - if (is_setup) { + if (is_setup && stack) { if (stack->skeleton) { ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); bone_idx = p_bone_idx; diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp index a6bcb0176a..2535f2b987 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.cpp +++ b/scene/resources/skeleton_modification_3d_jiggle.cpp @@ -206,8 +206,12 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans); Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position)); - bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(), - ray_result, Set<RID>(), collision_mask); + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = new_bone_trans_world.origin; + ray_params.to = dynamic_position_world.get_origin(); + ray_params.collision_mask = collision_mask; + + bool ray_hit = space_state->intersect_ray(ray_params, ray_result); if (ray_hit) { jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position; diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index a724b732b9..c03210cf48 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -125,6 +125,7 @@ Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_ } void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) { + ERR_FAIL_NULL(p_mod); p_mod->_setup_modification(this); modifications.push_back(p_mod); } diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index 710612ae05..15cdb86bab 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -133,7 +133,7 @@ void Skin::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); for (int i = 0; i < get_bind_count(); i++) { p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name")); - p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT)); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose")); } } diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 01afb00283..5524d59dc7 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -233,7 +233,7 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility } SpriteFrames::SpriteFrames() { diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 3381043d29..b960944d99 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -790,7 +790,7 @@ float StyleBoxFlat::get_style_margin(Side p_side) const { void StyleBoxFlat::_validate_property(PropertyInfo &property) const { if (!anti_aliased && property.name == "anti_aliasing_size") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 1b3a7a5f2a..311bd9524b 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "core/io/image_loader.h" +#include "core/math/geometry_2d.h" #include "core/os/os.h" #include "mesh.h" #include "scene/resources/bit_map.h" @@ -1502,6 +1503,7 @@ Ref<Curve> CurveTexture::get_curve() const { } void CurveTexture::set_texture_mode(TextureMode p_mode) { + ERR_FAIL_COND(p_mode < TEXTURE_MODE_RGB || p_mode > TEXTURE_MODE_RED); if (texture_mode == p_mode) { return; } @@ -1727,53 +1729,53 @@ CurveXYZTexture::~CurveXYZTexture() { ////////////////// -GradientTexture::GradientTexture() { +GradientTexture1D::GradientTexture1D() { _queue_update(); } -GradientTexture::~GradientTexture() { +GradientTexture1D::~GradientTexture1D() { if (texture.is_valid()) { RS::get_singleton()->free(texture); } } -void GradientTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture::set_gradient); - ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient); +void GradientTexture1D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient); + ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient); - ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width); + ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width); // The `get_width()` method is already exposed by the parent class Texture2D. - ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture::set_use_hdr); - ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture::is_using_hdr); + ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr); + ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr); - ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update); + ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); } -void GradientTexture::set_gradient(Ref<Gradient> p_gradient) { +void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) { if (p_gradient == gradient) { return; } if (gradient.is_valid()) { - gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update)); + gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update)); } gradient = p_gradient; if (gradient.is_valid()) { - gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update)); + gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update)); } _update(); emit_changed(); } -Ref<Gradient> GradientTexture::get_gradient() const { +Ref<Gradient> GradientTexture1D::get_gradient() const { return gradient; } -void GradientTexture::_queue_update() { +void GradientTexture1D::_queue_update() { if (update_pending) { return; } @@ -1782,7 +1784,7 @@ void GradientTexture::_queue_update() { call_deferred(SNAME("_update")); } -void GradientTexture::_update() { +void GradientTexture1D::_update() { update_pending = false; if (gradient.is_null()) { @@ -1837,17 +1839,17 @@ void GradientTexture::_update() { emit_changed(); } -void GradientTexture::set_width(int p_width) { +void GradientTexture1D::set_width(int p_width) { ERR_FAIL_COND(p_width <= 0); width = p_width; _queue_update(); } -int GradientTexture::get_width() const { +int GradientTexture1D::get_width() const { return width; } -void GradientTexture::set_use_hdr(bool p_enabled) { +void GradientTexture1D::set_use_hdr(bool p_enabled) { if (p_enabled == use_hdr) { return; } @@ -1856,17 +1858,271 @@ void GradientTexture::set_use_hdr(bool p_enabled) { _queue_update(); } -bool GradientTexture::is_using_hdr() const { +bool GradientTexture1D::is_using_hdr() const { return use_hdr; } -Ref<Image> GradientTexture::get_image() const { +Ref<Image> GradientTexture1D::get_image() const { if (!texture.is_valid()) { return Ref<Image>(); } return RenderingServer::get_singleton()->texture_2d_get(texture); } +GradientTexture2D::GradientTexture2D() { + _queue_update(); +} + +GradientTexture2D::~GradientTexture2D() { + if (texture.is_valid()) { + RS::get_singleton()->free(texture); + } +} + +void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) { + if (gradient == p_gradient) { + return; + } + if (gradient.is_valid()) { + gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); + } + gradient = p_gradient; + if (gradient.is_valid()) { + gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); + } + _queue_update(); +} + +Ref<Gradient> GradientTexture2D::get_gradient() const { + return gradient; +} + +void GradientTexture2D::_queue_update() { + if (update_pending) { + return; + } + update_pending = true; + call_deferred("_update"); +} + +void GradientTexture2D::_update() { + update_pending = false; + + if (gradient.is_null()) { + return; + } + Ref<Image> image; + image.instantiate(); + + if (gradient->get_points_count() <= 1) { // No need to interpolate. + image->create(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); + image->fill((gradient->get_points_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1)); + } else { + if (use_hdr) { + image->create(width, height, false, Image::FORMAT_RGBAF); + Gradient &g = **gradient; + // `create()` isn't available for non-uint8_t data, so fill in the data manually. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float ofs = _get_gradient_offset_at(x, y); + image->set_pixel(x, y, g.get_color_at_offset(ofs)); + } + } + } else { + Vector<uint8_t> data; + data.resize(width * height * 4); + { + uint8_t *wd8 = data.ptrw(); + Gradient &g = **gradient; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float ofs = _get_gradient_offset_at(x, y); + const Color &c = g.get_color_at_offset(ofs); + + wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255)); + } + } + } + image->create(width, height, false, Image::FORMAT_RGBA8, data); + } + } + + if (texture.is_valid()) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(texture, new_texture); + } else { + texture = RS::get_singleton()->texture_2d_create(image); + } + emit_changed(); +} + +float GradientTexture2D::_get_gradient_offset_at(int x, int y) const { + if (fill_to == fill_from) { + return 0; + } + float ofs = 0; + Vector2 pos; + if (width > 1) { + pos.x = static_cast<float>(x) / (width - 1); + } + if (height > 1) { + pos.y = static_cast<float>(y) / (height - 1); + } + if (fill == Fill::FILL_LINEAR) { + Vector2 segment[2]; + segment[0] = fill_from; + segment[1] = fill_to; + Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, &segment[0]); + ofs = (closest - fill_from).length() / (fill_to - fill_from).length(); + if ((closest - fill_from).dot(fill_to - fill_from) < 0) { + ofs *= -1; + } + } else if (fill == Fill::FILL_RADIAL) { + ofs = (pos - fill_from).length() / (fill_to - fill_from).length(); + } + if (repeat == Repeat::REPEAT_NONE) { + ofs = CLAMP(ofs, 0.0, 1.0); + } else if (repeat == Repeat::REPEAT) { + ofs = Math::fmod(ofs, 1.0f); + if (ofs < 0) { + ofs = 1 + ofs; + } + } else if (repeat == Repeat::REPEAT_MIRROR) { + ofs = Math::abs(ofs); + ofs = Math::fmod(ofs, 2.0f); + if (ofs > 1.0) { + ofs = 2.0 - ofs; + } + } + return ofs; +} + +void GradientTexture2D::set_width(int p_width) { + width = p_width; + _queue_update(); +} + +int GradientTexture2D::get_width() const { + return width; +} + +void GradientTexture2D::set_height(int p_height) { + height = p_height; + _queue_update(); +} +int GradientTexture2D::get_height() const { + return height; +} + +void GradientTexture2D::set_use_hdr(bool p_enabled) { + if (p_enabled == use_hdr) { + return; + } + + use_hdr = p_enabled; + _queue_update(); +} + +bool GradientTexture2D::is_using_hdr() const { + return use_hdr; +} + +void GradientTexture2D::set_fill_from(Vector2 p_fill_from) { + fill_from = p_fill_from; + _queue_update(); +} + +Vector2 GradientTexture2D::get_fill_from() const { + return fill_from; +} + +void GradientTexture2D::set_fill_to(Vector2 p_fill_to) { + fill_to = p_fill_to; + _queue_update(); +} + +Vector2 GradientTexture2D::get_fill_to() const { + return fill_to; +} + +void GradientTexture2D::set_fill(Fill p_fill) { + fill = p_fill; + _queue_update(); +} + +GradientTexture2D::Fill GradientTexture2D::get_fill() const { + return fill; +} + +void GradientTexture2D::set_repeat(Repeat p_repeat) { + repeat = p_repeat; + _queue_update(); +} + +GradientTexture2D::Repeat GradientTexture2D::get_repeat() const { + return repeat; +} + +RID GradientTexture2D::get_rid() const { + if (!texture.is_valid()) { + texture = RS::get_singleton()->texture_2d_placeholder_create(); + } + return texture; +} + +Ref<Image> GradientTexture2D::get_image() const { + if (!texture.is_valid()) { + return Ref<Image>(); + } + return RenderingServer::get_singleton()->texture_2d_get(texture); +} + +void GradientTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture2D::set_gradient); + ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture2D::get_gradient); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture2D::set_width); + ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture2D::set_height); + + ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture2D::set_use_hdr); + ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture2D::is_using_hdr); + + ClassDB::bind_method(D_METHOD("set_fill", "fill"), &GradientTexture2D::set_fill); + ClassDB::bind_method(D_METHOD("get_fill"), &GradientTexture2D::get_fill); + ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture2D::set_fill_from); + ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture2D::get_fill_from); + ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture2D::set_fill_to); + ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture2D::get_fill_to); + + ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &GradientTexture2D::set_repeat); + ClassDB::bind_method(D_METHOD("get_repeat"), &GradientTexture2D::get_repeat); + + ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); + + ADD_GROUP("Fill", "fill_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fill", PROPERTY_HINT_ENUM, "Linear,Radial"), "set_fill", "get_fill"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to"); + + ADD_GROUP("Repeat", "repeat_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Mirror Repeat"), "set_repeat", "get_repeat"); + + BIND_ENUM_CONSTANT(FILL_LINEAR); + BIND_ENUM_CONSTANT(FILL_RADIAL); + + BIND_ENUM_CONSTANT(REPEAT_NONE); + BIND_ENUM_CONSTANT(REPEAT); + BIND_ENUM_CONSTANT(REPEAT_MIRROR); +} + ////////////////////////////////////// void ProxyTexture::_bind_methods() { @@ -2615,15 +2871,6 @@ RID CameraTexture::get_rid() const { } } -void CameraTexture::set_flags(uint32_t p_flags) { - // not supported -} - -uint32_t CameraTexture::get_flags() const { - // not supported - return 0; -} - Ref<Image> CameraTexture::get_image() const { // not (yet) supported return Ref<Image>(); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 862b9a47a6..5b69711de6 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -669,8 +669,8 @@ public: ~CurveXYZTexture(); }; -class GradientTexture : public Texture2D { - GDCLASS(GradientTexture, Texture2D); +class GradientTexture1D : public Texture2D { + GDCLASS(GradientTexture1D, Texture2D); public: struct Point { @@ -710,10 +710,81 @@ public: virtual Ref<Image> get_image() const override; - GradientTexture(); - virtual ~GradientTexture(); + GradientTexture1D(); + virtual ~GradientTexture1D(); }; +class GradientTexture2D : public Texture2D { + GDCLASS(GradientTexture2D, Texture2D); + +public: + enum Fill { + FILL_LINEAR, + FILL_RADIAL, + }; + enum Repeat { + REPEAT_NONE, + REPEAT, + REPEAT_MIRROR, + }; + +private: + Ref<Gradient> gradient; + mutable RID texture; + + int width = 64; + int height = 64; + + bool use_hdr = false; + + Vector2 fill_from; + Vector2 fill_to = Vector2(1, 0); + + Fill fill = FILL_LINEAR; + Repeat repeat = REPEAT_NONE; + + float _get_gradient_offset_at(int x, int y) const; + + bool update_pending = false; + void _queue_update(); + void _update(); + +protected: + static void _bind_methods(); + +public: + void set_gradient(Ref<Gradient> p_gradient); + Ref<Gradient> get_gradient() const; + + void set_width(int p_width); + virtual int get_width() const override; + void set_height(int p_height); + virtual int get_height() const override; + + void set_use_hdr(bool p_enabled); + bool is_using_hdr() const; + + void set_fill(Fill p_fill); + Fill get_fill() const; + void set_fill_from(Vector2 p_fill_from); + Vector2 get_fill_from() const; + void set_fill_to(Vector2 p_fill_to); + Vector2 get_fill_to() const; + + void set_repeat(Repeat p_repeat); + Repeat get_repeat() const; + + virtual RID get_rid() const override; + virtual bool has_alpha() const override { return true; } + virtual Ref<Image> get_image() const override; + + GradientTexture2D(); + virtual ~GradientTexture2D(); +}; + +VARIANT_ENUM_CAST(GradientTexture2D::Fill); +VARIANT_ENUM_CAST(GradientTexture2D::Repeat); + class ProxyTexture : public Texture2D { GDCLASS(ProxyTexture, Texture2D); @@ -829,9 +900,6 @@ public: virtual RID get_rid() const override; virtual bool has_alpha() const override; - virtual void set_flags(uint32_t p_flags); - virtual uint32_t get_flags() const; - virtual Ref<Image> get_image() const override; void set_camera_feed_id(int p_new_id); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index a45b6b8eb6..5256803d56 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -203,7 +203,7 @@ bool TileMapPattern::_get(const StringName &p_name, Variant &r_ret) const { } void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } void TileMapPattern::_bind_methods() { @@ -212,7 +212,7 @@ void TileMapPattern::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell); - ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell); + ClassDB::bind_method(D_METHOD("remove_cell", "coords", "update_size"), &TileMapPattern::remove_cell); ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id); ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords); ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile); @@ -225,6 +225,90 @@ void TileMapPattern::_bind_methods() { /////////////////////////////// TileSet ////////////////////////////////////// +bool TileSet::TerrainsPattern::is_valid() const { + return valid; +} + +bool TileSet::TerrainsPattern::is_erase_pattern() const { + return not_empty_terrains_count == 0; +} + +bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_pattern) const { + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) { + return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i]; + } + } + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) { + return bits[i] < p_terrains_pattern.bits[i]; + } + } + return false; +} + +bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_pattern) const { + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) { + return false; + } + if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) { + return false; + } + } + return true; +} + +void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) { + ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX); + ERR_FAIL_COND(!is_valid_bit[p_peering_bit]); + ERR_FAIL_COND(p_terrain < -1); + + // Update the "is_erase_pattern" status. + if (p_terrain >= 0 && bits[p_peering_bit] < 0) { + not_empty_terrains_count++; + } else if (p_terrain < 0 && bits[p_peering_bit] >= 0) { + not_empty_terrains_count--; + } + + bits[p_peering_bit] = p_terrain; +} + +int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1); + ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1); + return bits[p_peering_bit]; +} + +void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) { + int in_array_index = 0; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (is_valid_bit[i]) { + ERR_FAIL_COND(in_array_index >= p_terrains.size()); + set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]); + in_array_index++; + } + } +} + +Array TileSet::TerrainsPattern::get_terrains_as_array() const { + Array output; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (is_valid_bit[i]) { + output.push_back(bits[i]); + } + } + return output; +} +TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) { + ERR_FAIL_COND(p_terrain_set < 0); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i))); + bits[i] = -1; + } + valid = true; +} + const int TileSet::INVALID_SOURCE = -1; const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { @@ -301,6 +385,64 @@ int TileSet::get_next_source_id() const { return next_source_id; } +void TileSet::_update_terrains_cache() { + if (terrains_cache_dirty) { + // Organizes tiles into structures. + per_terrain_pattern_tiles.resize(terrain_sets.size()); + for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) { + per_terrain_pattern_tiles[i].clear(); + } + + for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) { + Ref<TileSetSource> source = kv.value; + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) { + Vector2i tile_id = source->get_tile_id(tile_index); + for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) { + int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index); + + // Executed for each tile_data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id)); + int terrain_set = tile_data->get_terrain_set(); + if (terrain_set >= 0) { + TileMapCell cell; + cell.source_id = kv.key; + cell.set_atlas_coords(tile_id); + cell.alternative_tile = alternative_id; + + TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern(); + + // Terrain bits. + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + if (is_valid_peering_bit_terrain(terrain_set, bit)) { + int terrain = terrains_pattern.get_terrain(bit); + if (terrain >= 0) { + per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell); + } + } + } + } + } + } + } + } + + // Add the empty cell in the possible patterns and cells. + for (int i = 0; i < terrain_sets.size(); i++) { + TileSet::TerrainsPattern empty_pattern(this, i); + + TileMapCell empty_cell; + empty_cell.source_id = TileSet::INVALID_SOURCE; + empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + per_terrain_pattern_tiles[i][empty_pattern].insert(empty_cell); + } + terrains_cache_dirty = false; + } +} + void TileSet::_compute_next_source_id() { while (sources.has(next_source_id)) { next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30 @@ -321,6 +463,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); + terrains_cache_dirty = true; emit_changed(); return new_source_id; @@ -336,6 +479,7 @@ void TileSet::remove_source(int p_source_id) { source_ids.erase(p_source_id); source_ids.sort(); + terrains_cache_dirty = true; emit_changed(); } @@ -357,6 +501,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { _compute_next_source_id(); + terrains_cache_dirty = true; emit_changed(); } @@ -545,6 +690,7 @@ void TileSet::add_terrain_set(int p_index) { } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -557,6 +703,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) { source.value->move_terrain_set(p_from_index, p_to_pos); } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -567,6 +714,7 @@ void TileSet::remove_terrain_set(int p_index) { source.value->remove_terrain_set(p_index); } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -578,6 +726,7 @@ void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -612,6 +761,7 @@ void TileSet::add_terrain(int p_terrain_set, int p_index) { } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -627,6 +777,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos); } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -640,6 +791,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) { source.value->remove_terrain(p_terrain_set, p_index); } notify_property_list_changed(); + terrains_cache_dirty = true; emit_changed(); } @@ -1196,6 +1348,73 @@ int TileSet::get_patterns_count() { return patterns.size(); } +Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>()); + _update_terrains_cache(); + + Set<TileSet::TerrainsPattern> output; + for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) { + output.insert(kv.key); + } + return output; +} + +Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>()); + _update_terrains_cache(); + return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; +} + +TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell()); + _update_terrains_cache(); + + // Count the sum of probabilities. + double sum = 0.0; + Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; + for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { + if (E->get().source_id >= 0) { + Ref<TileSetSource> source = sources[E->get().source_id]; + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile)); + sum += tile_data->get_probability(); + } else { + sum += 1.0; + } + } else { + sum += 1.0; + } + } + + // Generate a random number. + double count = 0.0; + double picked = Math::random(0.0, sum); + + // Pick the tile. + for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { + if (E->get().source_id >= 0) { + Ref<TileSetSource> source = sources[E->get().source_id]; + + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile)); + count += tile_data->get_probability(); + } else { + count += 1.0; + } + } else { + count += 1.0; + } + + if (count >= picked) { + return E->get(); + } + } + + ERR_FAIL_V(TileMapCell()); +} + Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { @@ -1510,6 +1729,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { } void TileSet::_source_changed() { + terrains_cache_dirty = true; emit_changed(); } @@ -2904,19 +3124,19 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Sources. // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } // Tile Proxies. // Note: proxies need to be set after sources are set. p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); // Patterns. for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NO_EDITOR)); } } @@ -3517,35 +3737,35 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> tile_property_list; // size_in_atlas - property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (E_tile.value.size_in_atlas == Vector2i(1, 1)) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // next_alternative_id - property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (E_tile.value.next_alternative_id == 1) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_columns. - property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (E_tile.value.animation_columns == 0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_separation. - property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (E_tile.value.animation_separation == Vector2i()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_speed. - property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (E_tile.value.animation_speed == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } @@ -3557,7 +3777,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // animation_frame_*. bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) { - property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); if (!store_durations) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } @@ -3566,7 +3786,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; @@ -3985,10 +4205,10 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size); ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NO_EDITOR), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size"); // Base tiles ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); @@ -4500,6 +4720,37 @@ bool TileData::is_allowing_transform() const { return allow_transform; } +TileData *TileData::duplicate() { + TileData *output = memnew(TileData); + output->tile_set = tile_set; + + output->allow_transform = allow_transform; + + // Rendering + output->flip_h = flip_h; + output->flip_v = flip_v; + output->transpose = transpose; + output->tex_offset = tex_offset; + output->material = material; + output->modulate = modulate; + output->z_index = z_index; + output->y_sort_origin = y_sort_origin; + output->occluders = occluders; + // Physics + output->physics = physics; + // Terrain + output->terrain_set = -1; + memcpy(output->terrain_peering_bits, terrain_peering_bits, 16 * sizeof(int)); + // Navigation + output->navigation = navigation; + // Misc + output->probability = probability; + // Custom data + output->custom_data = custom_data; + + return output; +} + // Rendering void TileData::set_flip_h(bool p_flip_h) { ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); @@ -4695,8 +4946,8 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>()); - ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>()); - return physics[p_layer_id].polygons[shape_index].shapes[shape_index]; + ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>()); + return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index]; } // Terrain @@ -4743,6 +4994,18 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit); } +TileSet::TerrainsPattern TileData::get_terrains_pattern() const { + ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern()); + + TileSet::TerrainsPattern output(tile_set, terrain_set); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) { + output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i))); + } + } + return output; +} + // Navigation void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) { ERR_FAIL_INDEX(p_layer_id, navigation.size()); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 530c90920f..71559074e7 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -254,6 +254,30 @@ public: Vector2 offset; }; + class TerrainsPattern { + bool valid = false; + int bits[TileSet::CELL_NEIGHBOR_MAX]; + bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX]; + + int not_empty_terrains_count = 0; + + public: + bool is_valid() const; + bool is_erase_pattern() const; + + bool operator<(const TerrainsPattern &p_terrains_pattern) const; + bool operator==(const TerrainsPattern &p_terrains_pattern) const; + + void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain); + int get_terrain(TileSet::CellNeighbor p_peering_bit) const; + + void set_terrains_from_array(Array p_terrains); + Array get_terrains_as_array() const; + + TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set); + TerrainsPattern() {} + }; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -303,6 +327,10 @@ private: Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; bool terrain_bits_meshes_dirty = true; + LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data. + bool terrains_cache_dirty = true; + void _update_terrains_cache(); + // Navigation struct NavigationLayer { uint32_t layers = 1; @@ -470,6 +498,11 @@ public: void remove_pattern(int p_index); int get_patterns_count(); + // Terrains. + Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set); + Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); + TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); + // Helpers Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); @@ -784,6 +817,9 @@ public: void set_allow_transform(bool p_allow_transform); bool is_allowing_transform() const; + // To duplicate a TileData object, needed for runtiume update. + TileData *duplicate(); + // Rendering void set_flip_h(bool p_flip_h); bool get_flip_h() const; @@ -831,6 +867,8 @@ public: int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; + TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed. + // Navigation void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon); Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 0dc83b4bb8..0aa79b8ceb 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -199,6 +199,10 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const { return Vector<StringName>(); } +Map<StringName, String> VisualShaderNode::get_editable_properties_names() const { + return Map<StringName, String>(); +} + Array VisualShaderNode::get_default_input_values() const { Array ret; for (const KeyValue<int, Variant> &E : default_input_values) { @@ -246,8 +250,8 @@ void VisualShaderNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values); ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded"); ADD_SIGNAL(MethodInfo("editor_refresh_request")); BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR); @@ -431,7 +435,7 @@ void VisualShaderNodeCustom::_bind_methods() { ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized); ClassDB::bind_method(D_METHOD("_set_input_port_default_value", "port", "value"), &VisualShaderNodeCustom::_set_input_port_default_value); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized"); } VisualShaderNodeCustom::VisualShaderNodeCustom() { @@ -1096,6 +1100,7 @@ static const char *type_string[VisualShader::TYPE_MAX] = { "start_custom", "process_custom", "sky", + "fog", }; bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { @@ -1245,7 +1250,7 @@ void VisualShader::reset_state() { } void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { //mode - p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky")); + p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); //render modes Map<String, String> blend_mode_enums; @@ -1290,20 +1295,20 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { prop_name += "/" + itos(E.key); if (E.key != NODE_ID_OUTPUT) { - p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } - p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) { - p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } } - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } } @@ -1636,7 +1641,7 @@ void VisualShader::_update_shader() const { Vector<VisualShader::DefaultTextureParam> default_tex_params; Set<StringName> classes; Map<int, int> insertion_pos; - static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky" }; + static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" }; global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n"; @@ -1684,7 +1689,7 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } - static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky" }; + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; Set<String> used_uniform_names; @@ -1755,7 +1760,7 @@ void VisualShader::_update_shader() const { StringBuilder func_code; bool is_empty_func = false; - if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY) { + if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) { is_empty_func = true; } @@ -1921,6 +1926,10 @@ void VisualShader::_update_shader() const { global_compute_code += " return mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n"; global_compute_code += "}\n\n"; + global_compute_code += "vec2 __get_random_unit_vec2(inout uint seed) {\n"; + global_compute_code += " return normalize(vec2(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n"; + global_compute_code += "}\n\n"; + global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n"; global_compute_code += " return normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n"; global_compute_code += "}\n\n"; @@ -2016,8 +2025,8 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_engine_version", "get_engine_version"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version"); ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs. @@ -2030,6 +2039,7 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_START_CUSTOM); BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM); BIND_ENUM_CONSTANT(TYPE_SKY); + BIND_ENUM_CONSTANT(TYPE_FOG); BIND_ENUM_CONSTANT(TYPE_MAX); BIND_CONSTANT(NODE_ID_INVALID); @@ -2307,6 +2317,16 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Fog, Fog + + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "world_position", "WORLD_POSITION" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "object_position", "OBJECT_POSITION" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "uvw", "UVW" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "extents", "EXTENTS" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "sdf", "SDF" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2373,11 +2393,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Sky - - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Particles { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, @@ -2386,6 +2401,15 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Sky + + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + // Fog + + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2834,7 +2858,7 @@ void VisualShaderNodeUniformRef::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_uniform_type"), &VisualShaderNodeUniformRef::_get_uniform_type); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type"); } Vector<StringName> VisualShaderNodeUniformRef::get_editable_properties() const { @@ -2933,6 +2957,13 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" }, //////////////////////////////////////////////////////////////////////// + // Fog, Fog. + //////////////////////////////////////////////////////////////////////// + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" }, + + //////////////////////////////////////////////////////////////////////// { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index f910f28536..7743affba7 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -57,6 +57,7 @@ public: TYPE_START_CUSTOM, TYPE_PROCESS_CUSTOM, TYPE_SKY, + TYPE_FOG, TYPE_MAX }; @@ -271,6 +272,7 @@ public: void set_disabled(bool p_disabled = true); virtual Vector<StringName> get_editable_properties() const; + virtual Map<StringName, String> get_editable_properties_names() const; virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 18b933e5cf..ada91cec1e 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -51,6 +51,38 @@ bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const return false; } +void VisualShaderNodeParticleEmitter::set_mode_2d(bool p_enabled) { + mode_2d = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeParticleEmitter::is_mode_2d() const { + return mode_2d; +} + +Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() const { + Vector<StringName> props; + props.push_back("mode_2d"); + return props; +} + +Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const { + Map<StringName, String> names; + names.insert("mode_2d", TTR("2D Mode")); + return names; +} + +bool VisualShaderNodeParticleEmitter::is_show_prop_names() const { + return true; +} + +void VisualShaderNodeParticleEmitter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mode_2d", "enabled"), &VisualShaderNodeParticleEmitter::set_mode_2d); + ClassDB::bind_method(D_METHOD("is_mode_2d"), &VisualShaderNodeParticleEmitter::is_mode_2d); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_2d"), "set_mode_2d", "is_mode_2d"); +} + VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() { } @@ -79,15 +111,27 @@ String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) co String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code; + + code += "vec2 __get_random_point_in_circle(inout uint seed, float radius, float inner_radius) {\n"; + code += " return __get_random_unit_vec2(seed) * __randf_range(seed, inner_radius, radius);\n"; + code += "}\n\n"; + code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n"; code += " return __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n"; code += "}\n\n"; + return code; } String VisualShaderNodeParticleSphereEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; + + if (mode_2d) { + code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n"; + } else { + code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; + } + return code; } @@ -122,16 +166,27 @@ String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code; - code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n"; + + code += "vec2 __get_random_point_in_box2d(inout uint seed, vec2 extents) {\n"; + code += " vec2 half_extents = extents / 2.0;\n"; + code += " return vec2(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y));\n"; + code += "}\n\n"; + + code += "vec3 __get_random_point_in_box3d(inout uint seed, vec3 extents) {\n"; code += " vec3 half_extents = extents / 2.0;\n"; code += " return vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n"; code += "}\n\n"; + return code; } String VisualShaderNodeParticleBoxEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code += " " + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n"; + if (mode_2d) { + code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ".xy), 0.0);\n"; + } else { + code += " " + p_output_vars[0] + " = __get_random_point_in_box3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n"; + } return code; } @@ -166,17 +221,31 @@ String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) cons String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code; - code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n"; - code += " float angle = __rand_from_seed(seed) * PI * 2.0;\n"; + + code += "vec2 __get_random_point_on_ring2d(inout uint seed, float radius, float inner_radius) {\n"; + code += " float angle = __rand_from_seed(seed) * TAU;\n"; + code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n"; + code += " return vec2(ring.x, ring.y);\n"; + code += "}\n\n"; + + code += "vec3 __get_random_point_on_ring3d(inout uint seed, float radius, float inner_radius, float height) {\n"; + code += " float angle = __rand_from_seed(seed) * TAU;\n"; code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n"; code += " return vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n"; code += "}\n\n"; + return code; } String VisualShaderNodeParticleRingEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code = " " + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n"; + + if (mode_2d) { + code = " " + p_output_vars[0] + " = vec3(__get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n"; + } else { + code = " " + p_output_vars[0] + " = __get_random_point_on_ring3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n"; + } + return code; } diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index b8bc7992cc..0b1fa277de 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -38,12 +38,23 @@ class VisualShaderNodeParticleEmitter : public VisualShaderNode { GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode); +protected: + bool mode_2d = false; + static void _bind_methods(); + public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; virtual bool has_output_port_preview(int p_port) const override; + void set_mode_2d(bool p_enabled); + bool is_mode_2d() const; + + Vector<StringName> get_editable_properties() const override; + Map<StringName, String> get_editable_properties_names() const override; + bool is_show_prop_names() const override; + VisualShaderNodeParticleEmitter(); }; diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index 42047f104f..0e1b343eac 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -144,9 +144,9 @@ World3D::World3D() { PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8)); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); - ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); - ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); navigation_map = NavigationServer3D::get_singleton()->map_create(); NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); |