diff options
Diffstat (limited to 'scene')
154 files changed, 5618 insertions, 1042 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_listener_2d.h b/scene/2d/audio_listener_2d.h index 875887acc6..454053bc4a 100644 --- a/scene/2d/audio_listener_2d.h +++ b/scene/2d/audio_listener_2d.h @@ -43,8 +43,6 @@ private: friend class Viewport; protected: - void _update_listener(); - 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; 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/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 271a4da705..00bfa62449 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -131,6 +131,7 @@ void CollisionPolygon2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { + ERR_FAIL_COND(!is_inside_tree()); if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) { break; } diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 54cb851216..c7742c7ba5 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -88,6 +88,8 @@ void CollisionShape2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { + ERR_FAIL_COND(!is_inside_tree()); + if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) { break; } 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/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 4990d443e3..391f51224e 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -201,7 +201,6 @@ public: void set_explosiveness_ratio(real_t p_ratio); void set_randomness_ratio(real_t p_ratio); void set_lifetime_randomness(double p_random); - void set_visibility_aabb(const Rect2 &p_aabb); void set_use_local_coordinates(bool p_enable); void set_speed_scale(double p_scale); @@ -213,7 +212,6 @@ public: real_t get_explosiveness_ratio() const; real_t get_randomness_ratio() const; double get_lifetime_randomness() const; - Rect2 get_visibility_aabb() const; bool get_use_local_coordinates() const; double get_speed_scale() const; @@ -226,9 +224,6 @@ public: void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; - void set_draw_passes(int p_count); - int get_draw_passes() const; - void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; @@ -264,7 +259,6 @@ public: void set_emission_points(const Vector<Vector2> &p_points); void set_emission_normals(const Vector<Vector2> &p_normals); void set_emission_colors(const Vector<Color> &p_colors); - void set_emission_point_count(int p_count); void set_scale_curve_x(Ref<Curve> p_scale_curve); void set_scale_curve_y(Ref<Curve> p_scale_curve); void set_split_scale(bool p_split_scale); @@ -275,7 +269,6 @@ public: Vector<Vector2> get_emission_points() const; Vector<Vector2> get_emission_normals() const; Vector<Color> get_emission_colors() const; - int get_emission_point_count() const; Ref<Curve> get_scale_curve_x() const; Ref<Curve> get_scale_curve_y() const; bool get_split_scale(); 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 3ac2128c2e..7af91f3a8d 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -157,6 +157,7 @@ void RayCast2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { + ERR_FAIL_COND(!is_inside_tree()); if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) { break; } @@ -191,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_listener_3d.h b/scene/3d/audio_listener_3d.h index 492cacb0e9..31de3b4fb1 100644 --- a/scene/3d/audio_listener_3d.h +++ b/scene/3d/audio_listener_3d.h @@ -63,9 +63,6 @@ public: virtual Transform3D get_listener_transform() const; - void set_visible_layers(uint32_t p_layers); - uint32_t get_visible_layers() const; - AudioListener3D(); ~AudioListener3D(); }; 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 48ef41e015..d347d24c2c 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -73,6 +73,7 @@ void CPUParticles3D::set_amount(int p_amount) { } particle_data.resize((12 + 4 + 4) * p_amount); + RS::get_singleton()->multimesh_set_visible_instances(multimesh, -1); RS::get_singleton()->multimesh_allocate_data(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_3D, true, true); particle_order.resize(p_amount); @@ -212,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\".")); } @@ -531,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) { @@ -1326,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/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 160814ead4..aca7328a27 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -209,7 +209,6 @@ public: void set_explosiveness_ratio(real_t p_ratio); void set_randomness_ratio(real_t p_ratio); void set_lifetime_randomness(double p_random); - void set_visibility_aabb(const AABB &p_aabb); void set_use_local_coordinates(bool p_enable); void set_speed_scale(double p_scale); @@ -221,7 +220,6 @@ public: real_t get_explosiveness_ratio() const; real_t get_randomness_ratio() const; double get_lifetime_randomness() const; - AABB get_visibility_aabb() const; bool get_use_local_coordinates() const; double get_speed_scale() const; @@ -234,9 +232,6 @@ public: void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; - void set_draw_passes(int p_count); - int get_draw_passes() const; - void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh() const; @@ -275,7 +270,6 @@ public: void set_emission_points(const Vector<Vector3> &p_points); void set_emission_normals(const Vector<Vector3> &p_normals); void set_emission_colors(const Vector<Color> &p_colors); - void set_emission_point_count(int p_count); void set_emission_ring_axis(Vector3 p_axis); void set_emission_ring_height(real_t p_height); void set_emission_ring_radius(real_t p_radius); @@ -291,7 +285,6 @@ public: Vector<Vector3> get_emission_points() const; Vector<Vector3> get_emission_normals() const; Vector<Color> get_emission_colors() const; - int get_emission_point_count() const; Vector3 get_emission_ring_axis() const; real_t get_emission_ring_height() const; real_t get_emission_ring_radius() const; 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 c96204cf60..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; } @@ -484,14 +531,14 @@ void Node3D::_update_gizmos() { #endif } -#ifdef TOOLS_ENABLED void Node3D::set_disable_gizmos(bool p_enabled) { +#ifdef TOOLS_ENABLED data.gizmos_disabled = p_enabled; if (!p_enabled) { clear_gizmos(); } -} #endif +} void Node3D::set_disable_scale(bool p_enabled) { data.disable_scale = p_enabled; @@ -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 fbc3767151..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 @@ -178,32 +178,32 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { } void Skeleton3D::_validate_property(PropertyInfo &property) const { - PackedStringArray spr = property.name.split("/"); - if (spr.size() == 3 && spr[0] == "bones") { - if (spr[2] == "rest") { + PackedStringArray split = property.name.split("/"); + if (split.size() == 3 && split[0] == "bones") { + if (split[2] == "rest") { property.usage |= PROPERTY_USAGE_READ_ONLY; } if (is_show_rest_only()) { - if (spr[2] == "enabled") { + if (split[2] == "enabled") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - if (spr[2] == "position") { + if (split[2] == "position") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - if (spr[2] == "rotation") { + if (split[2] == "rotation") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - if (spr[2] == "scale") { + if (split[2] == "scale") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - } else if (!is_bone_enabled(spr[1].to_int())) { - if (spr[2] == "position") { + } else if (!is_bone_enabled(split[1].to_int())) { + if (split[2] == "position") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - if (spr[2] == "rotation") { + if (split[2] == "rotation") { property.usage |= PROPERTY_USAGE_READ_ONLY; } - if (spr[2] == "scale") { + if (split[2] == "scale") { property.usage |= PROPERTY_USAGE_READ_ONLY; } } 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/sprite_3d.h b/scene/3d/sprite_3d.h index 90c2a309e1..61448c0e32 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -118,12 +118,6 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - void set_region_enabled(bool p_region); - bool is_region_enabled() const; - - void set_region_rect(const Rect2 &p_region_rect); - Rect2 get_region_rect() const; - void set_modulate(const Color &p_color); Color get_modulate() const; 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_player.h b/scene/animation/animation_player.h index 21e309efe0..d9d88b5510 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -301,7 +301,6 @@ public: void set_current_animation(const String &p_anim); String get_assigned_animation() const; void set_assigned_animation(const String &p_anim); - void stop_all(); void set_active(bool p_active); bool is_active() const; bool is_valid() const; 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/animation_tree.h b/scene/animation/animation_tree.h index 21b49937a7..5abea39d20 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -57,8 +57,6 @@ public: Vector<Input> inputs; - real_t process_input(int p_input, real_t p_time, bool p_seek, real_t p_blend); - friend class AnimationTree; struct AnimationState { @@ -85,7 +83,6 @@ public: State *state = nullptr; real_t _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections); - void _pre_update_animations(HashMap<NodePath, int> *track_map); //all this is temporary StringName base_path; @@ -110,8 +107,6 @@ protected: void _validate_property(PropertyInfo &property) const override; - void _set_parent(Object *p_parent); - GDVIRTUAL0RC(Dictionary, _get_child_nodes) GDVIRTUAL0RC(Array, _get_parameter_list) GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName) @@ -265,7 +260,6 @@ private: AnimationNode::State state; bool cache_valid = false; void _node_removed(Node *p_node); - void _caches_cleared(); void _clear_caches(); bool _update_caches(AnimationPlayer *player); 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/code_edit.cpp b/scene/gui/code_edit.cpp index c1db684d9b..046d256867 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -129,7 +129,7 @@ void CodeEdit::_notification(int p_what) { } const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0; - const int code_completion_base_width = font->get_string_size(code_completion_base).width; + const int code_completion_base_width = font->get_string_size(code_completion_base, font_size).width; if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; } else { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 973074397b..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"]); @@ -400,7 +400,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::INT, "theme_override_font_sizes/" + E, PROPERTY_HINT_NONE, "", usage)); + p_list->push_back(PropertyInfo(Variant::INT, "theme_override_font_sizes/" + E, PROPERTY_HINT_RANGE, "1,256,1,or_greater", usage)); } } { @@ -637,7 +637,9 @@ void Control::_notification(int p_notification) { } } else { //is a regular root control or top_level - data.RI = get_viewport()->_gui_add_root_control(this); + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + data.RI = viewport->_gui_add_root_control(this); } data.parent_canvas_item = get_parent_item(); @@ -646,7 +648,9 @@ void Control::_notification(int p_notification) { data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed)); } else { //connect viewport - get_viewport()->connect("size_changed", callable_mp(this, &Control::_size_changed)); + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->connect("size_changed", callable_mp(this, &Control::_size_changed)); } } break; case NOTIFICATION_EXIT_CANVAS: { @@ -655,7 +659,9 @@ void Control::_notification(int p_notification) { data.parent_canvas_item = nullptr; } else if (!is_set_as_top_level()) { //disconnect viewport - get_viewport()->disconnect("size_changed", callable_mp(this, &Control::_size_changed)); + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed)); } if (data.RI) { @@ -979,7 +985,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Ref<Font> *font = data.font_override.getptr(p_name); - if (font) { + if (font && (*font)->get_data_count() > 0) { return *font; } } @@ -992,7 +998,7 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const int *font_size = data.font_size_override.getptr(p_name); - if (font_size) { + if (font_size && (*font_size) > 0) { return *font_size; } } @@ -2580,16 +2586,6 @@ void Control::warp_mouse(const Point2 &p_to_pos) { } bool Control::is_text_field() const { - /* - if (get_script_instance()) { - Variant v=p_point; - const Variant *p[2]={&v,&p_data}; - Callable::CallError ce; - Variant ret = get_script_instance()->call("is_text_field",p,2,ce); - if (ce.error==Callable::CallError::CALL_OK) - return ret; - } - */ return false; } diff --git a/scene/gui/control.h b/scene/gui/control.h index b551a2e299..02ab336ef0 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -233,9 +233,6 @@ private: static constexpr unsigned properties_managed_by_container_count = 11; static String properties_managed_by_container[properties_managed_by_container_count]; - // used internally - Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform); - void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); Control *_get_focus_neighbor(Side p_side, int p_count = 0); @@ -250,7 +247,6 @@ private: void _update_minimum_size(); void _clear_size_warning(); - void _update_scroll(); void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]); void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]); 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/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 99c5e3bf0c..0b85b4d3d2 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -235,7 +235,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable()) { + if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes(); deselect(); @@ -290,7 +290,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { selection.double_click = true; last_dblclk = 0; caret_column = selection.begin; - if (!pass) { + if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(text); } } else if (b->is_double_click()) { @@ -308,7 +308,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { break; } } - if (!pass) { + if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin)); } } @@ -328,7 +328,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { update(); } else { - if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT) { + if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin)); } if (!text.is_empty() && is_editable() && clear_button_enabled) { @@ -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 cd7737e5fe..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; @@ -189,7 +190,6 @@ private: void _toggle_draw_caret(); void clear_internal(); - void changed_internal(); void _editor_settings_changed(); @@ -326,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.h b/scene/gui/popup.h index c7090e7231..8458a75eef 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -80,7 +80,6 @@ protected: virtual Size2 _get_contents_minimum_size() const override; public: - void set_child_rect(Control *p_child); PopupPanel(); }; 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 bc6552c208..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; } } @@ -1596,14 +1602,16 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_char = words[i + 1]; selection.active = true; - DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } update(); break; } } } } else if (!b->is_pressed()) { - if (selection.enabled) { + if (selection.enabled && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } selection.click_item = nullptr; @@ -2815,12 +2823,12 @@ bool RichTextLabel::is_scroll_following() const { return scroll_follow; } -Error RichTextLabel::parse_bbcode(const String &p_bbcode) { +void RichTextLabel::parse_bbcode(const String &p_bbcode) { clear(); - return append_text(p_bbcode); + append_text(p_bbcode); } -Error RichTextLabel::append_text(const String &p_bbcode) { +void RichTextLabel::append_text(const String &p_bbcode) { int pos = 0; List<String> tag_stack; @@ -3543,8 +3551,6 @@ Error RichTextLabel::append_text(const String &p_bbcode) { break; } } - - return OK; } void RichTextLabel::scroll_to_paragraph(int p_paragraph) { @@ -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 94f02a3989..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,9 +552,11 @@ 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; - Error parse_bbcode(const String &p_bbcode); - Error append_text(const String &p_bbcode); + void parse_bbcode(const String &p_bbcode); + void append_text(const String &p_bbcode); void set_use_bbcode(bool p_enable); bool is_using_bbcode() const; 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/tab_bar.h b/scene/gui/tab_bar.h index 1d84d6dbc8..411a62b1d9 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -83,7 +83,6 @@ private: Vector<Tab> tabs; int current = 0; int previous = 0; - int _get_top_margin() const; TabAlign tab_align = ALIGN_CENTER; bool clip_tabs = true; int rb_hover = -1; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a1d66d8544..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()) { @@ -1534,7 +1538,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { update(); } - if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MOUSE_BUTTON_MIDDLE) { + if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MOUSE_BUTTON_MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { paste_primary_clipboard(); } @@ -1575,7 +1579,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { dragging_selection = false; can_drag_minimap = false; click_select_held->stop(); - DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } } // Notify to show soft keyboard. @@ -3667,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; } @@ -4714,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); @@ -4873,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"); @@ -5167,7 +5187,7 @@ void TextEdit::_paste_internal() { } void TextEdit::_paste_primary_clipboard_internal() { - if (!is_editable()) { + if (!is_editable() || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { return; } @@ -5520,7 +5540,9 @@ void TextEdit::_update_selection_mode_word() { } } - DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } update(); @@ -5549,7 +5571,9 @@ void TextEdit::_update_selection_mode_line() { set_caret_column(0); select(selection.selecting_line, selection.selecting_column, line, col); - DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } update(); 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/gui/tree.cpp b/scene/gui/tree.cpp index 3f041bf65a..1245a37c4d 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3570,7 +3570,9 @@ int Tree::_get_title_button_height() const { void Tree::_notification(int p_what) { if (p_what == NOTIFICATION_FOCUS_ENTER) { - focus_in_id = get_viewport()->get_processed_events_count(); + if (get_viewport()) { + focus_in_id = get_viewport()->get_processed_events_count(); + } } if (p_what == NOTIFICATION_MOUSE_EXIT) { if (cache.hover_type != Cache::CLICK_NONE) { @@ -4035,7 +4037,7 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); + min_width = MAX(cache.font->get_string_size(columns[p_column].title, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 916833c9a7..5065684839 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -892,9 +892,9 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform, DEFVAL(0.0), DEFVAL(Size2(1.0, 1.0))); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index ba9f47119d..04376ad809 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -34,10 +34,10 @@ #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "scene/resources/canvas_item_material.h" +#include "scene/resources/font.h" #include "servers/text_server.h" class CanvasLayer; -class Font; class MultiMesh; class StyleBox; class Window; @@ -235,9 +235,9 @@ public: void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); void draw_set_transform_matrix(const Transform2D &p_matrix); 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/scene_tree.h b/scene/main/scene_tree.h index 19331c1906..7a4f9f9c52 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -136,7 +136,6 @@ private: void _flush_ugc(); _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); - void _update_listener(); Array _get_nodes_in_group(const StringName &p_group); @@ -265,9 +264,6 @@ public: void set_pause(bool p_enabled); bool is_paused() const; - void set_camera(const RID &p_camera); - RID get_camera() const; - #ifdef DEBUG_ENABLED void set_debug_collisions_hint(bool p_enabled); bool is_debugging_collisions_hint() const; 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 08b78a39b1..71d2774335 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -30,13 +30,40 @@ #include "animation.h" +#include "core/io/marshalls.h" #include "core/math/geometry_3d.h" #include "scene/scene_string_names.h" bool Animation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - if (name.begins_with("tracks/")) { + if (p_name == SNAME("_compression")) { + ERR_FAIL_COND_V(tracks.size() > 0, false); //can only set compression if no tracks exist + Dictionary comp = p_value; + ERR_FAIL_COND_V(!comp.has("fps"), false); + ERR_FAIL_COND_V(!comp.has("bounds"), false); + ERR_FAIL_COND_V(!comp.has("pages"), false); + ERR_FAIL_COND_V(!comp.has("format_version"), false); + uint32_t format_version = comp["format_version"]; + ERR_FAIL_COND_V(format_version > Compression::FORMAT_VERSION, false); // version does not match this supported version + compression.fps = comp["fps"]; + Array bounds = comp["bounds"]; + compression.bounds.resize(bounds.size()); + for (int i = 0; i < bounds.size(); i++) { + compression.bounds[i] = bounds[i]; + } + Array pages = comp["pages"]; + compression.pages.resize(pages.size()); + for (int i = 0; i < pages.size(); i++) { + Dictionary page = pages[i]; + ERR_FAIL_COND_V(!page.has("data"), false); + ERR_FAIL_COND_V(!page.has("time_offset"), false); + compression.pages[i].data = page["data"]; + compression.pages[i].time_offset = page["time_offset"]; + } + compression.enabled = true; + return true; + } else if (name.begins_with("tracks/")) { int track = name.get_slicec('/', 1).to_int(); String what = name.get_slicec('/', 2); @@ -72,6 +99,34 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { if (what == "path") { track_set_path(track, p_value); + } else if (what == "compressed_track") { + int index = p_value; + ERR_FAIL_COND_V(!compression.enabled, false); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)index, compression.bounds.size(), false); + Track *t = tracks[track]; + t->interpolation = INTERPOLATION_LINEAR; //only linear supported + switch (t->type) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + tt->compressed_track = index; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + rt->compressed_track = index; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + st->compressed_track = index; + } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + bst->compressed_track = index; + } break; + default: { + return false; + } + } + return true; } else if (what == "interp") { track_set_interpolation_type(track, InterpolationType(p_value.operator int())); } else if (what == "loop_wrap") { @@ -369,7 +424,30 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { bool Animation::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name; - if (name == "length") { + if (p_name == SNAME("_compression")) { + ERR_FAIL_COND_V(!compression.enabled, false); + Dictionary comp; + comp["fps"] = compression.fps; + Array bounds; + bounds.resize(compression.bounds.size()); + for (uint32_t i = 0; i < compression.bounds.size(); i++) { + bounds[i] = compression.bounds[i]; + } + comp["bounds"] = bounds; + Array pages; + pages.resize(compression.pages.size()); + for (uint32_t i = 0; i < compression.pages.size(); i++) { + Dictionary page; + page["data"] = compression.pages[i].data; + page["time_offset"] = compression.pages[i].time_offset; + pages[i] = page; + } + comp["pages"] = pages; + comp["format_version"] = Compression::FORMAT_VERSION; + + r_ret = comp; + return true; + } else if (name == "length") { r_ret = length; } else if (name == "loop") { r_ret = loop; @@ -414,6 +492,34 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "path") { r_ret = track_get_path(track); + } else if (what == "compressed_track") { + ERR_FAIL_COND_V(!compression.enabled, false); + Track *t = tracks[track]; + switch (t->type) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + r_ret = tt->compressed_track; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + r_ret = rt->compressed_track; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + r_ret = st->compressed_track; + } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + r_ret = bst->compressed_track; + } break; + default: { + r_ret = Variant(); + ERR_FAIL_V(false); + } + } + + return true; + } else if (what == "interp") { r_ret = track_get_interpolation_type(track); } else if (what == "loop_wrap") { @@ -692,14 +798,21 @@ 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_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::NODE_PATH, "tracks/" + itos(i) + "/path", 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_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::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::ARRAY, "tracks/" + itos(i) + "/keys", 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_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + } else { + 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)); + } } } @@ -765,21 +878,25 @@ void Animation::remove_track(int p_track) { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND_MSG(tt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first."); _clear(tt->positions); } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND_MSG(rt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first."); _clear(rt->rotations); } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND_MSG(st->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first."); _clear(st->scales); } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND_MSG(bst->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first."); _clear(bst->blend_shapes); } break; @@ -837,9 +954,9 @@ NodePath Animation::track_get_path(int p_track) const { return tracks[p_track]->path; } -int Animation::find_track(const NodePath &p_path) const { +int Animation::find_track(const NodePath &p_path, const TrackType p_type) const { for (int i = 0; i < tracks.size(); i++) { - if (tracks[i]->path == p_path) { + if (tracks[i]->path == p_path && tracks[i]->type == p_type) { return i; } }; @@ -907,6 +1024,8 @@ int Animation::position_track_insert_key(int p_track, double p_time, const Vecto PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND_V(tt->compressed_track >= 0, -1); + TKey<Vector3> tkey; tkey.time = p_time; tkey.value = p_position; @@ -922,6 +1041,19 @@ Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_posit PositionTrack *tt = static_cast<PositionTrack *>(t); ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + + if (tt->compressed_track >= 0) { + Vector3i key; + double time; + bool fetch_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key, key, time); + if (!fetch_success) { + return ERR_INVALID_PARAMETER; + } + + *r_position = _uncompress_pos_scale(tt->compressed_track, key); + return OK; + } + ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER); *r_position = tt->positions[p_key].value; @@ -936,6 +1068,14 @@ Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 PositionTrack *tt = static_cast<PositionTrack *>(t); + if (tt->compressed_track >= 0) { + if (_pos_scale_interpolate_compressed(tt->compressed_track, p_time, *r_interpolation)) { + return OK; + } else { + return ERR_UNAVAILABLE; + } + } + bool ok = false; Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok); @@ -956,6 +1096,8 @@ int Animation::rotation_track_insert_key(int p_track, double p_time, const Quate RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND_V(rt->compressed_track >= 0, -1); + TKey<Quaternion> tkey; tkey.time = p_time; tkey.value = p_rotation; @@ -971,6 +1113,19 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro RotationTrack *rt = static_cast<RotationTrack *>(t); ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + + if (rt->compressed_track >= 0) { + Vector3i key; + double time; + bool fetch_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key, key, time); + if (!fetch_success) { + return ERR_INVALID_PARAMETER; + } + + *r_rotation = _uncompress_quaternion(key); + return OK; + } + ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER); *r_rotation = rt->rotations[p_key].value; @@ -985,6 +1140,14 @@ Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaterni RotationTrack *rt = static_cast<RotationTrack *>(t); + if (rt->compressed_track >= 0) { + if (_rotation_interpolate_compressed(rt->compressed_track, p_time, *r_interpolation)) { + return OK; + } else { + return ERR_UNAVAILABLE; + } + } + bool ok = false; Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok); @@ -1005,6 +1168,8 @@ int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND_V(st->compressed_track >= 0, -1); + TKey<Vector3> tkey; tkey.time = p_time; tkey.value = p_scale; @@ -1020,6 +1185,19 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c ScaleTrack *st = static_cast<ScaleTrack *>(t); ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + + if (st->compressed_track >= 0) { + Vector3i key; + double time; + bool fetch_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key, key, time); + if (!fetch_success) { + return ERR_INVALID_PARAMETER; + } + + *r_scale = _uncompress_pos_scale(st->compressed_track, key); + return OK; + } + ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER); *r_scale = st->scales[p_key].value; @@ -1034,6 +1212,14 @@ Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_ ScaleTrack *st = static_cast<ScaleTrack *>(t); + if (st->compressed_track >= 0) { + if (_pos_scale_interpolate_compressed(st->compressed_track, p_time, *r_interpolation)) { + return OK; + } else { + return ERR_UNAVAILABLE; + } + } + bool ok = false; Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok); @@ -1052,6 +1238,8 @@ int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND_V(st->compressed_track >= 0, -1); + TKey<float> tkey; tkey.time = p_time; tkey.value = p_blend_shape; @@ -1065,11 +1253,24 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); Track *t = tracks[p_track]; - BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_key, st->blend_shapes.size(), ERR_INVALID_PARAMETER); - *r_blend_shape = st->blend_shapes[p_key].value; + if (bst->compressed_track >= 0) { + Vector3i key; + double time; + bool fetch_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key, key, time); + if (!fetch_success) { + return ERR_INVALID_PARAMETER; + } + + *r_blend_shape = _uncompress_blend_shape(key); + return OK; + } + + ERR_FAIL_INDEX_V(p_key, bst->blend_shapes.size(), ERR_INVALID_PARAMETER); + + *r_blend_shape = bst->blend_shapes[p_key].value; return OK; } @@ -1079,11 +1280,19 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float Track *t = tracks[p_track]; ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER); - BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + + if (bst->compressed_track >= 0) { + if (_blend_shape_interpolate_compressed(bst->compressed_track, p_time, *r_interpolation)) { + return OK; + } else { + return ERR_UNAVAILABLE; + } + } bool ok = false; - float tk = _interpolate(st->blend_shapes, p_time, st->interpolation, st->loop_wrap, &ok); + float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok); if (!ok) { return ERR_UNAVAILABLE; @@ -1105,24 +1314,36 @@ void Animation::track_remove_key(int p_track, int p_idx) { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + + ERR_FAIL_COND(tt->compressed_track >= 0); + ERR_FAIL_INDEX(p_idx, tt->positions.size()); tt->positions.remove(p_idx); } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + + ERR_FAIL_COND(rt->compressed_track >= 0); + ERR_FAIL_INDEX(p_idx, rt->rotations.size()); rt->rotations.remove(p_idx); } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + + ERR_FAIL_COND(st->compressed_track >= 0); + ERR_FAIL_INDEX(p_idx, st->scales.size()); st->scales.remove(p_idx); } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + + ERR_FAIL_COND(bst->compressed_track >= 0); + ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size()); bst->blend_shapes.remove(p_idx); @@ -1169,6 +1390,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + + if (tt->compressed_track >= 0) { + double time; + double time_next; + Vector3i key; + Vector3i key_next; + uint32_t key_index; + bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index); + ERR_FAIL_COND_V(!fetch_compressed_success, -1); + if (p_exact && time != p_time) { + return -1; + } + return key_index; + } + int k = _find(tt->positions, p_time); if (k < 0 || k >= tt->positions.size()) { return -1; @@ -1181,6 +1417,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + + if (rt->compressed_track >= 0) { + double time; + double time_next; + Vector3i key; + Vector3i key_next; + uint32_t key_index; + bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index); + ERR_FAIL_COND_V(!fetch_compressed_success, -1); + if (p_exact && time != p_time) { + return -1; + } + return key_index; + } + int k = _find(rt->rotations, p_time); if (k < 0 || k >= rt->rotations.size()) { return -1; @@ -1193,6 +1444,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + + if (st->compressed_track >= 0) { + double time; + double time_next; + Vector3i key; + Vector3i key_next; + uint32_t key_index; + bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index); + ERR_FAIL_COND_V(!fetch_compressed_success, -1); + if (p_exact && time != p_time) { + return -1; + } + return key_index; + } + int k = _find(st->scales, p_time); if (k < 0 || k >= st->scales.size()) { return -1; @@ -1205,6 +1471,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + + if (bst->compressed_track >= 0) { + double time; + double time_next; + Vector3i key; + Vector3i key_next; + uint32_t key_index; + bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index); + ERR_FAIL_COND_V(!fetch_compressed_success, -1); + if (p_exact && time != p_time) { + return -1; + } + return key_index; + } + int k = _find(bst->blend_shapes, p_time); if (k < 0 || k >= bst->blend_shapes.size()) { return -1; @@ -1392,18 +1673,30 @@ int Animation::track_get_key_count(int p_track) const { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + if (tt->compressed_track >= 0) { + return _get_compressed_key_count(tt->compressed_track); + } return tt->positions.size(); } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + if (rt->compressed_track >= 0) { + return _get_compressed_key_count(rt->compressed_track); + } return rt->rotations.size(); } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + if (st->compressed_track >= 0) { + return _get_compressed_key_count(st->compressed_track); + } return st->scales.size(); } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + if (bst->compressed_track >= 0) { + return _get_compressed_key_count(bst->compressed_track); + } return bst->blend_shapes.size(); } break; case TYPE_VALUE: { @@ -1438,28 +1731,24 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { switch (t->type) { case TYPE_POSITION_3D: { - PositionTrack *tt = static_cast<PositionTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant()); - - return tt->positions[p_key_idx].value; + Vector3 value; + position_track_get_key(p_track, p_key_idx, &value); + return value; } break; case TYPE_ROTATION_3D: { - RotationTrack *rt = static_cast<RotationTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant()); - - return rt->rotations[p_key_idx].value; + Quaternion value; + rotation_track_get_key(p_track, p_key_idx, &value); + return value; } break; case TYPE_SCALE_3D: { - ScaleTrack *st = static_cast<ScaleTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant()); - - return st->scales[p_key_idx].value; + Vector3 value; + scale_track_get_key(p_track, p_key_idx, &value); + return value; } break; case TYPE_BLEND_SHAPE: { - BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), Variant()); - - return bst->blend_shapes[p_key_idx].value; + float value; + blend_shape_track_get_key(p_track, p_key_idx, &value); + return value; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1520,21 +1809,49 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + if (tt->compressed_track >= 0) { + Vector3i value; + double time; + bool fetch_compressed_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key_idx, value, time); + ERR_FAIL_COND_V(!fetch_compressed_success, false); + return time; + } ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); return tt->positions[p_key_idx].time; } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + if (rt->compressed_track >= 0) { + Vector3i value; + double time; + bool fetch_compressed_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key_idx, value, time); + ERR_FAIL_COND_V(!fetch_compressed_success, false); + return time; + } ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); return rt->rotations[p_key_idx].time; } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + if (st->compressed_track >= 0) { + Vector3i value; + double time; + bool fetch_compressed_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key_idx, value, time); + ERR_FAIL_COND_V(!fetch_compressed_success, false); + return time; + } ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); return st->scales[p_key_idx].time; } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + if (bst->compressed_track >= 0) { + Vector3i value; + double time; + bool fetch_compressed_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key_idx, value, time); + ERR_FAIL_COND_V(!fetch_compressed_success, false); + return time; + } ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1); return bst->blend_shapes[p_key_idx].time; } break; @@ -1580,6 +1897,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); TKey<Vector3> key = tt->positions[p_key_idx]; key.time = p_time; @@ -1589,6 +1907,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { } case TYPE_ROTATION_3D: { RotationTrack *tt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->rotations.size()); TKey<Quaternion> key = tt->rotations[p_key_idx]; key.time = p_time; @@ -1598,6 +1917,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { } case TYPE_SCALE_3D: { ScaleTrack *tt = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->scales.size()); TKey<Vector3> key = tt->scales[p_key_idx]; key.time = p_time; @@ -1607,6 +1927,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { } case TYPE_BLEND_SHAPE: { BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size()); TKey<float> key = tt->blend_shapes[p_key_idx]; key.time = p_time; @@ -1671,21 +1992,33 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + if (tt->compressed_track >= 0) { + return 1.0; + } ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); return tt->positions[p_key_idx].transition; } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + if (rt->compressed_track >= 0) { + return 1.0; + } ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); return rt->rotations[p_key_idx].transition; } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + if (st->compressed_track >= 0) { + return 1.0; + } ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); return st->scales[p_key_idx].transition; } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + if (bst->compressed_track >= 0) { + return 1.0; + } ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1); return bst->blend_shapes[p_key_idx].transition; } break; @@ -1715,6 +2048,35 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { ERR_FAIL_V(0); } +bool Animation::track_is_compressed(int p_track) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), false); + Track *t = tracks[p_track]; + + switch (t->type) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + return tt->compressed_track >= 0; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + return rt->compressed_track >= 0; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + return st->compressed_track >= 0; + } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + return bst->compressed_track >= 0; + } break; + default: { + return false; //animation does not really use transitions + } break; + } + + ERR_FAIL_V(false); +} + void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p_value) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; @@ -1723,6 +2085,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p case TYPE_POSITION_3D: { ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); tt->positions.write[p_key_idx].value = p_value; @@ -1731,6 +2094,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p case TYPE_ROTATION_3D: { ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS)); RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND(rt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); rt->rotations.write[p_key_idx].value = p_value; @@ -1739,6 +2103,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p case TYPE_SCALE_3D: { ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND(st->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, st->scales.size()); st->scales.write[p_key_idx].value = p_value; @@ -1747,6 +2112,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p case TYPE_BLEND_SHAPE: { ERR_FAIL_COND((p_value.get_type() != Variant::FLOAT) && (p_value.get_type() != Variant::INT)); BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND(bst->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size()); bst->blend_shapes.write[p_key_idx].value = p_value; @@ -1820,21 +2186,25 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr switch (t->type) { case TYPE_POSITION_3D: { PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND(tt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); tt->positions.write[p_key_idx].transition = p_transition; } break; case TYPE_ROTATION_3D: { RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND(rt->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); rt->rotations.write[p_key_idx].transition = p_transition; } break; case TYPE_SCALE_3D: { ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND(st->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, st->scales.size()); st->scales.write[p_key_idx].transition = p_transition; } break; case TYPE_BLEND_SHAPE: { BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND(bst->compressed_track >= 0); ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size()); bst->blend_shapes.write[p_key_idx].transition = p_transition; } break; @@ -1949,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 @@ -2334,26 +2705,50 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl switch (t->type) { case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast<const PositionTrack *>(t); - _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + if (tt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); + + } else { + _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); + _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast<const RotationTrack *>(t); - _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); - _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + if (rt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); + + } else { + _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast<const ScaleTrack *>(t); - _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); - _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); + if (st->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); + + } else { + _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); + _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); + } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); - _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); - _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices); + if (bst->compressed_track >= 0) { + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); + + } else { + _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); + _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices); + } } break; case TYPE_VALUE: { @@ -2408,22 +2803,38 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl switch (t->type) { case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast<const PositionTrack *>(t); - _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + if (tt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices); + } else { + _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast<const RotationTrack *>(t); - _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + if (rt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices); + } else { + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast<const ScaleTrack *>(t); - _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); + if (st->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices); + } else { + _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); + } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); - _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices); + if (bst->compressed_track >= 0) { + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices); + } else { + _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices); + } } break; case TYPE_VALUE: { @@ -3027,7 +3438,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type); ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path); ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path); - ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track); + ClassDB::bind_method(D_METHOD("find_track", "path", "type"), &Animation::find_track); ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up); ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down); @@ -3064,6 +3475,8 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap); + ClassDB::bind_method(D_METHOD("track_is_compressed", "track_idx"), &Animation::track_is_compressed); + ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); @@ -3110,6 +3523,8 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &Animation::clear); ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track); + ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0)); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step"); @@ -3143,6 +3558,10 @@ void Animation::clear() { tracks.clear(); loop = false; length = 1; + compression.enabled = false; + compression.bounds.clear(); + compression.pages.clear(); + compression.fps = 120; emit_changed(); emit_signal(SceneStringNames::get_singleton()->tracks_changed); } @@ -3447,6 +3866,9 @@ void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_e void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { for (int i = 0; i < tracks.size(); i++) { + if (track_is_compressed(i)) { + continue; //not possible to optimize compressed track + } if (tracks[i]->type == TYPE_POSITION_3D) { _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err); } else if (tracks[i]->type == TYPE_ROTATION_3D) { @@ -3459,6 +3881,1154 @@ void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_e } } +#define print_animc(m_str) +//#define print_animc(m_str) print_line(m_str); + +struct AnimationCompressionDataState { + enum { + MIN_OPTIMIZE_PACKETS = 5, + MAX_PACKETS = 16 + }; + + uint32_t components = 3; + LocalVector<uint8_t> data; //commited packets + struct PacketData { + int32_t data[3] = { 0, 0, 0 }; + uint32_t frame = 0; + }; + + float split_tolerance = 1.5; + + LocalVector<PacketData> temp_packets; + + //used for rollback if the new frame does not fit + int32_t validated_packet_count = -1; + + static int32_t _compute_delta16_signed(int32_t p_from, int32_t p_to) { + int32_t delta = p_to - p_from; + if (delta > 32767) { + return delta - 65536; // use wrap around + } else if (delta < -32768) { + return 65536 + delta; // use wrap around + } + return delta; + } + + static uint32_t _compute_shift_bits_signed(int32_t p_delta) { + if (p_delta == 0) { + return 0; + } else if (p_delta < 0) { + p_delta = ABS(p_delta) - 1; + if (p_delta == 0) { + return 1; + } + } + return nearest_shift(p_delta); + } + + void _compute_max_shifts(uint32_t p_from, uint32_t p_to, uint32_t *max_shifts, uint32_t &max_frame_delta_shift) const { + for (uint32_t j = 0; j < components; j++) { + max_shifts[j] = 0; + } + max_frame_delta_shift = 0; + + for (uint32_t i = p_from + 1; i <= p_to; i++) { + int32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame; + max_frame_delta_shift = MAX(max_frame_delta_shift, nearest_shift(frame_delta)); + for (uint32_t j = 0; j < components; j++) { + int32_t diff = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]); + uint32_t shift = _compute_shift_bits_signed(diff); + max_shifts[j] = MAX(shift, max_shifts[j]); + } + } + } + + bool insert_key(uint32_t p_frame, const Vector3i &p_key) { + if (temp_packets.size() == MAX_PACKETS) { + commit_temp_packets(); + } + PacketData packet; + packet.frame = p_frame; + for (int i = 0; i < 3; i++) { + ERR_FAIL_COND_V(p_key[i] > 65535, false); // Sanity check + packet.data[i] = p_key[i]; + } + + temp_packets.push_back(packet); + + if (temp_packets.size() >= MIN_OPTIMIZE_PACKETS) { + uint32_t max_shifts[3] = { 0, 0, 0 }; // Base sizes, 16 bit + uint32_t max_frame_delta_shift = 0; + // Compute the average shift before the packet was added + _compute_max_shifts(0, temp_packets.size() - 2, max_shifts, max_frame_delta_shift); + + float prev_packet_size_avg = 0; + prev_packet_size_avg = float(1 << max_frame_delta_shift); + for (uint32_t i = 0; i < components; i++) { + prev_packet_size_avg += float(1 << max_shifts[i]); + } + prev_packet_size_avg /= float(1 + components); + + _compute_max_shifts(temp_packets.size() - 2, temp_packets.size() - 1, max_shifts, max_frame_delta_shift); + + float new_packet_size_avg = 0; + new_packet_size_avg = float(1 << max_frame_delta_shift); + for (uint32_t i = 0; i < components; i++) { + new_packet_size_avg += float(1 << max_shifts[i]); + } + new_packet_size_avg /= float(1 + components); + + print_animc("packet count: " + rtos(temp_packets.size() - 1) + " size avg " + rtos(prev_packet_size_avg) + " new avg " + rtos(new_packet_size_avg)); + float ratio = (prev_packet_size_avg < new_packet_size_avg) ? (new_packet_size_avg / prev_packet_size_avg) : (prev_packet_size_avg / new_packet_size_avg); + + if (ratio > split_tolerance) { + print_animc("split!"); + temp_packets.resize(temp_packets.size() - 1); + commit_temp_packets(); + temp_packets.push_back(packet); + } + } + + return temp_packets.size() == 1; // First key + } + + uint32_t get_temp_packet_size() const { + if (temp_packets.size() == 0) { + return 0; + } else if (temp_packets.size() == 1) { + return components == 1 ? 4 : 8; // 1 component packet is 16 bits and 16 bits unused. 3 component packets is 48 bits and 16 bits unused + } + uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit + uint32_t max_frame_delta_shift = 0; + + _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift); + + uint32_t size_bits = 16; //base value (all 4 bits of shift sizes for x,y,z,time) + size_bits += max_frame_delta_shift * (temp_packets.size() - 1); //times + for (uint32_t j = 0; j < components; j++) { + size_bits += 16; //base value + uint32_t shift = max_shifts[j]; + if (shift > 0) { + shift += 1; //if not zero, add sign bit + } + size_bits += shift * (temp_packets.size() - 1); + } + if (size_bits % 8 != 0) { //wrap to 8 bits + size_bits += 8 - (size_bits % 8); + } + uint32_t size_bytes = size_bits / 8; //wrap to words + if (size_bytes % 4 != 0) { + size_bytes += 4 - (size_bytes % 4); + } + return size_bytes; + } + + static void _push_bits(LocalVector<uint8_t> &data, uint32_t &r_buffer, uint32_t &r_bits_used, uint32_t p_value, uint32_t p_bits) { + r_buffer |= p_value << r_bits_used; + r_bits_used += p_bits; + while (r_bits_used >= 8) { + uint8_t byte = r_buffer & 0xFF; + data.push_back(byte); + r_buffer >>= 8; + r_bits_used -= 8; + } + } + + void commit_temp_packets() { + if (temp_packets.size() == 0) { + return; //nohing to do + } +#define DEBUG_PACKET_PUSH +#ifdef DEBUG_PACKET_PUSH +#ifndef _MSC_VER +#warning Debugging packet push, disable this code in production to gain a bit more import performance. +#endif + uint32_t debug_packet_push = get_temp_packet_size(); + uint32_t debug_data_size = data.size(); +#endif + // Store header + + uint8_t header[8]; + uint32_t header_bytes = 0; + for (uint32_t i = 0; i < components; i++) { + encode_uint16(temp_packets[0].data[i], &header[header_bytes]); + header_bytes += 2; + } + + uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit + uint32_t max_frame_delta_shift = 0; + + if (temp_packets.size() > 1) { + _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift); + uint16_t shift_header = (max_frame_delta_shift - 1) << 12; + for (uint32_t i = 0; i < components; i++) { + shift_header |= max_shifts[i] << (4 * i); + } + + encode_uint16(shift_header, &header[header_bytes]); + header_bytes += 2; + } + + while (header_bytes % 4 != 0) { + header[header_bytes++] = 0; + } + + for (uint32_t i = 0; i < header_bytes; i++) { + data.push_back(header[i]); + } + + if (temp_packets.size() == 1) { + temp_packets.clear(); + validated_packet_count = 0; + return; //only header stored, nothing else to do + } + + uint32_t bit_buffer = 0; + uint32_t bits_used = 0; + + for (uint32_t i = 1; i < temp_packets.size(); i++) { + uint32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame; + _push_bits(data, bit_buffer, bits_used, frame_delta, max_frame_delta_shift); + + for (uint32_t j = 0; j < components; j++) { + if (max_shifts[j] == 0) { + continue; // Zero delta, do not store + } + int32_t delta = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]); + + ERR_FAIL_COND(delta < -32768 || delta > 32767); //sanity check + + uint16_t deltau; + if (delta < 0) { + deltau = (ABS(delta) - 1) | (1 << max_shifts[j]); + } else { + deltau = delta; + } + _push_bits(data, bit_buffer, bits_used, deltau, max_shifts[j] + 1); // Include sign bit + } + } + if (bits_used != 0) { + ERR_FAIL_COND(bit_buffer > 0xFF); // Sanity check + data.push_back(bit_buffer); + } + + while (data.size() % 4 != 0) { + data.push_back(0); //pad to align with 4 + } + + temp_packets.clear(); + validated_packet_count = 0; + +#ifdef DEBUG_PACKET_PUSH + ERR_FAIL_COND((data.size() - debug_data_size) != debug_packet_push); +#endif + } +}; + +struct AnimationCompressionTimeState { + struct Packet { + uint32_t frame; + uint32_t offset; + uint32_t count; + }; + + LocalVector<Packet> packets; + //used for rollback + int32_t key_index = 0; + int32_t validated_packet_count = 0; + int32_t validated_key_index = -1; + bool needs_start_frame = false; +}; + +Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key, float p_time) { + Vector3i values; + TrackType tt = track_get_type(p_track); + switch (tt) { + case TYPE_POSITION_3D: { + Vector3 pos; + if (p_key >= 0) { + position_track_get_key(p_track, p_key, &pos); + } else { + position_track_interpolate(p_track, p_time, &pos); + } + pos = (pos - p_bounds.position) / p_bounds.size; + for (int j = 0; j < 3; j++) { + values[j] = CLAMP(int32_t(pos[j] * 65535.0), 0, 65535); + } + } break; + case TYPE_ROTATION_3D: { + Quaternion rot; + if (p_key >= 0) { + rotation_track_get_key(p_track, p_key, &rot); + } else { + rotation_track_interpolate(p_track, p_time, &rot); + } + Vector3 axis = rot.get_axis(); + float angle = rot.get_angle(); + angle = Math::fposmod(double(angle), double(Math_PI * 2.0)); + Vector2 oct = axis.octahedron_encode(); + Vector3 rot_norm(oct.x, oct.y, angle / (Math_PI * 2.0)); // high resolution rotation in 0-1 angle. + + for (int j = 0; j < 3; j++) { + values[j] = CLAMP(int32_t(rot_norm[j] * 65535.0), 0, 65535); + } + } break; + case TYPE_SCALE_3D: { + Vector3 scale; + if (p_key >= 0) { + scale_track_get_key(p_track, p_key, &scale); + } else { + scale_track_interpolate(p_track, p_time, &scale); + } + scale = (scale - p_bounds.position) / p_bounds.size; + for (int j = 0; j < 3; j++) { + values[j] = CLAMP(int32_t(scale[j] * 65535.0), 0, 65535); + } + } break; + case TYPE_BLEND_SHAPE: { + float blend; + if (p_key >= 0) { + blend_shape_track_get_key(p_track, p_key, &blend); + } else { + blend_shape_track_interpolate(p_track, p_time, &blend); + } + + blend = (blend / float(Compression::BLEND_SHAPE_RANGE)) * 0.5 + 0.5; + values[0] = CLAMP(int32_t(blend * 65535.0), 0, 65535); + } break; + default: { + ERR_FAIL_V(Vector3i()); //sanity check + } break; + } + + return values; +} + +struct AnimationCompressionBufferBitsRead { + uint32_t buffer = 0; + uint32_t used = 0; + const uint8_t *src_data = nullptr; + + _FORCE_INLINE_ uint32_t read(uint32_t p_bits) { + uint32_t output = 0; + uint32_t written = 0; + while (p_bits > 0) { + if (used == 0) { + used = 8; + buffer = *src_data; + src_data++; + } + uint32_t to_write = MIN(used, p_bits); + output |= (buffer & ((1 << to_write) - 1)) << written; + buffer >>= to_write; + used -= to_write; + p_bits -= to_write; + written += to_write; + } + return output; + } +}; + +void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tolerance) { + ERR_FAIL_COND_MSG(compression.enabled, "This animation is already compressed"); + + p_split_tolerance = CLAMP(p_split_tolerance, 1.1, 8.0); + compression.pages.clear(); + + uint32_t base_page_size = 0; // Before compressing pages, compute how large the "end page" datablock is. + LocalVector<uint32_t> tracks_to_compress; + LocalVector<AABB> track_bounds; + const uint32_t time_packet_size = 4; + + const uint32_t track_header_size = 4 + 4 + 4; // pointer to time (4 bytes), amount of time keys (4 bytes) pointer to track data (4 bytes) + + for (int i = 0; i < get_track_count(); i++) { + TrackType type = track_get_type(i); + if (type != TYPE_POSITION_3D && type != TYPE_ROTATION_3D && type != TYPE_SCALE_3D && type != TYPE_BLEND_SHAPE) { + continue; + } + if (track_get_key_count(i) == 0) { + continue; //do not compress, no keys + } + base_page_size += track_header_size; //pointer to beginning of each track timeline and amount of time keys + base_page_size += time_packet_size; //for end of track time marker + base_page_size += (type == TYPE_BLEND_SHAPE) ? 4 : 8; // at least the end of track packet (at much 8 bytes). This could be less, but have to be pessimistic. + tracks_to_compress.push_back(i); + + AABB bounds; + + if (type == TYPE_POSITION_3D) { + AABB aabb; + int kcount = track_get_key_count(i); + for (int j = 0; j < kcount; j++) { + Vector3 pos; + position_track_get_key(i, j, &pos); + if (j == 0) { + aabb.position = pos; + } else { + aabb.expand_to(pos); + } + } + for (int j = 0; j < 3; j++) { + //cant have zero + if (aabb.size[j] < CMP_EPSILON) { + aabb.size[j] = CMP_EPSILON; + } + } + bounds = aabb; + } + if (type == TYPE_SCALE_3D) { + AABB aabb; + int kcount = track_get_key_count(i); + for (int j = 0; j < kcount; j++) { + Vector3 scale; + scale_track_get_key(i, j, &scale); + if (j == 0) { + aabb.position = scale; + } else { + aabb.expand_to(scale); + } + } + for (int j = 0; j < 3; j++) { + //cant have zero + if (aabb.size[j] < CMP_EPSILON) { + aabb.size[j] = CMP_EPSILON; + } + } + bounds = aabb; + } + + track_bounds.push_back(bounds); + } + + if (tracks_to_compress.size() == 0) { + return; //nothing to compress + } + + print_animc("Anim Compression:"); + print_animc("-----------------"); + print_animc("Tracks to compress: " + itos(tracks_to_compress.size())); + + uint32_t current_frame = 0; + uint32_t base_page_frame = 0; + double frame_len = 1.0 / double(p_fps); + const uint32_t max_frames_per_page = 65536; + + print_animc("Frame Len: " + rtos(frame_len)); + + LocalVector<AnimationCompressionDataState> data_tracks; + LocalVector<AnimationCompressionTimeState> time_tracks; + + data_tracks.resize(tracks_to_compress.size()); + time_tracks.resize(tracks_to_compress.size()); + + for (uint32_t i = 0; i < data_tracks.size(); i++) { + data_tracks[i].split_tolerance = p_split_tolerance; + if (track_get_type(tracks_to_compress[i]) == TYPE_BLEND_SHAPE) { + data_tracks[i].components = 1; + } else { + data_tracks[i].components = 3; + } + } + + while (true) { + // Begin by finding the keyframe in all tracks with the time closest to the current time + const uint32_t FRAME_MAX = 0xFFFFFFFF; + const int32_t NO_TRACK_FOUND = -1; + uint32_t best_frame = FRAME_MAX; + uint32_t best_invalid_frame = FRAME_MAX; + int32_t best_frame_track = NO_TRACK_FOUND; // Default is -1, which means all keyframes for this page are exhausted. + bool start_frame = false; + + for (uint32_t i = 0; i < tracks_to_compress.size(); i++) { + uint32_t uncomp_track = tracks_to_compress[i]; + + if (time_tracks[i].key_index == track_get_key_count(uncomp_track)) { + if (time_tracks[i].needs_start_frame) { + start_frame = true; + best_frame = base_page_frame; + best_frame_track = i; + time_tracks[i].needs_start_frame = false; + break; + } else { + continue; // This track is exhausted (all keys were added already), don't consider. + } + } + + uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len; + + if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) { + start_frame = true; + best_frame = base_page_frame; + best_frame_track = i; + time_tracks[i].needs_start_frame = false; + break; + } + + ERR_FAIL_COND(key_frame < base_page_frame); // Sanity check, should never happen + + if (key_frame - base_page_frame >= max_frames_per_page) { + // Invalid because beyond the max frames allowed per page + best_invalid_frame = MIN(best_invalid_frame, key_frame); + } else if (key_frame < best_frame) { + best_frame = key_frame; + best_frame_track = i; + } + } + + print_animc("*KEY*: Current Frame: " + itos(current_frame) + " Best Frame: " + rtos(best_frame) + " Best Track: " + itos(best_frame_track) + " Start: " + String(start_frame ? "true" : "false")); + + if (!start_frame && best_frame > current_frame) { + // Any case where the current frame advanced, either because nothing was found or because something was found greater than the current one. + print_animc("\tAdvance Condition."); + bool rollback = false; + + // The frame has advanced, time to validate the previous frame + uint32_t current_page_size = base_page_size; + for (uint32_t i = 0; i < data_tracks.size(); i++) { + uint32_t track_size = data_tracks[i].data.size(); // track size + track_size += data_tracks[i].get_temp_packet_size(); // Add the temporary data + if (track_size > Compression::MAX_DATA_TRACK_SIZE) { + rollback = true; //track to large, time track can't point to keys any longer, because key offset is 12 bits + break; + } + current_page_size += track_size; + } + for (uint32_t i = 0; i < time_tracks.size(); i++) { + current_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits + } + + if (!rollback && current_page_size > p_page_size) { + rollback = true; + } + + print_animc("\tCurrent Page Size: " + itos(current_page_size) + "/" + itos(p_page_size) + " Rollback? " + String(rollback ? "YES!" : "no")); + + if (rollback) { + // Not valid any longer, so rollback and commit page + + for (uint32_t i = 0; i < data_tracks.size(); i++) { + data_tracks[i].temp_packets.resize(data_tracks[i].validated_packet_count); + } + for (uint32_t i = 0; i < time_tracks.size(); i++) { + time_tracks[i].key_index = time_tracks[i].validated_key_index; //rollback key + time_tracks[i].packets.resize(time_tracks[i].validated_packet_count); + } + + } else { + // All valid, so save rollback information + for (uint32_t i = 0; i < data_tracks.size(); i++) { + data_tracks[i].validated_packet_count = data_tracks[i].temp_packets.size(); + } + for (uint32_t i = 0; i < time_tracks.size(); i++) { + time_tracks[i].validated_key_index = time_tracks[i].key_index; + time_tracks[i].validated_packet_count = time_tracks[i].packets.size(); + } + + // Accept this frame as the frame being processed (as long as it exists) + if (best_frame != FRAME_MAX) { + current_frame = best_frame; + print_animc("\tValidated, New Current Frame: " + itos(current_frame)); + } + } + + if (rollback || best_frame == FRAME_MAX) { + // Commit the page if had to rollback or if no track was found + print_animc("\tCommiting page.."); + + // The end frame for the page depends entirely on whether its valid or + // no more keys were found. + // If not valid, then the end frame is the current frame (as this means the current frame is being rolled back + // If valid, then the end frame is the next invalid one (in case more frames exist), or the current frame in case no more frames exist. + uint32_t page_end_frame = (rollback || best_frame == FRAME_MAX) ? current_frame : best_invalid_frame; + + print_animc("\tEnd Frame: " + itos(page_end_frame) + ", " + rtos(page_end_frame * frame_len) + "s"); + + // Add finalizer frames and commit pending tracks + uint32_t finalizer_local_frame = page_end_frame - base_page_frame; + + uint32_t total_page_size = 0; + + for (uint32_t i = 0; i < data_tracks.size(); i++) { + if (data_tracks[i].temp_packets.size() == 0 || (data_tracks[i].temp_packets[data_tracks[i].temp_packets.size() - 1].frame) < finalizer_local_frame) { + // Add finalizer frame if it makes sense + Vector3i values = _compress_key(tracks_to_compress[i], track_bounds[i], -1, page_end_frame * frame_len); + + bool first_key = data_tracks[i].insert_key(finalizer_local_frame, values); + if (first_key) { + AnimationCompressionTimeState::Packet p; + p.count = 1; + p.frame = finalizer_local_frame; + p.offset = data_tracks[i].data.size(); + time_tracks[i].packets.push_back(p); + } else { + ERR_FAIL_COND(time_tracks[i].packets.size() == 0); + time_tracks[i].packets[time_tracks[i].packets.size() - 1].count++; + } + } + + data_tracks[i].commit_temp_packets(); + total_page_size += data_tracks[i].data.size(); + total_page_size += time_tracks[i].packets.size() * 4; + total_page_size += track_header_size; + + print_animc("\tTrack " + itos(i) + " time packets: " + itos(time_tracks[i].packets.size()) + " Packet data: " + itos(data_tracks[i].data.size())); + } + + print_animc("\tTotal page Size: " + itos(total_page_size) + "/" + itos(p_page_size)); + + // Create Page + Vector<uint8_t> page_data; + page_data.resize(total_page_size); + { + uint8_t *page_ptr = page_data.ptrw(); + uint32_t base_offset = data_tracks.size() * track_header_size; + + for (uint32_t i = 0; i < data_tracks.size(); i++) { + encode_uint32(base_offset, page_ptr + (track_header_size * i + 0)); + uint16_t *key_time_ptr = (uint16_t *)(page_ptr + base_offset); + for (uint32_t j = 0; j < time_tracks[i].packets.size(); j++) { + key_time_ptr[j * 2 + 0] = uint16_t(time_tracks[i].packets[j].frame); + uint16_t ptr = time_tracks[i].packets[j].offset / 4; + ptr |= (time_tracks[i].packets[j].count - 1) << 12; + key_time_ptr[j * 2 + 1] = ptr; + base_offset += 4; + } + encode_uint32(time_tracks[i].packets.size(), page_ptr + (track_header_size * i + 4)); + encode_uint32(base_offset, page_ptr + (track_header_size * i + 8)); + memcpy(page_ptr + base_offset, data_tracks[i].data.ptr(), data_tracks[i].data.size()); + base_offset += data_tracks[i].data.size(); + + //reset track + data_tracks[i].data.clear(); + data_tracks[i].temp_packets.clear(); + data_tracks[i].validated_packet_count = -1; + + time_tracks[i].needs_start_frame = true; //Not required the first time, but from now on it is. + time_tracks[i].packets.clear(); + time_tracks[i].validated_key_index = -1; + time_tracks[i].validated_packet_count = 0; + } + } + + Compression::Page page; + page.data = page_data; + page.time_offset = base_page_frame * frame_len; + compression.pages.push_back(page); + + if (!rollback && best_invalid_frame == FRAME_MAX) { + break; // No more pages to add. + } + + current_frame = page_end_frame; + base_page_frame = page_end_frame; + + continue; // Start over + } + } + + // A key was found for the current frame and all is ok + + uint32_t comp_track = best_frame_track; + Vector3i values; + + if (start_frame) { + // Interpolate + values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], -1, base_page_frame * frame_len); + } else { + uint32_t key = time_tracks[comp_track].key_index; + values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], key); + time_tracks[comp_track].key_index++; //goto next key (but could be rolled back if beyond page size). + } + + bool first_key = data_tracks[comp_track].insert_key(best_frame - base_page_frame, values); + if (first_key) { + AnimationCompressionTimeState::Packet p; + p.count = 1; + p.frame = best_frame - base_page_frame; + p.offset = data_tracks[comp_track].data.size(); + time_tracks[comp_track].packets.push_back(p); + } else { + ERR_CONTINUE(time_tracks[comp_track].packets.size() == 0); + time_tracks[comp_track].packets[time_tracks[comp_track].packets.size() - 1].count++; + } + } + + compression.bounds = track_bounds; + compression.fps = p_fps; + compression.enabled = true; + + for (uint32_t i = 0; i < tracks_to_compress.size(); i++) { + Track *t = tracks[tracks_to_compress[i]]; + t->interpolation = INTERPOLATION_LINEAR; //only linear supported + switch (t->type) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + tt->positions.clear(); + tt->compressed_track = i; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + rt->rotations.clear(); + rt->compressed_track = i; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + st->scales.clear(); + st->compressed_track = i; + print_line("Scale Bounds " + itos(i) + ": " + track_bounds[i]); + } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + bst->blend_shapes.clear(); + bst->compressed_track = i; + } break; + default: { + } + } + } +#if 1 + uint32_t orig_size = 0; + for (int i = 0; i < get_track_count(); i++) { + switch (track_get_type(i)) { + case TYPE_SCALE_3D: + case TYPE_POSITION_3D: { + orig_size += sizeof(TKey<Vector3>) * track_get_key_count(i); + } break; + case TYPE_ROTATION_3D: { + orig_size += sizeof(TKey<Quaternion>) * track_get_key_count(i); + } break; + case TYPE_BLEND_SHAPE: { + orig_size += sizeof(TKey<float>) * track_get_key_count(i); + } break; + default: { + } + } + } + + uint32_t new_size = 0; + for (uint32_t i = 0; i < compression.pages.size(); i++) { + new_size += compression.pages[i].data.size(); + } + + print_line("Original size: " + itos(orig_size) + " - Compressed size: " + itos(new_size) + " " + String::num(float(new_size) / float(orig_size) * 100, 2) + "% pages: " + itos(compression.pages.size())); +#endif +} + +bool Animation::_rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const { + Vector3i current; + Vector3i next; + double time_current; + double time_next; + + if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) { + return false; //some sort of problem + } + + if (time_current >= p_time || time_current == time_next) { + r_ret = _uncompress_quaternion(current); + } else if (p_time >= time_next) { + r_ret = _uncompress_quaternion(next); + } else { + double c = (p_time - time_current) / (time_next - time_current); + Quaternion from = _uncompress_quaternion(current); + Quaternion to = _uncompress_quaternion(next); + r_ret = from.slerp(to, c); + } + + return true; +} + +bool Animation::_pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const { + Vector3i current; + Vector3i next; + double time_current; + double time_next; + + if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) { + return false; //some sort of problem + } + + if (time_current >= p_time || time_current == time_next) { + r_ret = _uncompress_pos_scale(p_compressed_track, current); + } else if (p_time >= time_next) { + r_ret = _uncompress_pos_scale(p_compressed_track, next); + } else { + double c = (p_time - time_current) / (time_next - time_current); + Vector3 from = _uncompress_pos_scale(p_compressed_track, current); + Vector3 to = _uncompress_pos_scale(p_compressed_track, next); + r_ret = from.lerp(to, c); + } + + return true; +} +bool Animation::_blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const { + Vector3i current; + Vector3i next; + double time_current; + double time_next; + + if (!_fetch_compressed<1>(p_compressed_track, p_time, current, time_current, next, time_next)) { + return false; //some sort of problem + } + + if (time_current >= p_time || time_current == time_next) { + r_ret = _uncompress_blend_shape(current); + } else if (p_time >= time_next) { + r_ret = _uncompress_blend_shape(next); + } else { + float c = (p_time - time_current) / (time_next - time_current); + float from = _uncompress_blend_shape(current); + float to = _uncompress_blend_shape(next); + r_ret = Math::lerp(from, to, c); + } + + return true; +} + +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_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false); + p_time = CLAMP(p_time, 0, length); + if (key_index) { + *key_index = 0; + } + + double frame_to_sec = 1.0 / double(compression.fps); + + int32_t page_index = -1; + for (uint32_t i = 0; i < compression.pages.size(); i++) { + if (compression.pages[i].time_offset > p_time) { + break; + } + page_index = i; + } + + ERR_FAIL_COND_V(page_index == -1, false); //should not happen + + double page_base_time = compression.pages[page_index].time_offset; + const uint8_t *page_data = compression.pages[page_index].data.ptr(); +#ifndef _MSC_VER +#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported +#endif + const uint32_t *indices = (const uint32_t *)page_data; + const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; + uint32_t time_key_count = indices[p_compressed_track * 3 + 1]; + + int32_t packet_idx = 0; + double packet_time = double(time_keys[0]) * frame_to_sec + page_base_time; + uint32_t base_frame = time_keys[0]; + + for (uint32_t i = 1; i < time_key_count; i++) { + uint32_t f = time_keys[i * 2 + 0]; + double frame_time = double(f) * frame_to_sec + page_base_time; + + if (frame_time > p_time) { + break; + } + + if (key_index) { + (*key_index) += (time_keys[(i - 1) * 2 + 1] >> 12) + 1; + } + + packet_idx = i; + packet_time = frame_time; + base_frame = f; + } + + const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]]; + + uint16_t time_key_data = time_keys[packet_idx * 2 + 1]; + uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits + uint32_t data_count = (time_key_data >> 12) + 1; + + const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset); + + uint16_t decode[COMPONENTS]; + uint16_t decode_next[COMPONENTS]; + + for (uint32_t i = 0; i < COMPONENTS; i++) { + decode[i] = data_key[i]; + decode_next[i] = data_key[i]; + } + + double next_time = packet_time; + + if (p_time > packet_time) { // If its equal or less, then don't bother + if (data_count > 1) { + //decode forward + uint32_t bit_width[COMPONENTS]; + for (uint32_t i = 0; i < COMPONENTS; i++) { + bit_width[i] = (data_key[COMPONENTS] >> (i * 4)) & 0xF; + } + + uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1; + + AnimationCompressionBufferBitsRead buffer; + + buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1]; + + for (uint32_t i = 1; i < data_count; i++) { + uint32_t frame_delta = buffer.read(frame_bit_width); + base_frame += frame_delta; + + for (uint32_t j = 0; j < COMPONENTS; j++) { + if (bit_width[j] == 0) { + continue; // do none + } + uint32_t valueu = buffer.read(bit_width[j] + 1); + bool sign = valueu & (1 << bit_width[j]); + int16_t value = valueu & ((1 << bit_width[j]) - 1); + if (sign) { + value = -value - 1; + } + + decode_next[j] += value; + } + + next_time = double(base_frame) * frame_to_sec + page_base_time; + if (p_time < next_time) { + break; + } + + packet_time = next_time; + + for (uint32_t j = 0; j < COMPONENTS; j++) { + decode[j] = decode_next[j]; + } + + if (key_index) { + (*key_index)++; + } + } + } + + if (p_time > next_time) { // > instead of >= because if its equal, then it will be properly interpolated anyway + // So, the last frame found still has a time that is less than the required frame, + // will have to interpolate with the first frame of the next timekey. + + if ((uint32_t)packet_idx < time_key_count - 1) { // Sanity check but should not matter much, otherwise current next packet is last packet + + uint16_t time_key_data_next = time_keys[(packet_idx + 1) * 2 + 1]; + uint32_t data_offset_next = (time_key_data_next & 0xFFF) * 4; // Lower 12 bits + + const uint16_t *data_key_next = (const uint16_t *)(data_keys_base + data_offset_next); + base_frame = time_keys[(packet_idx + 1) * 2 + 0]; + next_time = double(base_frame) * frame_to_sec + page_base_time; + for (uint32_t i = 0; i < COMPONENTS; i++) { + decode_next[i] = data_key_next[i]; + } + } + } + } + + r_current_time = packet_time; + r_next_time = next_time; + + for (uint32_t i = 0; i < COMPONENTS; i++) { + r_current_value[i] = decode[i]; + r_next_value[i] = decode_next[i]; + } + + return true; +} + +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_UNSIGNED_INDEX(p_compressed_track, compression.bounds.size()); + + double frame_to_sec = 1.0 / double(compression.fps); + uint32_t key_index = 0; + + for (uint32_t p = 0; p < compression.pages.size(); p++) { + if (compression.pages[p].time_offset >= p_time + p_delta) { + // Page beyond range + return; + } + + // Page within range + + uint32_t page_index = p; + + double page_base_time = compression.pages[page_index].time_offset; + const uint8_t *page_data = compression.pages[page_index].data.ptr(); +#ifndef _MSC_VER +#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported +#endif + const uint32_t *indices = (const uint32_t *)page_data; + const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; + uint32_t time_key_count = indices[p_compressed_track * 3 + 1]; + + for (uint32_t i = 0; i < time_key_count; i++) { + uint32_t f = time_keys[i * 2 + 0]; + double frame_time = f * frame_to_sec + page_base_time; + if (frame_time >= p_time + p_delta) { + return; + } else if (frame_time >= p_time) { + r_indices->push_back(key_index); + } + + key_index++; + + const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]]; + + uint16_t time_key_data = time_keys[i * 2 + 1]; + uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits + uint32_t data_count = (time_key_data >> 12) + 1; + + const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset); + + if (data_count > 1) { + //decode forward + uint32_t bit_width[COMPONENTS]; + for (uint32_t j = 0; j < COMPONENTS; j++) { + bit_width[j] = (data_key[COMPONENTS] >> (j * 4)) & 0xF; + } + + uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1; + + AnimationCompressionBufferBitsRead buffer; + + buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1]; + + for (uint32_t j = 1; j < data_count; j++) { + uint32_t frame_delta = buffer.read(frame_bit_width); + f += frame_delta; + + frame_time = f * frame_to_sec + page_base_time; + if (frame_time >= p_time + p_delta) { + return; + } else if (frame_time >= p_time) { + r_indices->push_back(key_index); + } + + for (uint32_t k = 0; k < COMPONENTS; k++) { + if (bit_width[k] == 0) { + continue; // do none + } + buffer.read(bit_width[k] + 1); // skip + } + + key_index++; + } + } + } + } +} + +int Animation::_get_compressed_key_count(uint32_t p_compressed_track) const { + ERR_FAIL_COND_V(!compression.enabled, -1); + ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), -1); + + int key_count = 0; + + for (uint32_t i = 0; i < compression.pages.size(); i++) { + const uint8_t *page_data = compression.pages[i].data.ptr(); +#ifndef _MSC_VER +#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported +#endif + const uint32_t *indices = (const uint32_t *)page_data; + const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; + uint32_t time_key_count = indices[p_compressed_track * 3 + 1]; + + for (uint32_t j = 0; j < time_key_count; j++) { + key_count += (time_keys[j * 2 + 1] >> 12) + 1; + } + } + + return key_count; +} + +Quaternion Animation::_uncompress_quaternion(const Vector3i &p_value) const { + Vector3 axis = Vector3::octahedron_decode(Vector2(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0)); + float angle = (float(p_value.z) / 65535.0) * 2.0 * Math_PI; + return Quaternion(axis, angle); +} +Vector3 Animation::_uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const { + Vector3 pos_norm(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0, float(p_value.z) / 65535.0); + return compression.bounds[p_compressed_track].position + pos_norm * compression.bounds[p_compressed_track].size; +} +float Animation::_uncompress_blend_shape(const Vector3i &p_value) const { + float bsn = float(p_value.x) / 65535.0; + return (bsn * 2.0 - 1.0) * float(Compression::BLEND_SHAPE_RANGE); +} + +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_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(); +#ifndef _MSC_VER +#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported +#endif + const uint32_t *indices = (const uint32_t *)page_data; + const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; + uint32_t time_key_count = indices[p_compressed_track * 3 + 1]; + const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]]; + + for (uint32_t j = 0; j < time_key_count; j++) { + uint32_t subkeys = (time_keys[j * 2 + 1] >> 12) + 1; + if ((uint32_t)p_index < subkeys) { + uint16_t data_offset = (time_keys[j * 2 + 1] & 0xFFF) * 4; + + const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset); + + uint16_t frame = time_keys[j * 2 + 0]; + uint16_t decode[COMPONENTS]; + + for (uint32_t k = 0; k < COMPONENTS; k++) { + decode[k] = data_key[k]; + } + + if (p_index > 0) { + uint32_t bit_width[COMPONENTS]; + for (uint32_t k = 0; k < COMPONENTS; k++) { + bit_width[k] = (data_key[COMPONENTS] >> (k * 4)) & 0xF; + } + uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1; + + AnimationCompressionBufferBitsRead buffer; + buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1]; + + for (int k = 0; k < p_index; k++) { + uint32_t frame_delta = buffer.read(frame_bit_width); + frame += frame_delta; + for (uint32_t l = 0; l < COMPONENTS; l++) { + if (bit_width[l] == 0) { + continue; // do none + } + uint32_t valueu = buffer.read(bit_width[l] + 1); + bool sign = valueu & (1 << bit_width[l]); + int16_t value = valueu & ((1 << bit_width[l]) - 1); + if (sign) { + value = -value - 1; + } + + decode[l] += value; + } + } + } + + r_time = compression.pages[i].time_offset + double(frame) / double(compression.fps); + for (uint32_t l = 0; l < COMPONENTS; l++) { + r_value[l] = decode[l]; + } + + return true; + + } else { + p_index -= subkeys; + } + } + } + + return false; +} + Animation::Animation() {} Animation::~Animation() { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 4ee0741d87..ee07fb19d3 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -32,6 +32,7 @@ #define ANIMATION_H #include "core/io/resource.h" +#include "core/templates/local_vector.h" #define ANIM_MIN_LENGTH 0.001 @@ -98,7 +99,7 @@ private: struct PositionTrack : public Track { Vector<TKey<Vector3>> positions; - + int32_t compressed_track = -1; PositionTrack() { type = TYPE_POSITION_3D; } }; @@ -106,7 +107,7 @@ private: struct RotationTrack : public Track { Vector<TKey<Quaternion>> rotations; - + int32_t compressed_track = -1; RotationTrack() { type = TYPE_ROTATION_3D; } }; @@ -114,6 +115,7 @@ private: struct ScaleTrack : public Track { Vector<TKey<Vector3>> scales; + int32_t compressed_track = -1; ScaleTrack() { type = TYPE_SCALE_3D; } }; @@ -121,6 +123,7 @@ private: struct BlendShapeTrack : public Track { Vector<TKey<float>> blend_shapes; + int32_t compressed_track = -1; BlendShapeTrack() { type = TYPE_BLEND_SHAPE; } }; @@ -230,6 +233,89 @@ private: real_t step = 0.1; bool loop = false; + /* Animation compression page format (version 1): + * + * Animation uses bitwidth based compression separated into small pages. The intention is that pages fit easily in the cache, so decoding is cache efficient. + * The page-based nature also makes future animation streaming from disk possible. + * + * Actual format: + * + * num_compressed_tracks = bounds.size() + * header : (x num_compressed_tracks) + * ------- + * timeline_keys_offset : uint32_t - offset to time keys + * timeline_size : uint32_t - amount of time keys + * data_keys_offset : uint32_t offset to key data + * + * time key (uint32_t): + * ------------------ + * frame : bits 0-15 - time offset of key, computed as: page.time_offset + frame * (1.0/fps) + * data_key_offset : bits 16-27 - offset to key data, computed as: data_keys_offset * 4 + data_key_offset + * data_key_count : bits 28-31 - amount of data keys pointed to, computed as: data_key_count+1 (max 16) + * + * data key: + * --------- + * X / Blend Shape : uint16_t - X coordinate of XYZ vector key, or Blend Shape value. If Blend shape, Y and Z are not present and can be ignored. + * Y : uint16_t + * Z : uint16_t + * If data_key_count+1 > 1 (if more than 1 key is stored): + * data_bitwidth : uint16_t - This is only present if data_key_count > 1. Contains delta bitwidth information. + * X / Blend Shape delta bitwidth: bits 0-3 - + * if 0, nothing is present for X (use the first key-value for subsequent keys), + * else assume the number of bits present for each element (+ 1 for sign). Assumed always 16 bits, delta max signed 15 bits, with underflow and overflow supported. + * Y delta bitwidth : bits 4-7 + * Z delta bitwidth : bits 8-11 + * FRAME delta bitwidth : 12-15 bits - always present (obviously), actual bitwidth is FRAME+1 + * Data key is 4 bytes long for Blend Shapes, 8 bytes long for pos/rot/scale. + * + * delta keys: + * ----------- + * Compressed format is packed in the following format after the data key, containing delta keys one after the next in a tightly bit packed fashion. + * FRAME bits -> X / Blend Shape Bits (if bitwidth > 0) -> Y Bits (if not Blend Shape and Y Bitwidth > 0) -> Z Bits (if not Blend Shape and Z Bitwidth > 0) + * + * data key format: + * ---------------- + * Decoding keys means starting from the base key and going key by key applying deltas until the proper position is reached needed for interpolation. + * Resulting values are uint32_t + * data for X / Blend Shape, Y and Z must be normalized first: unorm = float(data) / 65535.0 + * **Blend Shape**: (unorm * 2.0 - 1.0) * Compression::BLEND_SHAPE_RANGE + * **Pos/Scale**: unorm_vec3 * bounds[track].size + bounds[track].position + * **Rotation**: Quaternion(Vector3::octahedron_decode(unorm_vec3.xy),unorm_vec3.z * Math_PI * 2.0) + * **Frame**: page.time_offset + frame * (1.0/fps) + */ + + struct Compression { + enum { + MAX_DATA_TRACK_SIZE = 16384, + BLEND_SHAPE_RANGE = 8, // - 8.0 to 8.0 + FORMAT_VERSION = 1 + }; + struct Page { + Vector<uint8_t> data; + double time_offset; + }; + + uint32_t fps = 120; + LocalVector<Page> pages; + LocalVector<AABB> bounds; //used by position and scale tracks (which contain index to track and index to bounds). + bool enabled = false; + } compression; + + Vector3i _compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key = -1, float p_time = 0.0); + bool _rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const; + bool _pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const; + bool _blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const; + template <uint32_t COMPONENTS> + bool _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 = nullptr) const; + template <uint32_t COMPONENTS> + bool _fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const; + int _get_compressed_key_count(uint32_t p_compressed_track) const; + template <uint32_t COMPONENTS> + void _get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const; + _FORCE_INLINE_ Quaternion _uncompress_quaternion(const Vector3i &p_value) const; + _FORCE_INLINE_ Vector3 _uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const; + _FORCE_INLINE_ float _uncompress_blend_shape(const Vector3i &p_value) const; + // bind helpers private: Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const { @@ -281,8 +367,7 @@ public: void track_set_path(int p_track, const NodePath &p_path); NodePath track_get_path(int p_track) const; - int find_track(const NodePath &p_path) const; - // transform + int find_track(const NodePath &p_path, const TrackType p_type) const; void track_move_up(int p_track); void track_move_down(int p_track); @@ -306,6 +391,7 @@ public: Variant track_get_key_value(int p_track, int p_key_idx) const; double track_get_key_time(int p_track, int p_key_idx) const; real_t track_get_key_transition(int p_track, int p_key_idx) const; + bool track_is_compressed(int p_track) const; int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position); Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const; @@ -376,6 +462,7 @@ public: void clear(); void optimize(real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125); + void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests Animation(); ~Animation(); 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 9fdfd493c1..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)); @@ -1030,7 +1039,6 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { dynamic_font_data.instantiate(); dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); dynamic_font->add_data(dynamic_font_data); - dynamic_font->set_base_size(default_font_size); default_font = dynamic_font; } 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/font.cpp b/scene/resources/font.cpp index 04e2b0dc70..819ae95715 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -1072,10 +1072,6 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data); ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); - ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size); - ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size); - ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size"); - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates); ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates"); @@ -1087,20 +1083,20 @@ void Font::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); - ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); @@ -1195,7 +1191,6 @@ void Font::reset_state() { data.clear(); rids.clear(); - base_size = 16; variation_coordinates.clear(); spacing_bottom = 0; spacing_top = 0; @@ -1308,14 +1303,6 @@ void Font::remove_data(int p_idx) { notify_property_list_changed(); } -void Font::set_base_size(int p_size) { - base_size = p_size; -} - -int Font::get_base_size() const { - return base_size; -} - void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) { _data_changed(); variation_coordinates = p_variation_coordinates; @@ -1355,51 +1342,46 @@ int Font::get_spacing(TextServer::SpacingType p_spacing) const { } real_t Font::get_height(int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size)); + ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); } return ret + spacing_bottom + spacing_top; } real_t Font::get_ascent(int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], size)); + ret = MAX(ret, TS->font_get_ascent(rids[i], p_size)); } return ret + spacing_top; } real_t Font::get_descent(int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { _ensure_rid(i); - ret = MAX(ret, TS->font_get_descent(rids[i], size)); + ret = MAX(ret, TS->font_get_descent(rids[i], p_size)); } return ret + spacing_bottom; } real_t Font::get_underline_position(int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_position(rids[i], size)); + ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size)); } return ret + spacing_top; } real_t Font::get_underline_thickness(int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size)); + ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size)); } return ret; } @@ -1407,8 +1389,6 @@ real_t Font::get_underline_thickness(int p_size) const { Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const { ERR_FAIL_COND_V(data.is_empty(), Size2()); - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); } @@ -1418,14 +1398,14 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); hash = hash_djb2_one_64(p_flags, hash); } - hash = hash_djb2_one_64(size, hash); + hash = hash_djb2_one_64(p_size, hash); Ref<TextLine> buffer; if (cache.has(hash)) { buffer = cache.get(hash); } else { buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); } return buffer->get_size(); @@ -1434,8 +1414,6 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const { ERR_FAIL_COND_V(data.is_empty(), Size2()); - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); } @@ -1443,14 +1421,14 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int uint64_t hash = p_text.hash64(); uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(size, wrp_hash); + wrp_hash = hash_djb2_one_64(p_size, wrp_hash); Ref<TextParagraph> lines_buffer; if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); lines_buffer->set_flags(p_flags); cache_wrap.insert(wrp_hash, lines_buffer); @@ -1473,8 +1451,6 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); } @@ -1484,14 +1460,14 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); hash = hash_djb2_one_64(p_flags, hash); } - hash = hash_djb2_one_64(size, hash); + hash = hash_djb2_one_64(p_size, hash); Ref<TextLine> buffer; if (cache.has(hash)) { buffer = cache.get(hash); } else { buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); } @@ -1515,8 +1491,6 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); } @@ -1524,14 +1498,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S uint64_t hash = p_text.hash64(); uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(size, wrp_hash); + wrp_hash = hash_djb2_one_64(p_size, wrp_hash); Ref<TextParagraph> lines_buffer; if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); lines_buffer->set_flags(p_flags); cache_wrap.insert(wrp_hash, lines_buffer); @@ -1573,16 +1547,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S } Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0); - Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size)); + int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); + Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0); - ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x; + int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); + ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; } return ret; } @@ -1591,22 +1563,20 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { } real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { - int size = (p_size <= 0) ? base_size : p_size; - for (int i = 0; i < data.size(); i++) { _ensure_rid(i); if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0); - real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x; + int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); + real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x; if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0); - ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x; + int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); + ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; } if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate); + TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate); } - TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate); + TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate); return ret; } } diff --git a/scene/resources/font.h b/scene/resources/font.h index e65fdb0751..e1f1f6d742 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -219,7 +219,6 @@ class Font : public Resource { mutable Vector<RID> rids; // Font config. - int base_size = 16; Dictionary variation_coordinates; int spacing_bottom = 0; int spacing_top = 0; @@ -237,6 +236,8 @@ protected: virtual void reset_state() override; public: + static const int DEFAULT_FONT_SIZE = 16; + Dictionary get_feature_list() const; // Font data. @@ -249,9 +250,6 @@ public: virtual void remove_data(int p_idx); // Font configuration. - virtual void set_base_size(int p_size); - virtual int get_base_size() const; - virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates); virtual Dictionary get_variation_coordinates() const; @@ -259,26 +257,26 @@ public: virtual int get_spacing(TextServer::SpacingType p_spacing) const; // Font metrics. - virtual real_t get_height(int p_size = -1) const; - virtual real_t get_ascent(int p_size = -1) const; - virtual real_t get_descent(int p_size = -1) const; - virtual real_t get_underline_position(int p_size = -1) const; - virtual real_t get_underline_thickness(int p_size = -1) const; + virtual real_t get_height(int p_size = DEFAULT_FONT_SIZE) const; + virtual real_t get_ascent(int p_size = DEFAULT_FONT_SIZE) const; + virtual real_t get_descent(int p_size = DEFAULT_FONT_SIZE) const; + virtual real_t get_underline_position(int p_size = DEFAULT_FONT_SIZE) const; + virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) const; // Drawing string. - virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; + virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; - virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; // Helper functions. virtual bool has_char(char32_t p_char) const; virtual String get_supported_chars() const; // Drawing char. - virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const; - virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const; + virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; Vector<RID> get_rids() const; 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.h b/scene/resources/mesh.h index 8d5571d67c..a95b4d4a5e 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -278,7 +278,6 @@ public: int surface_get_array_index_len(int p_idx) const override; uint32_t surface_get_format(int p_idx) const override; PrimitiveType surface_get_primitive_type(int p_idx) const override; - bool surface_is_alpha_sorting_enabled(int p_idx) const; virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override; virtual Ref<Material> surface_get_material(int p_idx) const override; 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/shape_2d.h b/scene/resources/shape_2d.h index 14bdd60e4b..7c5d1344e8 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -64,7 +64,6 @@ public: static bool is_collision_outline_enabled(); - Shape2D(); ~Shape2D(); }; 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/theme.cpp b/scene/resources/theme.cpp index 65cdc1e24e..99977a20f2 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -169,7 +169,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { const StringName *key2 = nullptr; while ((key2 = font_size_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2)); + list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater")); } } @@ -1655,7 +1655,7 @@ void Theme::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater"), "set_default_font_size", "get_default_font_size"); BIND_ENUM_CONSTANT(DATA_TYPE_COLOR); BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT); 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); |