diff options
Diffstat (limited to 'scene')
148 files changed, 6428 insertions, 5123 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 597693aa6a..cf84767151 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -551,7 +551,7 @@ void Area2D::_bind_methods() { ADD_GROUP("Physics Overrides", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-4096,4096,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 127ef6762d..860ccfec64 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -494,7 +494,7 @@ void AudioStreamPlayer2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "1,4096,1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 926997a715..f293081987 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -178,20 +178,23 @@ Transform2D Camera2D::get_camera_transform() { } Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom); - if (screen_rect.position.x < limit[SIDE_LEFT]) { - screen_rect.position.x = limit[SIDE_LEFT]; - } - if (screen_rect.position.x + screen_rect.size.x > limit[SIDE_RIGHT]) { - screen_rect.position.x = limit[SIDE_RIGHT] - screen_rect.size.x; - } + if (!limit_smoothing_enabled) { + if (screen_rect.position.x < limit[SIDE_LEFT]) { + screen_rect.position.x = limit[SIDE_LEFT]; + } - if (screen_rect.position.y + screen_rect.size.y > limit[SIDE_BOTTOM]) { - screen_rect.position.y = limit[SIDE_BOTTOM] - screen_rect.size.y; - } + if (screen_rect.position.x + screen_rect.size.x > limit[SIDE_RIGHT]) { + screen_rect.position.x = limit[SIDE_RIGHT] - screen_rect.size.x; + } + + if (screen_rect.position.y + screen_rect.size.y > limit[SIDE_BOTTOM]) { + screen_rect.position.y = limit[SIDE_BOTTOM] - screen_rect.size.y; + } - if (screen_rect.position.y < limit[SIDE_TOP]) { - screen_rect.position.y = limit[SIDE_TOP]; + if (screen_rect.position.y < limit[SIDE_TOP]) { + screen_rect.position.y = limit[SIDE_TOP]; + } } if (offset != Vector2()) { diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 52eabefbcb..4de99959a3 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -81,7 +81,7 @@ TypedArray<String> CanvasModulate::get_configuration_warnings() const { get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); if (nodes.size() > 1) { - warnings.push_back(TTR("Only one visible CanvasModulate is allowed per scene (or set of instanced scenes). The first created one will work, while the rest will be ignored.")); + warnings.push_back(TTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored.")); } } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index a633923be7..93d154bb01 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -31,7 +31,6 @@ #include "collision_object_2d.h" #include "scene/scene_string_names.h" -#include "servers/physics_server_2d.h" void CollisionObject2D::_notification(int p_what) { switch (p_what) { @@ -44,16 +43,22 @@ void CollisionObject2D::_notification(int p_what) { PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform); } - RID space = get_world_2d()->get_space(); - if (area) { - PhysicsServer2D::get_singleton()->area_set_space(rid, space); - } else { - PhysicsServer2D::get_singleton()->body_set_space(rid, space); + bool disabled = !is_enabled(); + + if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) { + _apply_disabled(); } - _update_pickable(); + if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { + RID space = get_world_2d()->get_space(); + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, space); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, space); + } + } - //get space + _update_pickable(); } break; case NOTIFICATION_ENTER_CANVAS: { @@ -67,6 +72,7 @@ void CollisionObject2D::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { _update_pickable(); } break; + case NOTIFICATION_TRANSFORM_CHANGED: { if (only_update_transform_changes) { return; @@ -79,15 +85,22 @@ void CollisionObject2D::_notification(int p_what) { } else { PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform); } - } break; + case NOTIFICATION_EXIT_TREE: { - if (area) { - PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); - } else { - PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + bool disabled = !is_enabled(); + + if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + } } + if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) { + _apply_enabled(); + } } break; case NOTIFICATION_EXIT_CANVAS: { @@ -97,6 +110,14 @@ void CollisionObject2D::_notification(int p_what) { PhysicsServer2D::get_singleton()->body_attach_canvas_instance_id(rid, ObjectID()); } } break; + + case NOTIFICATION_DISABLED: { + _apply_disabled(); + } break; + + case NOTIFICATION_ENABLED: { + _apply_enabled(); + } break; } } @@ -158,6 +179,79 @@ bool CollisionObject2D::get_collision_mask_bit(int p_bit) const { return get_collision_mask() & (1 << p_bit); } +void CollisionObject2D::set_disable_mode(DisableMode p_mode) { + if (disable_mode == p_mode) { + return; + } + + bool disabled = is_inside_tree() && !is_enabled(); + + if (disabled) { + // Cancel previous disable mode. + _apply_enabled(); + } + + disable_mode = p_mode; + + if (disabled) { + // Apply new disable mode. + _apply_disabled(); + } +} + +CollisionObject2D::DisableMode CollisionObject2D::get_disable_mode() const { + return disable_mode; +} + +void CollisionObject2D::_apply_disabled() { + switch (disable_mode) { + case DISABLE_MODE_REMOVE: { + if (is_inside_tree()) { + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + } + } + } break; + + case DISABLE_MODE_MAKE_STATIC: { + if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) { + PhysicsServer2D::get_singleton()->body_set_mode(rid, PhysicsServer2D::BODY_MODE_STATIC); + } + } break; + + case DISABLE_MODE_KEEP_ACTIVE: { + // Nothing to do. + } break; + } +} + +void CollisionObject2D::_apply_enabled() { + switch (disable_mode) { + case DISABLE_MODE_REMOVE: { + if (is_inside_tree()) { + RID space = get_world_2d()->get_space(); + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, space); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, space); + } + } + } break; + + case DISABLE_MODE_MAKE_STATIC: { + if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) { + PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode); + } + } break; + + case DISABLE_MODE_KEEP_ACTIVE: { + // Nothing to do. + } break; + } +} + uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) { ShapeData sd; uint32_t id; @@ -412,6 +506,22 @@ bool CollisionObject2D::is_only_update_transform_changes_enabled() const { return only_update_transform_changes; } +void CollisionObject2D::set_body_mode(PhysicsServer2D::BodyMode p_mode) { + ERR_FAIL_COND(area); + + if (body_mode == p_mode) { + return; + } + + body_mode = p_mode; + + if (is_inside_tree() && !is_enabled() && (disable_mode == DISABLE_MODE_MAKE_STATIC)) { + return; + } + + PhysicsServer2D::get_singleton()->body_set_mode(rid, p_mode); +} + void CollisionObject2D::_update_pickable() { if (!is_inside_tree()) { return; @@ -445,6 +555,8 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit); ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit); ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode); + ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); ClassDB::bind_method(D_METHOD("is_pickable"), &CollisionObject2D::is_pickable); ClassDB::bind_method(D_METHOD("create_shape_owner", "owner"), &CollisionObject2D::create_shape_owner); @@ -473,12 +585,18 @@ void CollisionObject2D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); + ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable"); + + BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE); + BIND_ENUM_CONSTANT(DISABLE_MODE_MAKE_STATIC); + BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { @@ -493,6 +611,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { PhysicsServer2D::get_singleton()->area_attach_object_instance_id(rid, get_instance_id()); } else { PhysicsServer2D::get_singleton()->body_attach_object_instance_id(rid, get_instance_id()); + PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode); } } diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index e10f3097d9..7a71affbb5 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -33,10 +33,19 @@ #include "scene/2d/node_2d.h" #include "scene/resources/shape_2d.h" +#include "servers/physics_server_2d.h" class CollisionObject2D : public Node2D { GDCLASS(CollisionObject2D, Node2D); +public: + enum DisableMode { + DISABLE_MODE_REMOVE, + DISABLE_MODE_MAKE_STATIC, + DISABLE_MODE_KEEP_ACTIVE, + }; + +private: uint32_t collision_layer = 1; uint32_t collision_mask = 1; @@ -44,6 +53,10 @@ class CollisionObject2D : public Node2D { RID rid; bool pickable = false; + DisableMode disable_mode = DISABLE_MODE_REMOVE; + + PhysicsServer2D::BodyMode body_mode = PhysicsServer2D::BODY_MODE_STATIC; + struct ShapeData { Object *owner = nullptr; Transform2D xform; @@ -64,6 +77,9 @@ class CollisionObject2D : public Node2D { Map<uint32_t, ShapeData> shapes; bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D + void _apply_disabled(); + void _apply_enabled(); + protected: CollisionObject2D(RID p_rid, bool p_area); @@ -79,6 +95,8 @@ protected: void set_only_update_transform_changes(bool p_enable); bool is_only_update_transform_changes_enabled() const; + void set_body_mode(PhysicsServer2D::BodyMode p_mode); + public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; @@ -92,6 +110,9 @@ public: void set_collision_mask_bit(int p_bit, bool p_value); bool get_collision_mask_bit(int p_bit) const; + void set_disable_mode(DisableMode p_mode); + DisableMode get_disable_mode() const; + uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); @@ -131,4 +152,6 @@ public: ~CollisionObject2D(); }; +VARIANT_ENUM_CAST(CollisionObject2D::DisableMode); + #endif // COLLISION_OBJECT_2D_H diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 1578643d14..24f3301ce1 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -464,31 +464,31 @@ Vector2 CPUParticles2D::get_gravity() const { void CPUParticles2D::_validate_property(PropertyInfo &property) const { if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_rect_extents" && emission_shape != EMISSION_SHAPE_RECTANGLE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1202,7 +1202,7 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &CPUParticles2D::restart); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); @@ -1313,7 +1313,7 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); ADD_GROUP("Scale", ""); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index d7404ff479..adfb94d574 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -451,7 +451,7 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid); #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); } #endif @@ -531,7 +531,7 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index 8a4ccc2f96..dbba6917b5 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -274,7 +274,7 @@ void PinJoint2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_softness", "softness"), &PinJoint2D::set_softness); ClassDB::bind_method(D_METHOD("get_softness"), &PinJoint2D::get_softness); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "softness", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_softness", "get_softness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "softness", PROPERTY_HINT_RANGE, "0.00,16,0.01,exp"), "set_softness", "get_softness"); } PinJoint2D::PinJoint2D() { @@ -336,8 +336,8 @@ void GrooveJoint2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_initial_offset", "offset"), &GrooveJoint2D::set_initial_offset); ClassDB::bind_method(D_METHOD("get_initial_offset"), &GrooveJoint2D::get_initial_offset); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_EXP_RANGE, "1,65535,1"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "initial_offset", PROPERTY_HINT_EXP_RANGE, "1,65535,1"), "set_initial_offset", "get_initial_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "1,65535,1,exp"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "initial_offset", PROPERTY_HINT_RANGE, "1,65535,1,exp"), "set_initial_offset", "get_initial_offset"); } GrooveJoint2D::GrooveJoint2D() { @@ -433,10 +433,10 @@ void DampedSpringJoint2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_damping", "damping"), &DampedSpringJoint2D::set_damping); ClassDB::bind_method(D_METHOD("get_damping"), &DampedSpringJoint2D::get_damping); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_EXP_RANGE, "1,65535,1"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rest_length", PROPERTY_HINT_EXP_RANGE, "0,65535,1"), "set_rest_length", "get_rest_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness", PROPERTY_HINT_EXP_RANGE, "0.1,64,0.1"), "set_stiffness", "get_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_EXP_RANGE, "0.01,16,0.01"), "set_damping", "get_damping"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "1,65535,1,exp"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rest_length", PROPERTY_HINT_RANGE, "0,65535,1,exp"), "set_rest_length", "get_rest_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness", PROPERTY_HINT_RANGE, "0.1,64,0.1,exp"), "set_stiffness", "get_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0.01,16,0.01,exp"), "set_damping", "get_damping"); } DampedSpringJoint2D::DampedSpringJoint2D() { diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index d2caf5bea8..ea639ae3a3 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -169,7 +169,7 @@ Ref<NavigationMesh> NavigationPolygon::get_mesh() { MutexLock lock(navmesh_generation); if (navmesh.is_null()) { - navmesh.instance(); + navmesh.instantiate(); Vector<Vector3> verts; { verts.resize(get_vertices().size()); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 9d86ec88be..6fd383ddab 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -164,14 +164,6 @@ void Node2D::set_skew(real_t p_radians) { _update_transform(); } -void Node2D::set_rotation_degrees(real_t p_degrees) { - set_rotation(Math::deg2rad(p_degrees)); -} - -void Node2D::set_skew_degrees(real_t p_degrees) { - set_skew(Math::deg2rad(p_degrees)); -} - void Node2D::set_scale(const Size2 &p_scale) { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); @@ -210,14 +202,6 @@ real_t Node2D::get_skew() const { return skew; } -real_t Node2D::get_rotation_degrees() const { - return Math::rad2deg(get_rotation()); -} - -real_t Node2D::get_skew_degrees() const { - return Math::rad2deg(get_skew()); -} - Size2 Node2D::get_scale() const { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); @@ -293,14 +277,6 @@ void Node2D::set_global_rotation(real_t p_radians) { } } -real_t Node2D::get_global_rotation_degrees() const { - return Math::rad2deg(get_global_rotation()); -} - -void Node2D::set_global_rotation_degrees(real_t p_degrees) { - set_global_rotation(Math::deg2rad(p_degrees)); -} - Size2 Node2D::get_global_scale() const { return get_global_transform().get_scale(); } @@ -403,16 +379,12 @@ bool Node2D::is_y_sort_enabled() const { void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Node2D::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_skew", "radians"), &Node2D::set_skew); - ClassDB::bind_method(D_METHOD("set_skew_degrees", "degrees"), &Node2D::set_skew_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node2D::set_scale); ClassDB::bind_method(D_METHOD("get_position"), &Node2D::get_position); ClassDB::bind_method(D_METHOD("get_rotation"), &Node2D::get_rotation); - ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node2D::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_skew"), &Node2D::get_skew); - ClassDB::bind_method(D_METHOD("get_skew_degrees"), &Node2D::get_skew_degrees); ClassDB::bind_method(D_METHOD("get_scale"), &Node2D::get_scale); ClassDB::bind_method(D_METHOD("rotate", "radians"), &Node2D::rotate); @@ -426,8 +398,6 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_global_position"), &Node2D::get_global_position); ClassDB::bind_method(D_METHOD("set_global_rotation", "radians"), &Node2D::set_global_rotation); ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node2D::get_global_rotation); - ClassDB::bind_method(D_METHOD("set_global_rotation_degrees", "degrees"), &Node2D::set_global_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_global_rotation_degrees"), &Node2D::get_global_rotation_degrees); ClassDB::bind_method(D_METHOD("set_global_scale", "scale"), &Node2D::set_global_scale); ClassDB::bind_method(D_METHOD("get_global_scale"), &Node2D::get_global_scale); @@ -452,17 +422,14 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent); ADD_GROUP("Transform", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_lesser,or_greater,noslider,suffix:px"), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_skew", "get_skew"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew_degrees", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1", PROPERTY_USAGE_EDITOR), "set_skew_degrees", "get_skew_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians"), "set_skew", "get_skew"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_transform", "get_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation_degrees", "get_global_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_scale", "get_global_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 339efd9179..3e66541e32 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -75,9 +75,7 @@ public: void set_position(const Point2 &p_pos); void set_rotation(real_t p_radians); - void set_rotation_degrees(real_t p_degrees); void set_skew(real_t p_radians); - void set_skew_degrees(real_t p_radians); void set_scale(const Size2 &p_scale); void rotate(real_t p_radians); @@ -90,20 +88,16 @@ public: Point2 get_position() const; real_t get_rotation() const; real_t get_skew() const; - real_t get_rotation_degrees() const; - real_t get_skew_degrees() const; Size2 get_scale() const; Point2 get_global_position() const; real_t get_global_rotation() const; - real_t get_global_rotation_degrees() const; Size2 get_global_scale() const; void set_transform(const Transform2D &p_transform); void set_global_transform(const Transform2D &p_transform); void set_global_position(const Point2 &p_pos); void set_global_rotation(real_t p_radians); - void set_global_rotation_degrees(real_t p_degrees); void set_global_scale(const Size2 &p_scale); void set_z_index(int p_z); diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 0c1be16174..d547914e16 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -31,33 +31,37 @@ #include "physical_bone_2d.h" void PhysicalBone2D::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - // Position the RigidBody in the correct position - if (follow_bone_when_simulating) { - _position_at_bone2d(); - } + switch (p_what) { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Position the RigidBody in the correct position. + if (follow_bone_when_simulating) { + _position_at_bone2d(); + } - // Keep the child joint in the correct position. - if (child_joint && auto_configure_joint) { - child_joint->set_global_position(get_global_position()); - } - } else if (p_what == NOTIFICATION_READY) { - _find_skeleton_parent(); - _find_joint_child(); + // Keep the child joint in the correct position. + if (child_joint && auto_configure_joint) { + child_joint->set_global_position(get_global_position()); + } + } break; - // Configure joint - if (child_joint && auto_configure_joint) { - _auto_configure_joint(); - } + case NOTIFICATION_READY: { + _find_skeleton_parent(); + _find_joint_child(); - // Simulate physics if set - if (simulate_physics) { - _start_physics_simulation(); - } else { - _stop_physics_simulation(); - } + // Configure joint. + if (child_joint && auto_configure_joint) { + _auto_configure_joint(); + } + + // Simulate physics if set. + if (simulate_physics) { + _start_physics_simulation(); + } else { + _stop_physics_simulation(); + } - set_physics_process_internal(true); + set_physics_process_internal(true); + } break; } } @@ -156,16 +160,16 @@ void PhysicalBone2D::_start_physics_simulation() { // Apply the correct mode RigidBody2D::Mode rigid_mode = get_mode(); if (rigid_mode == RigidBody2D::MODE_STATIC) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); } else if (rigid_mode == RigidBody2D::MODE_KINEMATIC) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC_LOCKED) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC_LOCKED); + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); } else { - // Default to Rigid - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC); + // Default to Dynamic. + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); } _internal_simulate_physics = true; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index ce63b25ce0..be619ed60d 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -49,7 +49,7 @@ void PhysicsBody2D::_bind_methods() { PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) : CollisionObject2D(PhysicsServer2D::get_singleton()->body_create(), false) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), p_mode); + set_body_mode(p_mode); set_pickable(false); } @@ -64,7 +64,7 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_i if (move_and_collide(p_motion, p_infinite_inertia, result, p_margin, p_exclude_raycast_shapes, p_test_only)) { if (motion_cache.is_null()) { - motion_cache.instance(); + motion_cache.instantiate(); motion_cache->owner = this; } @@ -76,13 +76,46 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_i return Ref<KinematicCollision2D>(); } -bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only) { +bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) { if (is_only_update_transform_changes_enabled()) { ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation."); } Transform2D gt = get_global_transform(); bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes); + // Restore direction of motion to be along original motion, + // in order to avoid sliding due to recovery, + // but only if collision depth is low enough to avoid tunneling. + real_t motion_length = p_motion.length(); + if (motion_length > CMP_EPSILON) { + real_t precision = 0.001; + + if (colliding && p_cancel_sliding) { + // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, + // so even in normal resting cases the depth can be a bit more than the margin. + precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); + + if (r_result.collision_depth > (real_t)p_margin + precision) { + p_cancel_sliding = false; + } + } + + if (p_cancel_sliding) { + // Check depth of recovery. + Vector2 motion_normal = p_motion / motion_length; + real_t dot = r_result.motion.dot(motion_normal); + Vector2 recovery = r_result.motion - motion_normal * dot; + real_t recovery_length = recovery.length(); + // Fixes cases where canceling slide causes the motion to go too deep into the ground, + // Becauses we're only taking rest information into account and not general recovery. + if (recovery_length < (real_t)p_margin + precision) { + // Apply adjustment to motion. + r_result.motion = motion_normal * dot; + r_result.remainder = p_motion - r_result.motion; + } + } + } + if (!p_test_only) { gt.elements[2] += r_result.motion; set_global_transform(gt); @@ -186,9 +219,9 @@ void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) { kinematic_motion = p_enabled; if (kinematic_motion) { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); } else { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); } _update_kinematic_motion(); @@ -199,28 +232,30 @@ bool StaticBody2D::is_kinematic_motion_enabled() const { } void StaticBody2D::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + switch (p_what) { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } + if (Engine::get_singleton()->is_editor_hint()) { + return; + } #endif - ERR_FAIL_COND(!kinematic_motion); + ERR_FAIL_COND(!kinematic_motion); - real_t delta_time = get_physics_process_delta_time(); + real_t delta_time = get_physics_process_delta_time(); - Transform2D new_transform = get_global_transform(); + Transform2D new_transform = get_global_transform(); - new_transform.translate(constant_linear_velocity * delta_time); - new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); + new_transform.translate(constant_linear_velocity * delta_time); + new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); - // Propagate transform change to node. - set_block_transform_notify(true); - set_global_transform(new_transform); - set_block_transform_notify(false); + // Propagate transform change to node. + set_block_transform_notify(true); + set_global_transform(new_transform); + set_block_transform_notify(false); + } break; } } @@ -495,18 +530,18 @@ void RigidBody2D::set_mode(Mode p_mode) { mode = p_mode; switch (p_mode) { case MODE_DYNAMIC: { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); } break; case MODE_STATIC: { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); } break; case MODE_KINEMATIC: { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); } break; case MODE_DYNAMIC_LOCKED: { - PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); } break; } @@ -762,18 +797,19 @@ bool RigidBody2D::is_contact_monitor_enabled() const { void RigidBody2D::_notification(int p_what) { #ifdef TOOLS_ENABLED - if (p_what == NOTIFICATION_ENTER_TREE) { - if (Engine::get_singleton()->is_editor_hint()) { - set_notify_local_transform(true); //used for warnings and only in editor - } - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + set_notify_local_transform(true); //used for warnings and only in editor + } + } break; - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; } - #endif } @@ -855,8 +891,8 @@ void RigidBody2D::_bind_methods() { BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState2D"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_NONE), "set_inertia", "get_inertia"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp", PROPERTY_USAGE_NONE), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -942,15 +978,16 @@ void CharacterBody2D::move_and_slide() { floor_normal = Vector2(); floor_velocity = Vector2(); - int slide_count = max_slides; - while (slide_count) { + // No sliding on first attempt to keep floor motion stable when possible. + bool sliding_enabled = false; + for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionResult result; bool found_collision = false; for (int i = 0; i < 2; ++i) { bool collided; if (i == 0) { //collide - collided = move_and_collide(motion, infinite_inertia, result, margin); + collided = move_and_collide(motion, infinite_inertia, result, margin, true, false, !sliding_enabled); if (!collided) { motion = Vector2(); //clear because no collision happened and motion completed } @@ -966,7 +1003,6 @@ void CharacterBody2D::move_and_slide() { found_collision = true; motion_results.push_back(result); - motion = result.remainder; if (up_direction == Vector2()) { //all is a wall @@ -980,7 +1016,7 @@ void CharacterBody2D::move_and_slide() { floor_velocity = result.collider_velocity; if (stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) { + if ((body_velocity_normal + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); gt.elements[2] -= result.motion.slide(up_direction); set_global_transform(gt); @@ -995,16 +1031,20 @@ void CharacterBody2D::move_and_slide() { } } - motion = motion.slide(result.collision_normal); - linear_velocity = linear_velocity.slide(result.collision_normal); + if (sliding_enabled || !on_floor) { + motion = result.remainder.slide(result.collision_normal); + linear_velocity = linear_velocity.slide(result.collision_normal); + } else { + motion = result.remainder; + } } + + sliding_enabled = true; } if (!found_collision || motion == Vector2()) { break; } - - --slide_count; } if (!was_on_floor || snap == Vector2()) { @@ -1120,7 +1160,7 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1207,14 +1247,6 @@ void CharacterBody2D::set_floor_max_angle(real_t p_radians) { floor_max_angle = p_radians; } -real_t CharacterBody2D::get_floor_max_angle_degrees() const { - return Math::rad2deg(floor_max_angle); -} - -void CharacterBody2D::set_floor_max_angle_degrees(real_t p_degrees) { - floor_max_angle = Math::deg2rad(p_degrees); -} - const Vector2 &CharacterBody2D::get_snap() const { return snap; } @@ -1232,26 +1264,28 @@ void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) { } void CharacterBody2D::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - last_valid_transform = get_global_transform(); - - // Reset move_and_slide() data. - on_floor = false; - on_floor_body = RID(); - on_ceiling = false; - on_wall = false; - motion_results.clear(); - floor_velocity = Vector2(); - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + last_valid_transform = get_global_transform(); + + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + motion_results.clear(); + floor_velocity = Vector2(); + } break; - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - //used by sync to physics, send the new transform to the physics - Transform2D new_transform = get_global_transform(); - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); - //but then revert changes - set_notify_local_transform(false); - set_global_transform(last_valid_transform); - set_notify_local_transform(true); + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + // Used by sync to physics, send the new transform to the physics. + Transform2D new_transform = get_global_transform(); + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); + // But then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + } break; } } @@ -1271,8 +1305,6 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides); ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody2D::get_floor_max_angle); ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody2D::set_floor_max_angle); - ClassDB::bind_method(D_METHOD("get_floor_max_angle_degrees"), &CharacterBody2D::get_floor_max_angle_degrees); - ClassDB::bind_method(D_METHOD("set_floor_max_angle_degrees", "degrees"), &CharacterBody2D::set_floor_max_angle_degrees); ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody2D::get_snap); ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody2D::set_snap); ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction); @@ -1293,8 +1325,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle_degrees", PROPERTY_HINT_RANGE, "0,180,0.1", PROPERTY_USAGE_EDITOR), "set_floor_max_angle_degrees", "get_floor_max_angle_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index f084a247aa..5a5417eaf3 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -50,7 +50,7 @@ protected: Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.08); public: - bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false); + bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true); bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); TypedArray<PhysicsBody2D> get_collision_exceptions(); @@ -303,9 +303,6 @@ private: real_t get_floor_max_angle() const; void set_floor_max_angle(real_t p_radians); - real_t get_floor_max_angle_degrees() const; - void set_floor_max_angle_degrees(real_t p_degrees); - const Vector2 &get_snap() const; void set_snap(const Vector2 &p_snap); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 1eec2a3833..7366be5a7d 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -365,7 +365,7 @@ void Polygon2D::_notification(int p_what) { arr[RS::ARRAY_INDEX] = index_array; RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); - RS::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh, Transform2D(), Color(), texture.is_valid() ? texture->get_rid() : RID()); + RS::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh, Transform2D(), Color(1, 1, 1), texture.is_valid() ? texture->get_rid() : RID()); } } break; @@ -462,14 +462,6 @@ real_t Polygon2D::get_texture_rotation() const { return tex_rot; } -void Polygon2D::set_texture_rotation_degrees(real_t p_rot) { - set_texture_rotation(Math::deg2rad(p_rot)); -} - -real_t Polygon2D::get_texture_rotation_degrees() const { - return Math::rad2deg(get_texture_rotation()); -} - void Polygon2D::set_texture_scale(const Size2 &p_scale) { tex_scale = p_scale; update(); @@ -613,9 +605,6 @@ void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_rotation", "texture_rotation"), &Polygon2D::set_texture_rotation); ClassDB::bind_method(D_METHOD("get_texture_rotation"), &Polygon2D::get_texture_rotation); - ClassDB::bind_method(D_METHOD("set_texture_rotation_degrees", "texture_rotation"), &Polygon2D::set_texture_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_texture_rotation_degrees"), &Polygon2D::get_texture_rotation_degrees); - ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &Polygon2D::set_texture_scale); ClassDB::bind_method(D_METHOD("get_texture_scale"), &Polygon2D::get_texture_scale); @@ -657,8 +646,7 @@ void Polygon2D::_bind_methods() { ADD_GROUP("Texture2D", "texture_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_offset"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_texture_rotation_degrees", "get_texture_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_texture_rotation", "get_texture_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_texture_rotation", "get_texture_rotation"); ADD_GROUP("Skeleton", ""); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton"); diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index f9f36ff9a2..bf386b9ace 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -120,9 +120,6 @@ public: void set_texture_rotation(real_t p_rot); real_t get_texture_rotation() const; - void set_texture_rotation_degrees(real_t p_rot); - real_t get_texture_rotation_degrees() const; - void set_texture_scale(const Size2 &p_scale); Size2 get_texture_scale() const; diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index a7613dc009..e7cef965fe 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -35,7 +35,7 @@ void RemoteTransform2D::_update_cache() { cache = ObjectID(); if (has_node(remote_node)) { Node *node = get_node(remote_node); - if (!node || this == node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + if (!node || this == node || node->is_ancestor_of(this) || this->is_ancestor_of(node)) { return; } diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 4e58984b37..0a6393551c 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -288,7 +288,7 @@ void TouchScreenButton::_press(int p_finger_pressed) { if (action != StringName()) { Input::get_singleton()->action_press(action); Ref<InputEventAction> iea; - iea.instance(); + iea.instantiate(); iea->set_action(action); iea->set_pressed(true); get_viewport()->input(iea, true); @@ -305,7 +305,7 @@ void TouchScreenButton::_release(bool p_exiting_tree) { Input::get_singleton()->action_release(action); if (!p_exiting_tree) { Ref<InputEventAction> iea; - iea.instance(); + iea.instantiate(); iea->set_action(action); iea->set_pressed(false); get_viewport()->input(iea, true); diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 44708cddff..cd64a813dd 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -601,7 +601,7 @@ void Area3D::_bind_methods() { ADD_GROUP("Physics Overrides", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 9640043031..ba559b4ecb 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -967,13 +967,13 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); ADD_GROUP("Emission Angle", "emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emission_angle_enabled"), "set_emission_angle_enabled", "is_emission_angle_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1"), "set_emission_angle", "get_emission_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1,degrees"), "set_emission_angle", "get_emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_filter_attenuation_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_emission_angle_filter_attenuation_db", "get_emission_angle_filter_attenuation_db"); ADD_GROUP("Attenuation Filter", "attenuation_filter_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1"), "set_attenuation_filter_cutoff_hz", "get_attenuation_filter_cutoff_hz"); @@ -997,7 +997,7 @@ void AudioStreamPlayer3D::_bind_methods() { } AudioStreamPlayer3D::AudioStreamPlayer3D() { - velocity_tracker.instance(); + velocity_tracker.instantiate(); AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_bus_layout_changed)); set_disable_scale(true); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 62bbebe6e7..1ed8c0b4eb 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -513,11 +513,11 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.001,10,0.001,or_greater"), "set_near", "get_near"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.01,4000,0.01,or_greater"), "set_far", "get_far"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); @@ -661,7 +661,7 @@ Camera3D::Camera3D() { set_perspective(75.0, 0.05, 4000.0); RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers); //active=false; - velocity_tracker.instance(); + velocity_tracker.instantiate(); set_notify_transform(true); set_disable_scale(true); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index a04667e53b..40e3f7c764 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -32,7 +32,6 @@ #include "core/config/engine.h" #include "scene/scene_string_names.h" -#include "servers/physics_server_3d.h" void CollisionObject3D::_notification(int p_what) { switch (p_what) { @@ -59,15 +58,22 @@ void CollisionObject3D::_notification(int p_what) { PhysicsServer3D::get_singleton()->body_set_state(rid, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); } - RID space = get_world_3d()->get_space(); - if (area) { - PhysicsServer3D::get_singleton()->area_set_space(rid, space); - } else { - PhysicsServer3D::get_singleton()->body_set_space(rid, space); + bool disabled = !is_enabled(); + + if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) { + _apply_disabled(); + } + + if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { + RID space = get_world_3d()->get_space(); + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, space); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, space); + } } _update_pickable(); - //get space } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -78,19 +84,34 @@ void CollisionObject3D::_notification(int p_what) { } _on_transform_changed(); - } break; + case NOTIFICATION_VISIBILITY_CHANGED: { _update_pickable(); - } break; + case NOTIFICATION_EXIT_WORLD: { - if (area) { - PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); - } else { - PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + bool disabled = !is_enabled(); + + if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + } + } + + if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) { + _apply_enabled(); } + } break; + case NOTIFICATION_DISABLED: { + _apply_disabled(); + } break; + + case NOTIFICATION_ENABLED: { + _apply_enabled(); } break; } } @@ -153,6 +174,79 @@ bool CollisionObject3D::get_collision_mask_bit(int p_bit) const { return get_collision_mask() & (1 << p_bit); } +void CollisionObject3D::set_disable_mode(DisableMode p_mode) { + if (disable_mode == p_mode) { + return; + } + + bool disabled = is_inside_tree() && !is_enabled(); + + if (disabled) { + // Cancel previous disable mode. + _apply_enabled(); + } + + disable_mode = p_mode; + + if (disabled) { + // Apply new disable mode. + _apply_disabled(); + } +} + +CollisionObject3D::DisableMode CollisionObject3D::get_disable_mode() const { + return disable_mode; +} + +void CollisionObject3D::_apply_disabled() { + switch (disable_mode) { + case DISABLE_MODE_REMOVE: { + if (is_inside_tree()) { + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + } + } + } break; + + case DISABLE_MODE_MAKE_STATIC: { + if (!area && (body_mode != PhysicsServer3D::BODY_MODE_STATIC)) { + PhysicsServer3D::get_singleton()->body_set_mode(rid, PhysicsServer3D::BODY_MODE_STATIC); + } + } break; + + case DISABLE_MODE_KEEP_ACTIVE: { + // Nothing to do. + } break; + } +} + +void CollisionObject3D::_apply_enabled() { + switch (disable_mode) { + case DISABLE_MODE_REMOVE: { + if (is_inside_tree()) { + RID space = get_world_3d()->get_space(); + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, space); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, space); + } + } + } break; + + case DISABLE_MODE_MAKE_STATIC: { + if (!area && (body_mode != PhysicsServer3D::BODY_MODE_STATIC)) { + PhysicsServer3D::get_singleton()->body_set_mode(rid, body_mode); + } + } break; + + case DISABLE_MODE_KEEP_ACTIVE: { + // Nothing to do. + } break; + } +} + void CollisionObject3D::_input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { if (get_script_instance()) { get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); @@ -174,6 +268,22 @@ void CollisionObject3D::_mouse_exit() { emit_signal(SceneStringNames::get_singleton()->mouse_exited); } +void CollisionObject3D::set_body_mode(PhysicsServer3D::BodyMode p_mode) { + ERR_FAIL_COND(area); + + if (body_mode == p_mode) { + return; + } + + body_mode = p_mode; + + if (is_inside_tree() && !is_enabled() && (disable_mode == DISABLE_MODE_MAKE_STATIC)) { + return; + } + + PhysicsServer3D::get_singleton()->body_set_mode(rid, p_mode); +} + void CollisionObject3D::_update_pickable() { if (!is_inside_tree()) { return; @@ -305,6 +415,8 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject3D::get_collision_layer_bit); ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject3D::set_collision_mask_bit); ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject3D::set_disable_mode); + ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject3D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable); ClassDB::bind_method(D_METHOD("is_ray_pickable"), &CollisionObject3D::is_ray_pickable); ClassDB::bind_method(D_METHOD("set_capture_input_on_drag", "enable"), &CollisionObject3D::set_capture_input_on_drag); @@ -332,6 +444,8 @@ void CollisionObject3D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); + ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); @@ -339,6 +453,10 @@ void CollisionObject3D::_bind_methods() { ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_ray_pickable"), "set_ray_pickable", "is_ray_pickable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_capture_on_drag"), "set_capture_input_on_drag", "get_capture_input_on_drag"); + + BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE); + BIND_ENUM_CONSTANT(DISABLE_MODE_MAKE_STATIC); + BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } uint32_t CollisionObject3D::create_shape_owner(Object *p_owner) { @@ -540,8 +658,8 @@ CollisionObject3D::CollisionObject3D(RID p_rid, bool p_area) { PhysicsServer3D::get_singleton()->area_attach_object_instance_id(rid, get_instance_id()); } else { PhysicsServer3D::get_singleton()->body_attach_object_instance_id(rid, get_instance_id()); + PhysicsServer3D::get_singleton()->body_set_mode(rid, body_mode); } - //set_transform_notify(true); } void CollisionObject3D::set_capture_input_on_drag(bool p_capture) { diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 50a9b4fcd0..a3a890db75 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -33,10 +33,19 @@ #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" +#include "servers/physics_server_3d.h" class CollisionObject3D : public Node3D { GDCLASS(CollisionObject3D, Node3D); +public: + enum DisableMode { + DISABLE_MODE_REMOVE, + DISABLE_MODE_MAKE_STATIC, + DISABLE_MODE_KEEP_ACTIVE, + }; + +private: uint32_t collision_layer = 1; uint32_t collision_mask = 1; @@ -44,6 +53,10 @@ class CollisionObject3D : public Node3D { RID rid; + DisableMode disable_mode = DISABLE_MODE_REMOVE; + + PhysicsServer3D::BodyMode body_mode = PhysicsServer3D::BODY_MODE_STATIC; + struct ShapeData { Object *owner = nullptr; Transform3D xform; @@ -76,6 +89,9 @@ class CollisionObject3D : public Node3D { void _update_debug_shapes(); void _clear_debug_shapes(); + void _apply_disabled(); + void _apply_enabled(); + protected: CollisionObject3D(RID p_rid, bool p_area); @@ -89,6 +105,8 @@ protected: virtual void _mouse_enter(); virtual void _mouse_exit(); + void set_body_mode(PhysicsServer3D::BodyMode p_mode); + public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; @@ -102,6 +120,9 @@ public: void set_collision_mask_bit(int p_bit, bool p_value); bool get_collision_mask_bit(int p_bit) const; + void set_disable_mode(DisableMode p_mode); + DisableMode get_disable_mode() const; + uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); @@ -138,4 +159,6 @@ public: ~CollisionObject3D(); }; +VARIANT_ENUM_CAST(CollisionObject3D::DisableMode); + #endif // COLLISION_OBJECT__H diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 2301fef651..6dc865ec0e 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -435,27 +435,27 @@ Vector3 CPUParticles3D::get_gravity() const { void CPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1273,11 +1273,11 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); @@ -1390,7 +1390,7 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); ADD_GROUP("Scale", ""); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index d7f4bfeb4e..f78027e6c7 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -385,7 +385,7 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name.begins_with("draw_pass_")) { int index = property.name.get_slicec('_', 2).to_int() - 1; if (index >= draw_passes.size()) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } @@ -562,12 +562,12 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 322bc60fce..c1e71b9565 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -495,7 +495,7 @@ Ref<Image> GPUParticlesCollisionSDF::bake() { _compute_sdf(¶ms); Ref<Image> ret; - ret.instance(); + ret.instantiate(); ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, data); ret->convert(Image::FORMAT_RH); //convert to half, save space ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function diff --git a/scene/3d/immediate_geometry_3d.cpp b/scene/3d/immediate_geometry_3d.cpp deleted file mode 100644 index d64babaa9d..0000000000 --- a/scene/3d/immediate_geometry_3d.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************/ -/* immediate_geometry_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "immediate_geometry_3d.h" - -void ImmediateGeometry3D::begin(Mesh::PrimitiveType p_primitive, const Ref<Texture2D> &p_texture) { - RS::get_singleton()->immediate_begin(im, (RS::PrimitiveType)p_primitive, p_texture.is_valid() ? p_texture->get_rid() : RID()); - if (p_texture.is_valid()) { - cached_textures.push_back(p_texture); - } -} - -void ImmediateGeometry3D::set_normal(const Vector3 &p_normal) { - RS::get_singleton()->immediate_normal(im, p_normal); -} - -void ImmediateGeometry3D::set_tangent(const Plane &p_tangent) { - RS::get_singleton()->immediate_tangent(im, p_tangent); -} - -void ImmediateGeometry3D::set_color(const Color &p_color) { - RS::get_singleton()->immediate_color(im, p_color); -} - -void ImmediateGeometry3D::set_uv(const Vector2 &p_uv) { - RS::get_singleton()->immediate_uv(im, p_uv); -} - -void ImmediateGeometry3D::set_uv2(const Vector2 &p_uv2) { - RS::get_singleton()->immediate_uv2(im, p_uv2); -} - -void ImmediateGeometry3D::add_vertex(const Vector3 &p_vertex) { - RS::get_singleton()->immediate_vertex(im, p_vertex); - if (empty) { - aabb.position = p_vertex; - aabb.size = Vector3(); - empty = false; - } else { - aabb.expand_to(p_vertex); - } -} - -void ImmediateGeometry3D::end() { - RS::get_singleton()->immediate_end(im); -} - -void ImmediateGeometry3D::clear() { - RS::get_singleton()->immediate_clear(im); - empty = true; - cached_textures.clear(); -} - -AABB ImmediateGeometry3D::get_aabb() const { - return aabb; -} - -Vector<Face3> ImmediateGeometry3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - -void ImmediateGeometry3D::add_sphere(int p_lats, int p_lons, float p_radius, bool p_add_uv) { - const double lat_step = Math_TAU / p_lats; - const double lon_step = Math_TAU / p_lons; - - for (int i = 1; i <= p_lats; i++) { - double lat0 = lat_step * (i - 1) - Math_TAU / 4; - double z0 = Math::sin(lat0); - double zr0 = Math::cos(lat0); - - double lat1 = lat_step * i - Math_TAU / 4; - double z1 = Math::sin(lat1); - double zr1 = Math::cos(lat1); - - for (int j = p_lons; j >= 1; j--) { - double lng0 = lon_step * (j - 1); - double x0 = Math::cos(lng0); - double y0 = Math::sin(lng0); - - double lng1 = lon_step * j; - double x1 = Math::cos(lng1); - double y1 = Math::sin(lng1); - - Vector3 v[4] = { - Vector3(x1 * zr0, z0, y1 * zr0), - Vector3(x1 * zr1, z1, y1 * zr1), - Vector3(x0 * zr1, z1, y0 * zr1), - Vector3(x0 * zr0, z0, y0 * zr0) - }; - -#define ADD_POINT(m_idx) \ - if (p_add_uv) { \ - set_uv(Vector2(Math::atan2(v[m_idx].x, v[m_idx].z) / Math_PI * 0.5 + 0.5, v[m_idx].y * 0.5 + 0.5)); \ - set_tangent(Plane(Vector3(-v[m_idx].z, v[m_idx].y, v[m_idx].x), 1)); \ - } \ - set_normal(v[m_idx]); \ - add_vertex(v[m_idx] * p_radius); - - ADD_POINT(0); - ADD_POINT(1); - ADD_POINT(2); - - ADD_POINT(2); - ADD_POINT(3); - ADD_POINT(0); - } - } -} - -void ImmediateGeometry3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("begin", "primitive", "texture"), &ImmediateGeometry3D::begin, DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("set_normal", "normal"), &ImmediateGeometry3D::set_normal); - ClassDB::bind_method(D_METHOD("set_tangent", "tangent"), &ImmediateGeometry3D::set_tangent); - ClassDB::bind_method(D_METHOD("set_color", "color"), &ImmediateGeometry3D::set_color); - ClassDB::bind_method(D_METHOD("set_uv", "uv"), &ImmediateGeometry3D::set_uv); - ClassDB::bind_method(D_METHOD("set_uv2", "uv"), &ImmediateGeometry3D::set_uv2); - ClassDB::bind_method(D_METHOD("add_vertex", "position"), &ImmediateGeometry3D::add_vertex); - ClassDB::bind_method(D_METHOD("add_sphere", "lats", "lons", "radius", "add_uv"), &ImmediateGeometry3D::add_sphere, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("end"), &ImmediateGeometry3D::end); - ClassDB::bind_method(D_METHOD("clear"), &ImmediateGeometry3D::clear); -} - -ImmediateGeometry3D::ImmediateGeometry3D() { - im = RenderingServer::get_singleton()->immediate_create(); - set_base(im); -} - -ImmediateGeometry3D::~ImmediateGeometry3D() { - RenderingServer::get_singleton()->free(im); -} diff --git a/scene/3d/immediate_geometry_3d.h b/scene/3d/immediate_geometry_3d.h deleted file mode 100644 index ee4938d9f7..0000000000 --- a/scene/3d/immediate_geometry_3d.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************/ -/* immediate_geometry_3d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef IMMEDIATE_GEOMETRY_3D_H -#define IMMEDIATE_GEOMETRY_3D_H - -#include "scene/3d/visual_instance_3d.h" -#include "scene/resources/mesh.h" - -class ImmediateGeometry3D : public GeometryInstance3D { - GDCLASS(ImmediateGeometry3D, GeometryInstance3D); - - RID im; - //a list of textures drawn need to be kept, to avoid references - // in RenderingServer from becoming invalid if the texture is no longer used - List<Ref<Texture2D>> cached_textures; - bool empty = true; - AABB aabb; - -protected: - static void _bind_methods(); - -public: - void begin(Mesh::PrimitiveType p_primitive, const Ref<Texture2D> &p_texture = Ref<Texture2D>()); - void set_normal(const Vector3 &p_normal); - void set_tangent(const Plane &p_tangent); - void set_color(const Color &p_color); - void set_uv(const Vector2 &p_uv); - void set_uv2(const Vector2 &p_uv2); - - void add_vertex(const Vector3 &p_vertex); - - void end(); - void clear(); - - void add_sphere(int p_lats, int p_lons, float p_radius, bool p_add_uv = true); - - virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - - ImmediateGeometry3D(); - ~ImmediateGeometry3D(); -}; - -#endif // IMMEDIATE_GEOMETRY_H diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index d45749d36b..8478821ba1 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -209,19 +209,19 @@ void Light3D::_validate_property(PropertyInfo &property) const { } if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_size") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_specular") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -353,7 +353,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { } Light3D::Light3D() { - ERR_PRINT("Light3D should not be instanced directly; use the DirectionalLight3D, OmniLight3D or SpotLight3D subtypes instead."); + ERR_PRINT("Light3D should not be instantiated directly; use the DirectionalLight3D, OmniLight3D or SpotLight3D subtypes instead."); } Light3D::~Light3D() { @@ -375,15 +375,6 @@ DirectionalLight3D::ShadowMode DirectionalLight3D::get_shadow_mode() const { return shadow_mode; } -void DirectionalLight3D::set_shadow_depth_range(ShadowDepthRange p_range) { - shadow_depth_range = p_range; - RS::get_singleton()->light_directional_set_shadow_depth_range_mode(light, RS::LightDirectionalShadowDepthRangeMode(p_range)); -} - -DirectionalLight3D::ShadowDepthRange DirectionalLight3D::get_shadow_depth_range() const { - return shadow_depth_range; -} - void DirectionalLight3D::set_blend_splits(bool p_enable) { blend_splits = p_enable; RS::get_singleton()->light_directional_set_blend_splits(light, p_enable); @@ -406,9 +397,6 @@ void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight3D::set_shadow_mode); ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight3D::get_shadow_mode); - ClassDB::bind_method(D_METHOD("set_shadow_depth_range", "mode"), &DirectionalLight3D::set_shadow_depth_range); - ClassDB::bind_method(D_METHOD("get_shadow_depth_range"), &DirectionalLight3D::get_shadow_depth_range); - ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits); ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled); @@ -422,18 +410,14 @@ void DirectionalLight3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_EXP_RANGE, "0,1024,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,8192,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_RANGE, "0,1024,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only"); BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS); - - BIND_ENUM_CONSTANT(SHADOW_DEPTH_RANGE_STABLE); - BIND_ENUM_CONSTANT(SHADOW_DEPTH_RANGE_OPTIMIZED); } DirectionalLight3D::DirectionalLight3D() : @@ -444,7 +428,6 @@ DirectionalLight3D::DirectionalLight3D() : // Leave normal bias untouched as it doesn't benefit DirectionalLight3D as much as OmniLight3D. set_param(PARAM_SHADOW_BIAS, 0.05); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); - set_shadow_depth_range(SHADOW_DEPTH_RANGE_STABLE); blend_splits = false; } @@ -472,7 +455,7 @@ void OmniLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shadow_mode"), &OmniLight3D::get_shadow_mode); ADD_GROUP("Omni", "omni_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode"); @@ -504,8 +487,8 @@ TypedArray<String> SpotLight3D::get_configuration_warnings() const { void SpotLight3D::_bind_methods() { ADD_GROUP("Spot", "spot_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_param", "get_param", PARAM_SPOT_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index e145b08b74..d0308a3025 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -149,15 +149,9 @@ public: SHADOW_PARALLEL_4_SPLITS, }; - enum ShadowDepthRange { - SHADOW_DEPTH_RANGE_STABLE = RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE, - SHADOW_DEPTH_RANGE_OPTIMIZED = RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED, - }; - private: bool blend_splits; ShadowMode shadow_mode; - ShadowDepthRange shadow_depth_range; bool sky_only = false; protected: @@ -167,9 +161,6 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - void set_shadow_depth_range(ShadowDepthRange p_range); - ShadowDepthRange get_shadow_depth_range() const; - void set_blend_splits(bool p_enable); bool is_blend_splits_enabled() const; @@ -180,7 +171,6 @@ public: }; VARIANT_ENUM_CAST(DirectionalLight3D::ShadowMode) -VARIANT_ENUM_CAST(DirectionalLight3D::ShadowDepthRange) class OmniLight3D : public Light3D { GDCLASS(OmniLight3D, Light3D); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index a3f681e53c..66e3535fc4 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -717,7 +717,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa w_albedo[i + 3] = 255; } - md.albedo_on_uv2.instance(); + md.albedo_on_uv2.instantiate(); md.albedo_on_uv2->create(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); } @@ -940,7 +940,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } break; case ENVIRONMENT_MODE_CUSTOM_COLOR: { - environment_image.instance(); + environment_image.instantiate(); environment_image->create(128, 64, false, Image::FORMAT_RGBAF); Color c = environment_custom_color; c.r *= environment_custom_energy; @@ -972,7 +972,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } //we assume they are all the same, so let's create a large one for saving Ref<Image> large_image; - large_image.instance(); + large_image.instantiate(); large_image->create(images[0]->get_width(), images[0]->get_height() * images.size(), false, images[0]->get_format()); @@ -984,7 +984,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Ref<ConfigFile> config; - config.instance(); + config.instantiate(); if (FileAccess::exists(base_path + ".import")) { config->load(base_path + ".import"); } @@ -1017,7 +1017,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa set_light_data(Ref<LightmapGIData>()); //clear data->clear(); } else { - data.instance(); + data.instantiate(); } data->set_light_texture(texture); @@ -1367,13 +1367,13 @@ LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { void LightmapGI::_validate_property(PropertyInfo &property) const { if (property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "environment_custom_color" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index c495f68890..279a1fb7de 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -100,7 +100,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { if (mesh.is_valid()) { for (int i = 0; i < mesh->get_surface_count(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } } @@ -405,14 +405,14 @@ void MeshInstance3D::create_debug_tangents() { if (lines.size()) { Ref<StandardMaterial3D> sm; - sm.instance(); + sm.instantiate(); sm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); sm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); sm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); Ref<ArrayMesh> am; - am.instance(); + am.instantiate(); Array a; a.resize(Mesh::ARRAY_MAX); a[Mesh::ARRAY_VERTEX] = lines; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index f78a2ff14e..d6c6ec75b4 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -328,10 +328,6 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { } } -void Node3D::set_rotation_degrees(const Vector3 &p_euler_deg) { - set_rotation(p_euler_deg * (Math_PI / 180.0)); -} - void Node3D::set_scale(const Vector3 &p_scale) { if (data.dirty & DIRTY_VECTORS) { data.rotation = data.local_transform.basis.get_rotation(); @@ -361,10 +357,6 @@ Vector3 Node3D::get_rotation() const { return data.rotation; } -Vector3 Node3D::get_rotation_degrees() const { - return get_rotation() * (180.0 / Math_PI); -} - Vector3 Node3D::get_scale() const { if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); @@ -749,8 +741,6 @@ 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_degrees", "euler_degrees"), &Node3D::set_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node3D::get_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node3D::set_scale); ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale); ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); @@ -814,9 +804,8 @@ void Node3D::_bind_methods() { //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_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); ADD_GROUP("Matrix", ""); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index c7e36cf2ec..fe6324c796 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -131,12 +131,10 @@ public: void set_position(const Vector3 &p_position); void set_rotation(const Vector3 &p_euler_rad); - void set_rotation_degrees(const Vector3 &p_euler_deg); void set_scale(const Vector3 &p_scale); Vector3 get_position() const; Vector3 get_rotation() const; - Vector3 get_rotation_degrees() const; Vector3 get_scale() const; void set_transform(const Transform3D &p_transform); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 429e1d4b98..7b736e689c 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -114,7 +114,7 @@ Ref<ArrayMesh> Occluder3D::get_debug_mesh() const { arrays[Mesh::ARRAY_VERTEX] = vertices; arrays[Mesh::ARRAY_INDEX] = indices; - debug_mesh.instance(); + debug_mesh.instantiate(); debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); return debug_mesh; } @@ -174,10 +174,12 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { } update_gizmo(); + update_configuration_warnings(); } void OccluderInstance3D::_occluder_changed() { update_gizmo(); + update_configuration_warnings(); } Ref<Occluder3D> OccluderInstance3D::get_occluder() const { @@ -186,6 +188,7 @@ Ref<Occluder3D> OccluderInstance3D::get_occluder() const { void OccluderInstance3D::set_bake_mask(uint32_t p_mask) { bake_mask = p_mask; + update_configuration_warnings(); } uint32_t OccluderInstance3D::get_bake_mask() const { @@ -303,7 +306,7 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String if (get_occluder().is_valid()) { occ = get_occluder(); } else { - occ.instance(); + occ.instantiate(); occ->set_path(p_occluder_path); } @@ -314,6 +317,31 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String return BAKE_ERROR_OK; } +TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { + warnings.push_back(TTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling.")); + } + + if (bake_mask == 0) { + // NOTE: This warning will not be emitted if none of the 20 checkboxes + // exposed in the editor are checked. This is because there are + // currently 12 unexposed layers in the editor inspector. + warnings.push_back(TTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); + } + + if (occluder.is_null()) { + warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); + } else if (occluder->get_vertices().size() < 3) { + // Using the "New Occluder" dropdown button won't result in a correct occluder, + // so warn the user about this. + warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); + } + + return warnings; +} + void OccluderInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 4bb468274d..d382cd090e 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -82,6 +82,8 @@ protected: static void _bind_methods(); public: + virtual TypedArray<String> get_configuration_warnings() const override; + enum BakeError { BAKE_ERROR_OK, BAKE_ERROR_NO_SAVE_PATH, diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 28a0c72fe3..e7482d35e7 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -65,7 +65,7 @@ void PhysicsBody3D::_bind_methods() { PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) : CollisionObject3D(PhysicsServer3D::get_singleton()->body_create(), false) { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), p_mode); + set_body_mode(p_mode); } PhysicsBody3D::~PhysicsBody3D() { @@ -106,7 +106,7 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_i PhysicsServer3D::MotionResult result; if (move_and_collide(p_motion, p_infinite_inertia, result, p_margin, p_exclude_raycast_shapes, p_test_only)) { if (motion_cache.is_null()) { - motion_cache.instance(); + motion_cache.instantiate(); motion_cache->owner = this; } @@ -118,10 +118,43 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_i return Ref<KinematicCollision3D>(); } -bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only) { +bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) { Transform3D gt = get_global_transform(); bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes); + // Restore direction of motion to be along original motion, + // in order to avoid sliding due to recovery, + // but only if collision depth is low enough to avoid tunneling. + real_t motion_length = p_motion.length(); + if (motion_length > CMP_EPSILON) { + real_t precision = CMP_EPSILON; + + if (colliding && p_cancel_sliding) { + // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, + // so even in normal resting cases the depth can be a bit more than the margin. + precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); + + if (r_result.collision_depth > (real_t)p_margin + precision) { + p_cancel_sliding = false; + } + } + + if (p_cancel_sliding) { + // Check depth of recovery. + Vector3 motion_normal = p_motion / motion_length; + real_t dot = r_result.motion.dot(motion_normal); + Vector3 recovery = r_result.motion - motion_normal * dot; + real_t recovery_length = recovery.length(); + // Fixes cases where canceling slide causes the motion to go too deep into the ground, + // Becauses we're only taking rest information into account and not general recovery. + if (recovery_length < (real_t)p_margin + precision) { + // Apply adjustment to motion. + r_result.motion = motion_normal * dot; + r_result.remainder = p_motion - r_result.motion; + } + } + } + for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { r_result.motion[i] = 0; @@ -200,9 +233,9 @@ void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) { kinematic_motion = p_enabled; if (kinematic_motion) { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); } else { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC); + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); } _update_kinematic_motion(); @@ -249,35 +282,37 @@ Vector3 StaticBody3D::get_angular_velocity() const { } void StaticBody3D::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + switch (p_what) { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } + if (Engine::get_singleton()->is_editor_hint()) { + return; + } #endif - ERR_FAIL_COND(!kinematic_motion); + ERR_FAIL_COND(!kinematic_motion); - real_t delta_time = get_physics_process_delta_time(); + real_t delta_time = get_physics_process_delta_time(); - Transform3D new_transform = get_global_transform(); - new_transform.origin += constant_linear_velocity * delta_time; + Transform3D new_transform = get_global_transform(); + new_transform.origin += constant_linear_velocity * delta_time; - real_t ang_vel = constant_angular_velocity.length(); - if (!Math::is_zero_approx(ang_vel)) { - Vector3 ang_vel_axis = constant_angular_velocity / ang_vel; - Basis rot(ang_vel_axis, ang_vel * delta_time); - new_transform.basis = rot * new_transform.basis; - new_transform.orthonormalize(); - } + real_t ang_vel = constant_angular_velocity.length(); + if (!Math::is_zero_approx(ang_vel)) { + Vector3 ang_vel_axis = constant_angular_velocity / ang_vel; + Basis rot(ang_vel_axis, ang_vel * delta_time); + new_transform.basis = rot * new_transform.basis; + new_transform.orthonormalize(); + } - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); - // Propagate transform change to node. - set_ignore_transform_notification(true); - set_global_transform(new_transform); - set_ignore_transform_notification(false); - _on_transform_changed(); + // Propagate transform change to node. + set_ignore_transform_notification(true); + set_global_transform(new_transform); + set_ignore_transform_notification(false); + _on_transform_changed(); + } break; } } @@ -565,18 +600,19 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { void RigidBody3D::_notification(int p_what) { #ifdef TOOLS_ENABLED - if (p_what == NOTIFICATION_ENTER_TREE) { - if (Engine::get_singleton()->is_editor_hint()) { - set_notify_local_transform(true); //used for warnings and only in editor - } - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + set_notify_local_transform(true); //used for warnings and only in editor + } + } break; - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; } - #endif } @@ -584,18 +620,16 @@ void RigidBody3D::set_mode(Mode p_mode) { mode = p_mode; switch (p_mode) { case MODE_DYNAMIC: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); } break; case MODE_STATIC: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC); - + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); } break; case MODE_DYNAMIC_LOCKED: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); - + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); } break; case MODE_KINEMATIC: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); } break; } update_configuration_warnings(); @@ -902,7 +936,7 @@ void RigidBody3D::_bind_methods() { BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState3D"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -978,15 +1012,16 @@ void CharacterBody3D::move_and_slide() { floor_normal = Vector3(); floor_velocity = Vector3(); - int slide_count = max_slides; - while (slide_count) { + // No sliding on first attempt to keep motion stable when possible. + bool sliding_enabled = false; + for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionResult result; bool found_collision = false; for (int i = 0; i < 2; ++i) { bool collided; if (i == 0) { //collide - collided = move_and_collide(motion, infinite_inertia, result, margin); + collided = move_and_collide(motion, infinite_inertia, result, margin, true, false, !sliding_enabled); if (!collided) { motion = Vector3(); //clear because no collision happened and motion completed } @@ -1002,7 +1037,6 @@ void CharacterBody3D::move_and_slide() { found_collision = true; motion_results.push_back(result); - motion = result.remainder; if (up_direction == Vector3()) { //all is a wall @@ -1016,7 +1050,7 @@ void CharacterBody3D::move_and_slide() { floor_velocity = result.collider_velocity; if (stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) { + if ((body_velocity_normal + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); gt.origin -= result.motion.slide(up_direction); set_global_transform(gt); @@ -1031,22 +1065,26 @@ void CharacterBody3D::move_and_slide() { } } - motion = motion.slide(result.collision_normal); - linear_velocity = linear_velocity.slide(result.collision_normal); + if (sliding_enabled || !on_floor) { + motion = result.remainder.slide(result.collision_normal); + linear_velocity = linear_velocity.slide(result.collision_normal); - for (int j = 0; j < 3; j++) { - if (locked_axis & (1 << j)) { - linear_velocity[j] = 0.0; + for (int j = 0; j < 3; j++) { + if (locked_axis & (1 << j)) { + linear_velocity[j] = 0.0; + } } + } else { + motion = result.remainder; } } + + sliding_enabled = true; } if (!found_collision || motion == Vector3()) { break; } - - --slide_count; } if (!was_on_floor || snap == Vector3()) { @@ -1168,7 +1206,7 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1208,14 +1246,6 @@ void CharacterBody3D::set_floor_max_angle(real_t p_radians) { floor_max_angle = p_radians; } -real_t CharacterBody3D::get_floor_max_angle_degrees() const { - return Math::rad2deg(floor_max_angle); -} - -void CharacterBody3D::set_floor_max_angle_degrees(real_t p_degrees) { - floor_max_angle = Math::deg2rad(p_degrees); -} - const Vector3 &CharacterBody3D::get_snap() const { return snap; } @@ -1233,14 +1263,16 @@ void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) { } void CharacterBody3D::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - // Reset move_and_slide() data. - on_floor = false; - on_floor_body = RID(); - on_ceiling = false; - on_wall = false; - motion_results.clear(); - floor_velocity = Vector3(); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + motion_results.clear(); + floor_velocity = Vector3(); + } break; } } @@ -1260,8 +1292,6 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides); ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody3D::get_floor_max_angle); ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody3D::set_floor_max_angle); - ClassDB::bind_method(D_METHOD("get_floor_max_angle_degrees"), &CharacterBody3D::get_floor_max_angle_degrees); - ClassDB::bind_method(D_METHOD("set_floor_max_angle_degrees", "degrees"), &CharacterBody3D::set_floor_max_angle_degrees); ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody3D::get_snap); ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody3D::set_snap); ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody3D::get_up_direction); @@ -1280,8 +1310,7 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle_degrees", PROPERTY_HINT_RANGE, "0,180,0.1", PROPERTY_USAGE_EDITOR), "set_floor_max_angle_degrees", "get_floor_max_angle_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); @@ -2095,7 +2124,8 @@ void PhysicalBone3D::_notification(int p_what) { _reload_joint(); } break; - case NOTIFICATION_EXIT_TREE: + + case NOTIFICATION_EXIT_TREE: { if (parent_skeleton) { if (-1 != bone_id) { parent_skeleton->unbind_physical_bone_from_bone(bone_id); @@ -2105,12 +2135,13 @@ void PhysicalBone3D::_notification(int p_what) { } parent_skeleton = nullptr; PhysicsServer3D::get_singleton()->joint_clear(joint); - break; - case NOTIFICATION_TRANSFORM_CHANGED: + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { if (Engine::get_singleton()->is_editor_hint()) { update_offset(); } - break; + } break; } } @@ -2156,8 +2187,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone3D::get_joint_offset); ClassDB::bind_method(D_METHOD("set_joint_rotation", "euler"), &PhysicalBone3D::set_joint_rotation); ClassDB::bind_method(D_METHOD("get_joint_rotation"), &PhysicalBone3D::get_joint_rotation); - ClassDB::bind_method(D_METHOD("set_joint_rotation_degrees", "euler_degrees"), &PhysicalBone3D::set_joint_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_joint_rotation_degrees"), &PhysicalBone3D::get_joint_rotation_degrees); ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone3D::set_body_offset); ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone3D::get_body_offset); @@ -2192,12 +2221,11 @@ void PhysicalBone3D::_bind_methods() { ADD_GROUP("Joint", "joint_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset"), "set_joint_offset", "get_joint_offset"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_joint_rotation_degrees", "get_joint_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_joint_rotation", "get_joint_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_RANGE, "-360,360,0.01,or_lesser,or_greater,radians"), "set_joint_rotation", "get_joint_rotation"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset"), "set_body_offset", "get_body_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); @@ -2428,14 +2456,6 @@ Vector3 PhysicalBone3D::get_joint_rotation() const { return joint_offset.basis.get_rotation(); } -void PhysicalBone3D::set_joint_rotation_degrees(const Vector3 &p_euler_deg) { - set_joint_rotation(p_euler_deg * (Math_PI / 180.0)); -} - -Vector3 PhysicalBone3D::get_joint_rotation_degrees() const { - return get_joint_rotation() * (180.0 / Math_PI); -} - const Transform3D &PhysicalBone3D::get_body_offset() const { return body_offset; } @@ -2605,7 +2625,7 @@ void PhysicalBone3D::_start_physics_simulation() { return; } reset_to_rest_position(); - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); @@ -2618,11 +2638,11 @@ void PhysicalBone3D::_stop_physics_simulation() { return; } if (parent_skeleton->get_animate_physical_bones()) { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC); + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); } else { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC); + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 7d7adf1624..9d45ce3799 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -53,7 +53,7 @@ protected: Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.001); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true); bool test_move(const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); @@ -307,9 +307,6 @@ private: real_t get_floor_max_angle() const; void set_floor_max_angle(real_t p_radians); - real_t get_floor_max_angle_degrees() const; - void set_floor_max_angle_degrees(real_t p_degrees); - const Vector3 &get_snap() const; void set_snap(const Vector3 &p_snap); @@ -551,9 +548,6 @@ public: void set_joint_rotation(const Vector3 &p_euler_rad); Vector3 get_joint_rotation() const; - void set_joint_rotation_degrees(const Vector3 &p_euler_deg); - Vector3 get_joint_rotation_degrees() const; - void set_body_offset(const Transform3D &p_offset); const Transform3D &get_body_offset() const; diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index db841101e5..dfab3d4a17 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -419,6 +419,8 @@ void RayCast3D::_update_debug_shape_material(bool p_check_collision) { debug_material = material; material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + // Use double-sided rendering so that the RayCast can be seen if the camera is inside. + material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); } diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index 7762156b4a..c289fc4fd7 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -232,7 +232,7 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1,or_greater,exp"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled"); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 15c2da3ae0..4bf20c89c5 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -55,7 +55,7 @@ private: RID probe; float intensity = 1.0; float max_distance = 0.0; - Vector3 extents = Vector3(1, 1, 1); + Vector3 extents = Vector3(10, 10, 10); Vector3 origin_offset = Vector3(0, 0, 0); bool box_projection = false; bool enable_shadows = false; diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index a7b3a6f1ec..d5fb1fa6ab 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -34,7 +34,7 @@ void RemoteTransform3D::_update_cache() { cache = ObjectID(); if (has_node(remote_node)) { Node *node = get_node(remote_node); - if (!node || this == node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + if (!node || this == node || node->is_ancestor_of(this) || this->is_ancestor_of(node)) { return; } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index f9d613a4bb..fa3b16935c 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -797,7 +797,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { //when skeletons did not support skins. It is also used by gizmo //to display the skeleton. - skin.instance(); + skin.instantiate(); skin->set_bind_count(bones.size()); _update_process_order(); //just in case @@ -826,7 +826,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); Ref<SkinReference> skin_ref; - skin_ref.instance(); + skin_ref.instantiate(); skin_ref->skeleton_node = this; skin_ref->bind_count = 0; diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index df5474d03e..efd8a2b50c 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -81,7 +81,7 @@ void SoftBodyRenderingServerHandler::close() { } void SoftBodyRenderingServerHandler::commit_changes() { - RS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, surface, 0, buffer); } void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { @@ -266,12 +266,13 @@ void SoftBody3D::_notification(int p_what) { PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, space); prepare_physics_server(); } break; + case NOTIFICATION_READY: { if (!parent_collision_ignore.is_empty()) { add_collision_exception_with(get_node(parent_collision_ignore)); } - } break; + case NOTIFICATION_TRANSFORM_CHANGED: { if (Engine::get_singleton()->is_editor_hint()) { _reset_points_offsets(); @@ -285,27 +286,36 @@ void SoftBody3D::_notification(int p_what) { set_as_top_level(true); set_transform(Transform3D()); set_notify_transform(true); - } break; + case NOTIFICATION_VISIBILITY_CHANGED: { _update_pickable(); - } break; + case NOTIFICATION_EXIT_WORLD: { PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, RID()); - } break; - } -#ifdef TOOLS_ENABLED + case NOTIFICATION_DISABLED: { + if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } + } break; - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } - } + case NOTIFICATION_ENABLED: { + if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } + } break; +#ifdef TOOLS_ENABLED + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; #endif + } } void SoftBody3D::_bind_methods() { @@ -326,6 +336,9 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore); ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftBody3D::set_disable_mode); + ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftBody3D::get_disable_mode); + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody3D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody3D::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody3D::remove_collision_exception_with); @@ -364,6 +377,11 @@ void SoftBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ray_pickable"), "set_ray_pickable", "is_ray_pickable"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,KeepActive"), "set_disable_mode", "get_disable_mode"); + + BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE); + BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } TypedArray<String> SoftBody3D::get_configuration_warnings() const { @@ -421,6 +439,7 @@ void SoftBody3D::_draw_soft_mesh() { } void SoftBody3D::prepare_physics_server() { +#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { if (get_mesh().is_valid()) { PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); @@ -430,8 +449,9 @@ void SoftBody3D::prepare_physics_server() { return; } +#endif - if (get_mesh().is_valid()) { + if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) { become_mesh_owner(); PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh)); @@ -465,7 +485,7 @@ void SoftBody3D::become_mesh_owner() { surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; Ref<ArrayMesh> soft_mesh; - soft_mesh.instance(); + soft_mesh.instantiate(); soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format); soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); @@ -527,6 +547,28 @@ bool SoftBody3D::get_collision_layer_bit(int p_bit) const { return get_collision_layer() & (1 << p_bit); } +void SoftBody3D::set_disable_mode(DisableMode p_mode) { + if (disable_mode == p_mode) { + return; + } + + bool inside_tree = is_inside_tree(); + + if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } + + disable_mode = p_mode; + + if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } +} + +SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const { + return disable_mode; +} + void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { parent_collision_ignore = p_parent_collision_ignore; } diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index 0d0d39d48f..81aa0c10c6 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -67,6 +67,11 @@ class SoftBody3D : public MeshInstance3D { GDCLASS(SoftBody3D, MeshInstance3D); public: + enum DisableMode { + DISABLE_MODE_REMOVE, + DISABLE_MODE_KEEP_ACTIVE, + }; + struct PinnedPoint { int point_index = -1; NodePath spatial_attachment_path; @@ -83,6 +88,8 @@ private: RID physics_rid; + DisableMode disable_mode = DISABLE_MODE_REMOVE; + bool mesh_owner = false; uint32_t collision_mask = 1; uint32_t collision_layer = 1; @@ -137,6 +144,9 @@ public: void set_collision_layer_bit(int p_bit, bool p_value); bool get_collision_layer_bit(int p_bit) const; + void set_disable_mode(DisableMode p_mode); + DisableMode get_disable_mode() const; + void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore); const NodePath &get_parent_collision_ignore() const; @@ -192,4 +202,6 @@ private: int _has_pinned_point(int p_point_index) const; }; +VARIANT_ENUM_CAST(SoftBody3D::DisableMode); + #endif // SOFT_PHYSICS_BODY_H diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 9ec461d973..13f8002721 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -164,6 +164,8 @@ void SpriteBase3D::_im_update() { _draw(); pending_update = false; + + //texture->draw_rect_region(ci,dst_rect,src_rect,modulate); } void SpriteBase3D::_queue_update() { @@ -204,6 +206,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { float pixel_size = get_pixel_size(); Vector2 vertices[4] = { + (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, (final_rect.position + final_rect.size) * pixel_size, (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, @@ -313,6 +316,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); + ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update); + ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h"); @@ -343,21 +349,89 @@ SpriteBase3D::SpriteBase3D() { flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; } - immediate = RenderingServer::get_singleton()->immediate_create(); - set_base(immediate); + material = RenderingServer::get_singleton()->material_create(); + // Set defaults for material, names need to match up those in StandardMaterial3D + RS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1)); + RS::get_singleton()->material_set_param(material, "specular", 0.5); + RS::get_singleton()->material_set_param(material, "metallic", 0.0); + RS::get_singleton()->material_set_param(material, "roughness", 1.0); + RS::get_singleton()->material_set_param(material, "uv1_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(material, "uv1_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(material, "uv2_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(material, "uv2_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(material, "alpha_scissor_threshold", 0.98); + + mesh = RenderingServer::get_singleton()->mesh_create(); + + PackedVector3Array mesh_vertices; + PackedVector3Array mesh_normals; + PackedFloat32Array mesh_tangents; + PackedColorArray mesh_colors; + PackedVector2Array mesh_uvs; + PackedInt32Array indices; + + mesh_vertices.resize(4); + mesh_normals.resize(4); + mesh_tangents.resize(16); + mesh_colors.resize(4); + mesh_uvs.resize(4); + + // create basic mesh and store format information + for (int i = 0; i < 4; i++) { + mesh_normals.write[i] = Vector3(0.0, 0.0, 0.0); + mesh_tangents.write[i * 4 + 0] = 0.0; + mesh_tangents.write[i * 4 + 1] = 0.0; + mesh_tangents.write[i * 4 + 2] = 0.0; + mesh_tangents.write[i * 4 + 3] = 0.0; + mesh_colors.write[i] = Color(1.0, 1.0, 1.0, 1.0); + mesh_uvs.write[i] = Vector2(0.0, 0.0); + mesh_vertices.write[i] = Vector3(0.0, 0.0, 0.0); + } + + indices.resize(6); + indices.write[0] = 0; + indices.write[1] = 1; + indices.write[2] = 2; + indices.write[3] = 0; + indices.write[4] = 2; + indices.write[5] = 3; + + Array mesh_array; + mesh_array.resize(RS::ARRAY_MAX); + mesh_array[RS::ARRAY_VERTEX] = mesh_vertices; + mesh_array[RS::ARRAY_NORMAL] = mesh_normals; + mesh_array[RS::ARRAY_TANGENT] = mesh_tangents; + mesh_array[RS::ARRAY_COLOR] = mesh_colors; + mesh_array[RS::ARRAY_TEX_UV] = mesh_uvs; + mesh_array[RS::ARRAY_INDEX] = indices; + + RS::SurfaceData sd; + RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); + + mesh_surface_format = sd.format; + vertex_buffer = sd.vertex_data; + attribute_buffer = sd.attribute_data; + + sd.material = material; + + RS::get_singleton()->mesh_surface_make_offsets_from_format(sd.format, sd.vertex_count, sd.index_count, mesh_surface_offsets, vertex_stride, attrib_stride, skin_stride); + RS::get_singleton()->mesh_add_surface(mesh, sd); + set_base(mesh); } SpriteBase3D::~SpriteBase3D() { - RenderingServer::get_singleton()->free(immediate); + RenderingServer::get_singleton()->free(mesh); + RenderingServer::get_singleton()->free(material); } /////////////////////////////////////////// void Sprite3D::_draw() { - RID immediate = get_immediate(); - - RS::get_singleton()->immediate_clear(immediate); + if (get_base() != get_mesh()) { + set_base(get_mesh()); + } if (!texture.is_valid()) { + set_base(RID()); return; } Vector2 tsize = texture->get_size(); @@ -366,7 +440,7 @@ void Sprite3D::_draw() { } Rect2 base_rect; - if (region_enabled) { + if (region) { base_rect = region_rect; } else { base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); @@ -399,6 +473,7 @@ void Sprite3D::_draw() { float pixel_size = get_pixel_size(); Vector2 vertices[4] = { + (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, (final_rect.position + final_rect.size) * pixel_size, (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, @@ -426,6 +501,7 @@ void Sprite3D::_draw() { SWAP(uvs[0], uvs[1]); SWAP(uvs[2], uvs[3]); } + if (is_flipped_v()) { SWAP(uvs[0], uvs[3]); SWAP(uvs[1], uvs[2]); @@ -442,11 +518,6 @@ void Sprite3D::_draw() { tangent = Plane(1, 0, 0, 1); } - RID mat = StandardMaterial3D::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y); - RS::get_singleton()->immediate_set_material(immediate, mat); - - RS::get_singleton()->immediate_begin(immediate, RS::PRIMITIVE_TRIANGLES, texture->get_rid()); - int x_axis = ((axis + 1) % 3); int y_axis = ((axis + 2) % 3); @@ -466,31 +537,80 @@ void Sprite3D::_draw() { AABB aabb; - for (int i = 0; i < 6; i++) { - static const int index[6] = { 0, 1, 2, 0, 2, 3 }; + // Everything except position and UV is compressed + uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); + uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); + + uint32_t v_normal; + { + Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + v_normal = value; + } + uint32_t v_tangent; + { + Plane t = tangent; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + v_tangent = value; + } - RS::get_singleton()->immediate_normal(immediate, normal); - RS::get_singleton()->immediate_tangent(immediate, tangent); - RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[index[i]]); + uint8_t v_color[4] = { + uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) + }; + for (int i = 0; i < 4; i++) { Vector3 vtx; - vtx[x_axis] = vertices[index[i]][0]; - vtx[y_axis] = vertices[index[i]][1]; - RS::get_singleton()->immediate_vertex(immediate, vtx); + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; if (i == 0) { aabb.position = vtx; aabb.size = Vector3(); } else { aabb.expand_to(vtx); } + + float v_uv[2] = { uvs[i].x, uvs[i].y }; + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); + + float v_vertex[3] = { vtx.x, vtx.y, vtx.z }; + + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); } + + RID mesh = get_mesh(); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); + + RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); set_aabb(aabb); - RS::get_singleton()->immediate_end(immediate); -} -void Sprite3D::_texture_changed() { - _queue_update(); + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + if (last_shader != shader_rid) { + RS::get_singleton()->material_set_shader(get_material(), shader_rid); + last_shader = shader_rid; + } + if (last_texture != texture->get_rid()) { + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); + last_texture = texture->get_rid(); + } } void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { @@ -498,38 +618,36 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { return; } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite3D::_texture_changed)); + texture->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite3D::_texture_changed)); + texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); } _queue_update(); - emit_signal(SceneStringNames::get_singleton()->texture_changed); } Ref<Texture2D> Sprite3D::get_texture() const { return texture; } -void Sprite3D::set_region_enabled(bool p_region_enabled) { - if (p_region_enabled == region_enabled) { +void Sprite3D::set_region_enabled(bool p_region) { + if (p_region == region) { return; } - region_enabled = p_region_enabled; + region = p_region; _queue_update(); - notify_property_list_changed(); } bool Sprite3D::is_region_enabled() const { - return region_enabled; + return region; } void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { bool changed = region_rect != p_region_rect; region_rect = p_region_rect; - if (region_enabled && changed) { + if (region && changed) { _queue_update(); } } @@ -596,7 +714,7 @@ Rect2 Sprite3D::get_item_rect() const { Size2i s; - if (region_enabled) { + if (region) { s = region_rect.size; } else { s = texture->get_size(); @@ -625,10 +743,6 @@ void Sprite3D::_validate_property(PropertyInfo &property) const { if (property.name == "frame_coords") { property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - - if (!region_enabled && property.name == "region_rect") { - property.usage = PROPERTY_USAGE_NOEDITOR; - } } void Sprite3D::_bind_methods() { @@ -653,32 +767,28 @@ void Sprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hframes", "hframes"), &Sprite3D::set_hframes); ClassDB::bind_method(D_METHOD("get_hframes"), &Sprite3D::get_hframes); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_GROUP("Animation", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); ADD_GROUP("Region", "region_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region_enabled", "is_region_enabled"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_SIGNAL(MethodInfo("frame_changed")); - ADD_SIGNAL(MethodInfo("texture_changed")); } Sprite3D::Sprite3D() { - region_enabled = false; - frame = 0; - vframes = 1; - hframes = 1; } //////////////////////////////////////// void AnimatedSprite3D::_draw() { - RID immediate = get_immediate(); - RS::get_singleton()->immediate_clear(immediate); + if (get_base() != get_mesh()) { + set_base(get_mesh()); + } if (frames.is_null()) { return; @@ -694,7 +804,8 @@ void AnimatedSprite3D::_draw() { Ref<Texture2D> texture = frames->get_frame(animation, frame); if (!texture.is_valid()) { - return; //no texture no life + set_base(RID()); + return; //no texuture no life } Vector2 tsize = texture->get_size(); if (tsize.x == 0 || tsize.y == 0) { @@ -729,6 +840,7 @@ void AnimatedSprite3D::_draw() { float pixel_size = get_pixel_size(); Vector2 vertices[4] = { + (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, (final_rect.position + final_rect.size) * pixel_size, (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, @@ -772,12 +884,6 @@ void AnimatedSprite3D::_draw() { tangent = Plane(1, 0, 0, -1); } - RID mat = StandardMaterial3D::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y); - - RS::get_singleton()->immediate_set_material(immediate, mat); - - RS::get_singleton()->immediate_begin(immediate, RS::PRIMITIVE_TRIANGLES, texture->get_rid()); - int x_axis = ((axis + 1) % 3); int y_axis = ((axis + 2) % 3); @@ -797,30 +903,79 @@ void AnimatedSprite3D::_draw() { AABB aabb; - for (int i = 0; i < 6; i++) { - static const int indices[6] = { - 0, 1, 2, - 0, 2, 3 - }; + // Everything except position and UV is compressed + uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); + uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); + + uint32_t v_normal; + { + Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + v_normal = value; + } + uint32_t v_tangent; + { + Plane t = tangent; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + v_tangent = value; + } - RS::get_singleton()->immediate_normal(immediate, normal); - RS::get_singleton()->immediate_tangent(immediate, tangent); - RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[indices[i]]); + uint8_t v_color[4] = { + uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) + }; + for (int i = 0; i < 4; i++) { Vector3 vtx; - vtx[x_axis] = vertices[indices[i]][0]; - vtx[y_axis] = vertices[indices[i]][1]; - RS::get_singleton()->immediate_vertex(immediate, vtx); + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; if (i == 0) { aabb.position = vtx; aabb.size = Vector3(); } else { aabb.expand_to(vtx); } + + float v_uv[2] = { uvs[i].x, uvs[i].y }; + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); + + float v_vertex[3] = { vtx.x, vtx.y, vtx.z }; + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); } + + RID mesh = get_mesh(); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); + + RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); set_aabb(aabb); - RS::get_singleton()->immediate_end(immediate); + + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + if (last_shader != shader_rid) { + RS::get_singleton()->material_set_shader(get_material(), shader_rid); + last_shader = shader_rid; + } + if (last_texture != texture->get_rid()) { + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); + last_texture = texture->get_rid(); + } } void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { @@ -914,11 +1069,11 @@ void AnimatedSprite3D::_notification(int p_what) { void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { if (frames.is_valid()) { - frames->disconnect("changed", callable_mp(this, &AnimatedSprite3D::_res_changed)); + frames->disconnect("changed", Callable(this, "_res_changed")); } frames = p_frames; if (frames.is_valid()) { - frames->connect("changed", callable_mp(this, &AnimatedSprite3D::_res_changed)); + frames->connect("changed", Callable(this, "_res_changed")); } if (!frames.is_valid()) { @@ -944,9 +1099,8 @@ void AnimatedSprite3D::set_frame(int p_frame) { if (frames->has_animation(animation)) { int limit = frames->get_frame_count(animation); - if (p_frame >= limit) { + if (p_frame >= limit) p_frame = limit - 1; - } } if (p_frame < 0) { @@ -960,7 +1114,6 @@ void AnimatedSprite3D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); _queue_update(); - emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -1061,8 +1214,7 @@ StringName AnimatedSprite3D::get_animation() const { } TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); - + TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } @@ -1087,11 +1239,13 @@ void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame); + ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed); + ADD_SIGNAL(MethodInfo("frame_changed")); ADD_SIGNAL(MethodInfo("animation_finished")); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); } diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index e3dd117804..404ef57e6a 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -31,8 +31,8 @@ #ifndef SPRITE_3D_H #define SPRITE_3D_H +#include "scene/2d/animated_sprite_2d.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/sprite_frames.h" class SpriteBase3D : public GeometryInstance3D { GDCLASS(SpriteBase3D, GeometryInstance3D); @@ -75,9 +75,10 @@ private: float pixel_size = 0.01; AABB aabb; - RID immediate; + RID mesh; + RID material; - bool flags[FLAG_MAX]; + bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; bool pending_update = false; @@ -91,7 +92,17 @@ protected: static void _bind_methods(); virtual void _draw() = 0; _FORCE_INLINE_ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; } - _FORCE_INLINE_ RID &get_immediate() { return immediate; } + _FORCE_INLINE_ RID &get_mesh() { return mesh; } + _FORCE_INLINE_ RID &get_material() { return material; } + + uint32_t mesh_surface_offsets[RS::ARRAY_MAX]; + PackedByteArray vertex_buffer; + PackedByteArray attribute_buffer; + uint32_t vertex_stride; + uint32_t attrib_stride; + uint32_t skin_stride; + uint32_t mesh_surface_format; + void _queue_update(); public: @@ -107,7 +118,7 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - void set_region_enabled(bool p_region_enabled); + void set_region_enabled(bool p_region); bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); @@ -147,15 +158,16 @@ class Sprite3D : public SpriteBase3D { GDCLASS(Sprite3D, SpriteBase3D); Ref<Texture2D> texture; - bool region_enabled; + bool region = false; Rect2 region_rect; - int frame; + int frame = 0; - int vframes; - int hframes; + int vframes = 1; + int hframes = 1; - void _texture_changed(); + RID last_shader; + RID last_texture; protected: virtual void _draw() override; @@ -167,7 +179,7 @@ public: void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; - void set_region_enabled(bool p_region_enabled); + void set_region_enabled(bool p_region); bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); @@ -199,14 +211,9 @@ class AnimatedSprite3D : public SpriteBase3D { StringName animation = "default"; int frame = 0; - bool centered = true; - - float timeout = 0.0; - - bool hflip = true; - bool vflip = true; + bool centered = false; - Color modulate; + float timeout = 0; void _res_changed(); @@ -214,6 +221,9 @@ class AnimatedSprite3D : public SpriteBase3D { void _set_playing(bool p_playing); bool _is_playing() const; + RID last_shader; + RID last_texture; + protected: virtual void _draw() override; static void _bind_methods(); @@ -236,10 +246,11 @@ public: virtual Rect2 get_item_rect() const override; - TypedArray<String> get_configuration_warnings() const override; + virtual TypedArray<String> get_configuration_warnings() const override; AnimatedSprite3D(); }; VARIANT_ENUM_CAST(SpriteBase3D::DrawFlags); VARIANT_ENUM_CAST(SpriteBase3D::AlphaCutMode); + #endif // SPRITE_3D_H diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 0d88769b70..b0e37b81a5 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -79,26 +79,29 @@ public: }; void VehicleWheel3D::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent()); - if (!cb) { - return; - } - body = cb; - local_xform = get_transform(); - cb->wheels.push_back(this); - - m_chassisConnectionPointCS = get_transform().origin; - m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); - } - if (p_what == NOTIFICATION_EXIT_TREE) { - VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent()); - if (!cb) { - return; - } - cb->wheels.erase(this); - body = nullptr; + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent()); + if (!cb) { + return; + } + body = cb; + local_xform = get_transform(); + cb->wheels.push_back(this); + + m_chassisConnectionPointCS = get_transform().origin; + m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + } break; + + case NOTIFICATION_EXIT_TREE: { + VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent()); + if (!cb) { + return; + } + cb->wheels.erase(this); + body = nullptr; + } break; } } diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index c16e3c2d78..bd47af8100 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -246,9 +246,9 @@ void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { has_def_value = true; } if (instance_uniforms.has(pi.name)) { - pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : 0); + pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE); } else { - pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : 0); //do not save if not changed + pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); //do not save if not changed } pi.name = "shader_params/" + pi.name; @@ -293,7 +293,12 @@ void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform instance_uniforms.erase(p_value); } else { instance_uniforms[p_uniform] = p_value; - RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + if (p_value.get_type() == Variant::OBJECT) { + RID tex_id = p_value; + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, tex_id); + } else { + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + } } } @@ -389,7 +394,7 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb); ADD_GROUP("Geometry", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index e00be9204c..3da59ac4c0 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -55,7 +55,7 @@ void VoxelGIData::_set_data(const Dictionary &p_data) { } else if (p_data.has("octree_df_png")) { Vector<uint8_t> octree_df_png = p_data["octree_df_png"]; Ref<Image> img; - img.instance(); + img.instantiate(); Error err = img->load_png_from_buffer(octree_df_png); ERR_FAIL_COND(err != OK); ERR_FAIL_COND(img->get_format() != Image::FORMAT_L8); @@ -76,7 +76,7 @@ Dictionary VoxelGIData::_get_data() const { d["octree_data"] = get_data_cells(); if (otsize != Vector3i()) { Ref<Image> img; - img.instance(); + img.instantiate(); img->create(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); Vector<uint8_t> df_png = img->save_png_to_buffer(); ERR_FAIL_COND_V(df_png.size() == 0, Dictionary()); @@ -143,15 +143,6 @@ float VoxelGIData::get_propagation() const { return propagation; } -void VoxelGIData::set_anisotropy_strength(float p_anisotropy_strength) { - RS::get_singleton()->voxel_gi_set_anisotropy_strength(probe, p_anisotropy_strength); - anisotropy_strength = p_anisotropy_strength; -} - -float VoxelGIData::get_anisotropy_strength() const { - return anisotropy_strength; -} - void VoxelGIData::set_energy(float p_energy) { RS::get_singleton()->voxel_gi_set_energy(probe, p_energy); energy = p_energy; @@ -161,24 +152,6 @@ float VoxelGIData::get_energy() const { return energy; } -void VoxelGIData::set_ao(float p_ao) { - RS::get_singleton()->voxel_gi_set_ao(probe, p_ao); - ao = p_ao; -} - -float VoxelGIData::get_ao() const { - return ao; -} - -void VoxelGIData::set_ao_size(float p_ao_size) { - RS::get_singleton()->voxel_gi_set_ao_size(probe, p_ao_size); - ao_size = p_ao_size; -} - -float VoxelGIData::get_ao_size() const { - return ao_size; -} - void VoxelGIData::set_bias(float p_bias) { RS::get_singleton()->voxel_gi_set_bias(probe, p_bias); bias = p_bias; @@ -219,15 +192,6 @@ RID VoxelGIData::get_rid() const { return probe; } -void VoxelGIData::_validate_property(PropertyInfo &property) const { - if (property.name == "anisotropy_strength") { - bool anisotropy_enabled = ProjectSettings::get_singleton()->get("rendering/global_illumination/voxel_gi/anisotropic"); - if (!anisotropy_enabled) { - property.usage = PROPERTY_USAGE_NOEDITOR; - } - } -} - void VoxelGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("allocate", "to_cell_xform", "aabb", "octree_size", "octree_cells", "data_cells", "distance_field", "level_counts"), &VoxelGIData::allocate); @@ -253,15 +217,6 @@ void VoxelGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &VoxelGIData::set_propagation); ClassDB::bind_method(D_METHOD("get_propagation"), &VoxelGIData::get_propagation); - ClassDB::bind_method(D_METHOD("set_anisotropy_strength", "strength"), &VoxelGIData::set_anisotropy_strength); - ClassDB::bind_method(D_METHOD("get_anisotropy_strength"), &VoxelGIData::get_anisotropy_strength); - - ClassDB::bind_method(D_METHOD("set_ao", "ao"), &VoxelGIData::set_ao); - ClassDB::bind_method(D_METHOD("get_ao"), &VoxelGIData::get_ao); - - ClassDB::bind_method(D_METHOD("set_ao_size", "strength"), &VoxelGIData::set_ao_size); - ClassDB::bind_method(D_METHOD("get_ao_size"), &VoxelGIData::get_ao_size); - ClassDB::bind_method(D_METHOD("set_interior", "interior"), &VoxelGIData::set_interior); ClassDB::bind_method(D_METHOD("is_interior"), &VoxelGIData::is_interior); @@ -278,9 +233,6 @@ void VoxelGIData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_bias", "get_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_normal_bias", "get_normal_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anisotropy_strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_anisotropy_strength", "get_anisotropy_strength"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ao", "get_ao"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao_size", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ao_size", "get_ao_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_two_bounces"), "set_use_two_bounces", "is_using_two_bounces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); } @@ -467,7 +419,7 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { Ref<VoxelGIData> probe_data = get_probe_data(); if (probe_data.is_null()) { - probe_data.instance(); + probe_data.instantiate(); } if (bake_step_function) { diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 5b9ee28b33..434d209421 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -51,15 +51,11 @@ class VoxelGIData : public Resource { float bias = 1.5; float normal_bias = 0.0; float propagation = 0.7; - float anisotropy_strength = 0.5; - float ao = 0.0; - float ao_size = 0.5; bool interior = false; bool use_two_bounces = false; protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; public: void allocate(const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3 &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts); @@ -77,15 +73,6 @@ public: void set_propagation(float p_propagation); float get_propagation() const; - void set_anisotropy_strength(float p_anisotropy_strength); - float get_anisotropy_strength() const; - - void set_ao(float p_ao); - float get_ao() const; - - void set_ao_size(float p_ao_size); - float get_ao_size() const; - void set_energy(float p_energy); float get_energy() const; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index ee0c3fe9b6..12f055c01d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -931,14 +931,14 @@ void Voxelizer::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<Mult Ref<MultiMesh> Voxelizer::create_debug_multimesh() { Ref<MultiMesh> mm; - mm.instance(); + mm.instantiate(); mm->set_transform_format(MultiMesh::TRANSFORM_3D); mm->set_use_colors(true); mm->set_instance_count(leaf_voxel_count); Ref<ArrayMesh> mesh; - mesh.instance(); + mesh.instantiate(); { Array arr; @@ -985,7 +985,7 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() { { Ref<StandardMaterial3D> fsm; - fsm.instance(); + fsm.instantiate(); fsm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); fsm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); fsm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 829ecc5ec2..352bef072f 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -145,7 +145,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { } if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { - warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).")); + warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } return warnings; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 4f2c816934..a91e712b0b 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -204,7 +204,7 @@ void XRController3D::_notification(int p_what) { // check button states for (int i = 0; i < 16; i++) { bool was_pressed = (button_states & mask) == mask; - bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, i); + bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)i); if (!was_pressed && is_pressed) { emit_signal("button_pressed", i); @@ -304,7 +304,7 @@ bool XRController3D::is_button_pressed(int p_button) const { return false; }; - return Input::get_singleton()->is_joy_button_pressed(joy_id, p_button); + return Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)p_button); }; float XRController3D::get_joystick_axis(int p_axis) const { @@ -313,7 +313,7 @@ float XRController3D::get_joystick_axis(int p_axis) const { return 0.0; }; - return Input::get_singleton()->get_joy_axis(joy_id, p_axis); + return Input::get_singleton()->get_joy_axis(joy_id, (JoyAxis)p_axis); }; real_t XRController3D::get_rumble() const { diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 15f562242f..3818c7edd1 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -47,7 +47,7 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const String left = property.name.get_slicec('/', 0); int idx = left.get_slicec('_', 2).to_int(); if (idx >= blend_points_used) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } AnimationRootNode::_validate_property(property); diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 9c4bc107dd..935ec457aa 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -34,8 +34,8 @@ void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position)); - r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const { @@ -556,13 +556,13 @@ String AnimationNodeBlendSpace2D::get_caption() const { void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &property) const { if (auto_triangles && property.name == "triangles") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("blend_point_")) { String left = property.name.get_slicec('/', 0); int idx = left.get_slicec('_', 2).to_int(); if (idx >= blend_points_used) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } AnimationRootNode::_validate_property(property); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 79a1dc1ac0..6a988042b5 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -43,7 +43,7 @@ StringName AnimationNodeAnimation::get_animation() const { Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr; void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { @@ -130,10 +130,10 @@ AnimationNodeAnimation::AnimationNodeAnimation() { void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::BOOL, active)); - r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { @@ -607,10 +607,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con } r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); - r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", 0)); - r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { @@ -752,7 +752,7 @@ void AnimationNodeTransition::_validate_property(PropertyInfo &property) const { if (n != "count") { int idx = n.to_int(); if (idx >= enabled_inputs) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } } @@ -1164,7 +1164,7 @@ void AnimationNodeBlendTree::_bind_methods() { AnimationNodeBlendTree::AnimationNodeBlendTree() { Ref<AnimationNodeOutput> output; - output.instance(); + output.instantiate(); Node n; n.node = output; n.position = Vector2(300, 150); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 65918a2989..f494f5c163 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -496,7 +496,7 @@ void AnimationNodeStateMachinePlayback::_bind_methods() { } AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { - set_local_to_scene(true); //only one per instanced scene + set_local_to_scene(true); //only one per instantiated scene } /////////////////////////////////////////////////////// @@ -520,7 +520,7 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { if (p_parameter == playback) { Ref<AnimationNodeStateMachinePlayback> p; - p.instance(); + p.instantiate(); return p; } else { return false; //advance condition diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index e3748a4ae1..6154eef3cf 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -513,7 +513,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float } #endif - static_cast<Node2D *>(pa->object)->set_rotation(Math::deg2rad((double)value)); + static_cast<Node2D *>(pa->object)->set_rotation((double)value); } break; case SP_NODE2D_SCALE: { #ifdef DEBUG_ENABLED @@ -1511,7 +1511,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o _ensure_node_caches(playback.current.from, p_root_override); - backup.instance(); + backup.instantiate(); for (int i = 0; i < playback.current.from->node_cache.size(); i++) { TrackNodeCache *nc = playback.current.from->node_cache[i]; if (!nc) { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 6fac70bdd5..1e07f83d09 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -385,7 +385,7 @@ void AnimationNode::_set_filters(const Array &p_filters) { void AnimationNode::_validate_property(PropertyInfo &property) const { if (!has_filter() && (property.name == "filter_enabled" || property.name == "filters")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index b963cf5702..770996820d 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -77,7 +77,7 @@ bool RootMotionView::get_zero_y() const { void RootMotionView::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - RS::get_singleton()->immediate_set_material(immediate, StandardMaterial3D::get_material_rid_for_2d(false, true, false, false, false)); + immediate_material = StandardMaterial3D::get_material_for_2d(false, true, false, false, false); first = true; } @@ -119,11 +119,12 @@ void RootMotionView::_notification(int p_what) { } accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size); - RS::get_singleton()->immediate_clear(immediate); + immediate->clear_surfaces(); int cells_in_radius = int((radius / cell_size) + 1.0); - RS::get_singleton()->immediate_begin(immediate, RS::PRIMITIVE_LINES); + immediate->surface_begin(Mesh::PRIMITIVE_LINES, immediate_material); + for (int i = -cells_in_radius; i < cells_in_radius; i++) { for (int j = -cells_in_radius; j < cells_in_radius; j++) { Vector3 from(i * cell_size, 0, j * cell_size); @@ -138,21 +139,21 @@ void RootMotionView::_notification(int p_what) { c_i.a *= MAX(0, 1.0 - from_i.length() / radius); c_j.a *= MAX(0, 1.0 - from_j.length() / radius); - RS::get_singleton()->immediate_color(immediate, c); - RS::get_singleton()->immediate_vertex(immediate, from); + immediate->surface_set_color(c); + immediate->surface_add_vertex(from); - RS::get_singleton()->immediate_color(immediate, c_i); - RS::get_singleton()->immediate_vertex(immediate, from_i); + immediate->surface_set_color(c_i); + immediate->surface_add_vertex(from_i); - RS::get_singleton()->immediate_color(immediate, c); - RS::get_singleton()->immediate_vertex(immediate, from); + immediate->surface_set_color(c); + immediate->surface_add_vertex(from); - RS::get_singleton()->immediate_color(immediate, c_j); - RS::get_singleton()->immediate_vertex(immediate, from_j); + immediate->surface_set_color(c_j); + immediate->surface_add_vertex(from_j); } } - RS::get_singleton()->immediate_end(immediate); + immediate->surface_end(); } } @@ -188,12 +189,13 @@ void RootMotionView::_bind_methods() { } RootMotionView::RootMotionView() { - set_process_internal(true); - immediate = RenderingServer::get_singleton()->immediate_create(); - set_base(immediate); + if (Engine::get_singleton()->is_editor_hint()) { + set_process_internal(true); + } + immediate.instantiate(); + set_base(immediate->get_rid()); } RootMotionView::~RootMotionView() { set_base(RID()); - RenderingServer::get_singleton()->free(immediate); } diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h index 4cd3c7b443..55fd2d2b73 100644 --- a/scene/animation/root_motion_view.h +++ b/scene/animation/root_motion_view.h @@ -32,12 +32,12 @@ #define ROOT_MOTION_VIEW_H #include "scene/3d/visual_instance_3d.h" - +#include "scene/resources/immediate_mesh.h" class RootMotionView : public VisualInstance3D { GDCLASS(RootMotionView, VisualInstance3D); public: - RID immediate; + Ref<ImmediateMesh> immediate; NodePath path; float cell_size = 1.0; float radius = 10.0; @@ -46,6 +46,8 @@ public: bool first = true; bool zero_y = true; + Ref<Material> immediate_material; + Transform3D accumulated; private: diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index b4e597f75e..7bf616e602 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -30,535 +30,407 @@ #include "tween.h" -void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) { - // Add a new pending command and reference it - pending_commands.push_back(PendingCommand()); - PendingCommand &cmd = pending_commands.back()->get(); - - // Update the command with the target key - cmd.key = p_key; - - // Determine command argument count - int &count = cmd.args; - if (p_arg10.get_type() != Variant::NIL) { - count = 10; - } else if (p_arg9.get_type() != Variant::NIL) { - count = 9; - } else if (p_arg8.get_type() != Variant::NIL) { - count = 8; - } else if (p_arg7.get_type() != Variant::NIL) { - count = 7; - } else if (p_arg6.get_type() != Variant::NIL) { - count = 6; - } else if (p_arg5.get_type() != Variant::NIL) { - count = 5; - } else if (p_arg4.get_type() != Variant::NIL) { - count = 4; - } else if (p_arg3.get_type() != Variant::NIL) { - count = 3; - } else if (p_arg2.get_type() != Variant::NIL) { - count = 2; - } else if (p_arg1.get_type() != Variant::NIL) { - count = 1; - } else { - count = 0; - } +#include "scene/main/node.h" - // Add the specified arguments to the command - if (count > 0) { - cmd.arg[0] = p_arg1; - } - if (count > 1) { - cmd.arg[1] = p_arg2; - } - if (count > 2) { - cmd.arg[2] = p_arg3; - } - if (count > 3) { - cmd.arg[3] = p_arg4; - } - if (count > 4) { - cmd.arg[4] = p_arg5; - } - if (count > 5) { - cmd.arg[5] = p_arg6; - } - if (count > 6) { - cmd.arg[6] = p_arg7; - } - if (count > 7) { - cmd.arg[7] = p_arg8; - } - if (count > 8) { - cmd.arg[8] = p_arg9; +void Tweener::set_tween(Ref<Tween> p_tween) { + tween = p_tween; +} + +void Tweener::_bind_methods() { + ADD_SIGNAL(MethodInfo("finished")); +} + +void Tween::start_tweeners() { + if (tweeners.is_empty()) { + dead = true; + ERR_FAIL_MSG("Tween without commands, aborting."); } - if (count > 9) { - cmd.arg[9] = p_arg10; + + for (List<Ref<Tweener>>::Element *E = tweeners.write[current_step].front(); E; E = E->next()) { + E->get()->start(); } } -void Tween::_process_pending_commands() { - // For each pending command... - for (List<PendingCommand>::Element *E = pending_commands.front(); E; E = E->next()) { - // Get the command - PendingCommand &cmd = E->get(); - Callable::CallError err; - - // Grab all of the arguments for the command - Variant *arg[10] = { - &cmd.arg[0], - &cmd.arg[1], - &cmd.arg[2], - &cmd.arg[3], - &cmd.arg[4], - &cmd.arg[5], - &cmd.arg[6], - &cmd.arg[7], - &cmd.arg[8], - &cmd.arg[9], - }; - - // Execute the command (and retrieve any errors) - this->call(cmd.key, (const Variant **)arg, cmd.args, err); - } +Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration) { + ERR_FAIL_NULL_V(p_target, nullptr); + ERR_FAIL_COND_V_MSG(invalid, nullptr, "Tween was created outside the scene tree, can't use Tweeners."); + ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); - // Clear the pending commands - pending_commands.clear(); + Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, p_property, p_to, p_duration)); + append(tweener); + return tweener; } -bool Tween::_set(const StringName &p_name, const Variant &p_value) { - // Set the correct attribute based on the given name - String name = p_name; - if (name == "playback/speed" || name == "speed") { // Backwards compatibility - set_speed_scale(p_value); - return true; +Ref<IntervalTweener> Tween::tween_interval(float p_time) { + ERR_FAIL_COND_V_MSG(invalid, nullptr, "Tween was created outside the scene tree, can't use Tweeners."); + ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); - } else if (name == "playback/active") { - set_active(p_value); - return true; + Ref<IntervalTweener> tweener = memnew(IntervalTweener(p_time)); + append(tweener); + return tweener; +} - } else if (name == "playback/repeat") { - set_repeat(p_value); - return true; - } - return false; +Ref<CallbackTweener> Tween::tween_callback(Callable p_callback) { + ERR_FAIL_COND_V_MSG(invalid, nullptr, "Tween was created outside the scene tree, can't use Tweeners."); + ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); + + Ref<CallbackTweener> tweener = memnew(CallbackTweener(p_callback)); + append(tweener); + return tweener; } -bool Tween::_get(const StringName &p_name, Variant &r_ret) const { - // Get the correct attribute based on the given name - String name = p_name; - if (name == "playback/speed") { // Backwards compatibility - r_ret = speed_scale; - return true; +Ref<MethodTweener> Tween::tween_method(Callable p_callback, float p_from, float p_to, float p_duration) { + ERR_FAIL_COND_V_MSG(invalid, nullptr, "Tween was created outside the scene tree, can't use Tweeners."); + ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); - } else if (name == "playback/active") { - r_ret = is_active(); - return true; + Ref<MethodTweener> tweener = memnew(MethodTweener(p_callback, p_from, p_to, p_duration)); + append(tweener); + return tweener; +} - } else if (name == "playback/repeat") { - r_ret = is_repeat(); - return true; +Ref<Tween> Tween::append(Ref<Tweener> p_tweener) { + ERR_FAIL_COND_V_MSG(invalid, nullptr, "Tween was created outside the scene tree, can't use Tweeners."); + ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); + p_tweener->set_tween(this); + + if (parallel_enabled) { + current_step = MAX(current_step, 0); + } else { + current_step++; } - return false; -} - -void Tween::_get_property_list(List<PropertyInfo> *p_list) const { - // Add the property info for the Tween object - p_list->push_back(PropertyInfo(Variant::BOOL, "playback/active", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::BOOL, "playback/repeat", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01")); -} - -void Tween::_notification(int p_what) { - // What notification did we receive? - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - // Are we not already active? - if (!is_active()) { - // Make sure that a previous process state was not saved - // Only process if "processing" is set - set_physics_process_internal(false); - set_process_internal(false); - } - } break; + parallel_enabled = default_parallel; - case NOTIFICATION_READY: { - // Do nothing - } break; + tweeners.resize(current_step + 1); + tweeners.write[current_step].push_back(p_tweener); - case NOTIFICATION_INTERNAL_PROCESS: { - // Are we processing during physics time? - if (tween_process_mode == TWEEN_PROCESS_PHYSICS) { - // Do nothing since we aren't aligned with physics when we should be - break; - } + return this; +} - // Should we update? - if (is_active()) { - // Update the tweens - _tween_process(get_process_delta_time()); - } - } break; +void Tween::stop() { + started = false; + running = false; + dead = false; +} - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - // Are we processing during 'regular' time? - if (tween_process_mode == TWEEN_PROCESS_IDLE) { - // Do nothing since we would only process during idle time - break; - } +void Tween::pause() { + running = false; +} - // Should we update? - if (is_active()) { - // Update the tweens - _tween_process(get_physics_process_delta_time()); - } - } break; +void Tween::play() { + ERR_FAIL_COND_MSG(invalid, "Tween invalid, can't play."); + ERR_FAIL_COND_MSG(dead, "Can't play finished Tween, use stop() first to reset its state."); + running = true; +} - case NOTIFICATION_EXIT_TREE: { - // We've left the tree. Stop all tweens - stop_all(); - } break; - } +void Tween::kill() { + running = false; // For the sake of is_running(). + dead = true; } -void Tween::_bind_methods() { - // Bind getters and setters - ClassDB::bind_method(D_METHOD("is_active"), &Tween::is_active); - ClassDB::bind_method(D_METHOD("set_active", "active"), &Tween::set_active); +bool Tween::is_running() { + return running; +} - ClassDB::bind_method(D_METHOD("is_repeat"), &Tween::is_repeat); - ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &Tween::set_repeat); +void Tween::set_valid(bool p_valid) { + invalid = !p_valid; +} - ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &Tween::set_speed_scale); - ClassDB::bind_method(D_METHOD("get_speed_scale"), &Tween::get_speed_scale); - - ClassDB::bind_method(D_METHOD("set_tween_process_mode", "mode"), &Tween::set_tween_process_mode); - ClassDB::bind_method(D_METHOD("get_tween_process_mode"), &Tween::get_tween_process_mode); - - // Bind the various Tween control methods - ClassDB::bind_method(D_METHOD("start"), &Tween::start); - ClassDB::bind_method(D_METHOD("reset", "object", "key"), &Tween::reset, DEFVAL("")); - ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all); - ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL("")); - ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all); - ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL("")); - ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all); - ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL("")); - ClassDB::bind_method(D_METHOD("_remove_by_uid", "uid"), &Tween::_remove_by_uid); - ClassDB::bind_method(D_METHOD("remove_all"), &Tween::remove_all); - ClassDB::bind_method(D_METHOD("seek", "time"), &Tween::seek); - ClassDB::bind_method(D_METHOD("tell"), &Tween::tell); - ClassDB::bind_method(D_METHOD("get_runtime"), &Tween::get_runtime); - - // Bind interpolation and follow methods - ClassDB::bind_method(D_METHOD("interpolate_property", "object", "property", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_property, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("interpolate_method", "object", "method", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_method, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("interpolate_callback", "object", "duration", "callback", "arg1", "arg2", "arg3", "arg4", "arg5"), &Tween::interpolate_callback, DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("interpolate_deferred_callback", "object", "duration", "callback", "arg1", "arg2", "arg3", "arg4", "arg5"), &Tween::interpolate_deferred_callback, DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("follow_property", "object", "property", "initial_val", "target", "target_property", "duration", "trans_type", "ease_type", "delay"), &Tween::follow_property, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("follow_method", "object", "method", "initial_val", "target", "target_method", "duration", "trans_type", "ease_type", "delay"), &Tween::follow_method, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("targeting_property", "object", "property", "initial", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_property, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("targeting_method", "object", "method", "initial", "initial_method", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_method, DEFVAL(TRANS_LINEAR), DEFVAL(EASE_IN_OUT), DEFVAL(0)); - - // Add the Tween signals - ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); - ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::FLOAT, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); - ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); - ADD_SIGNAL(MethodInfo("tween_all_completed")); - - // Add the properties and tie them to the getters and setters - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); - - // Bind Idle vs Physics process - BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS); - BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE); +bool Tween::is_valid() { + return invalid; +} - // Bind the Transition type constants - BIND_ENUM_CONSTANT(TRANS_LINEAR); - BIND_ENUM_CONSTANT(TRANS_SINE); - BIND_ENUM_CONSTANT(TRANS_QUINT); - BIND_ENUM_CONSTANT(TRANS_QUART); - BIND_ENUM_CONSTANT(TRANS_QUAD); - BIND_ENUM_CONSTANT(TRANS_EXPO); - BIND_ENUM_CONSTANT(TRANS_ELASTIC); - BIND_ENUM_CONSTANT(TRANS_CUBIC); - BIND_ENUM_CONSTANT(TRANS_CIRC); - BIND_ENUM_CONSTANT(TRANS_BOUNCE); - BIND_ENUM_CONSTANT(TRANS_BACK); +Ref<Tween> Tween::bind_node(Node *p_node) { + bound_node = p_node->get_instance_id(); + is_bound = true; + return this; +} - // Bind the easing constants - BIND_ENUM_CONSTANT(EASE_IN); - BIND_ENUM_CONSTANT(EASE_OUT); - BIND_ENUM_CONSTANT(EASE_IN_OUT); - BIND_ENUM_CONSTANT(EASE_OUT_IN); +Ref<Tween> Tween::set_process_mode(TweenProcessMode p_mode) { + process_mode = p_mode; + return this; } -Variant Tween::_get_initial_val(const InterpolateData &p_data) const { - // What type of data are we interpolating? - switch (p_data.type) { - case INTER_PROPERTY: - case INTER_METHOD: - case FOLLOW_PROPERTY: - case FOLLOW_METHOD: - // Simply use the given initial value - return p_data.initial_val; - - case TARGETING_PROPERTY: - case TARGETING_METHOD: { - // Get the object that is being targeted - Object *object = ObjectDB::get_instance(p_data.target_id); - ERR_FAIL_COND_V(object == nullptr, p_data.initial_val); - - // Are we targeting a property or a method? - Variant initial_val; - if (p_data.type == TARGETING_PROPERTY) { - // Get the property from the target object - bool valid = false; - initial_val = object->get_indexed(p_data.target_key, &valid); - ERR_FAIL_COND_V(!valid, p_data.initial_val); - } else { - // Call the method and get the initial value from it - Callable::CallError error; - initial_val = object->call(p_data.target_key[0], nullptr, 0, error); - ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, p_data.initial_val); - } - return initial_val; - } +Tween::TweenProcessMode Tween::get_process_mode() { + return process_mode; +} + +Ref<Tween> Tween::set_pause_mode(TweenPauseMode p_mode) { + pause_mode = p_mode; + return this; +} + +Tween::TweenPauseMode Tween::get_pause_mode() { + return pause_mode; +} + +Ref<Tween> Tween::set_parallel(bool p_parallel) { + default_parallel = p_parallel; + parallel_enabled = p_parallel; + return this; +} + +Ref<Tween> Tween::set_loops(int p_loops) { + loops = p_loops; + return this; +} + +Ref<Tween> Tween::set_speed_scale(float p_speed) { + speed_scale = p_speed; + return this; +} + +Ref<Tween> Tween::set_trans(TransitionType p_trans) { + default_transition = p_trans; + return this; +} - case INTER_CALLBACK: - // Callback does not have a special initial value - break; +Tween::TransitionType Tween::get_trans() { + return default_transition; +} + +Ref<Tween> Tween::set_ease(EaseType p_ease) { + default_ease = p_ease; + return this; +} + +Tween::EaseType Tween::get_ease() { + return default_ease; +} + +Ref<Tween> Tween::parallel() { + parallel_enabled = true; + return this; +} + +Ref<Tween> Tween::chain() { + parallel_enabled = false; + return this; +} + +bool Tween::custom_step(float p_delta) { + bool r = running; + running = true; + bool ret = step(p_delta); + running = running && r; // Running might turn false when Tween finished. + return ret; +} + +bool Tween::step(float p_delta) { + ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners."); + + if (dead) { + return false; } - // If we've made it here, just return the delta value as the initial value - return p_data.delta_val; -} - -Variant Tween::_get_final_val(const InterpolateData &p_data) const { - switch (p_data.type) { - case FOLLOW_PROPERTY: - case FOLLOW_METHOD: { - // Get the object that is being followed - Object *target = ObjectDB::get_instance(p_data.target_id); - ERR_FAIL_COND_V(target == nullptr, p_data.initial_val); - - // We want to figure out the final value - Variant final_val; - if (p_data.type == FOLLOW_PROPERTY) { - // Read the property as-is - bool valid = false; - final_val = target->get_indexed(p_data.target_key, &valid); - ERR_FAIL_COND_V(!valid, p_data.initial_val); - } else { - // We're looking at a method. Call the method on the target object - Callable::CallError error; - final_val = target->call(p_data.target_key[0], nullptr, 0, error); - ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, p_data.initial_val); - } - // If we're looking at an INT value, instead convert it to a FLOAT - // This is better for interpolation - if (final_val.get_type() == Variant::INT) { - final_val = final_val.operator real_t(); - } + if (!running) { + return true; + } - return final_val; - } - default: { - // If we're not following a final value/method, use the final value from the data - return p_data.final_val; + if (is_bound) { + Object *bound_instance = ObjectDB::get_instance(bound_node); + if (bound_instance) { + Node *bound_node = Object::cast_to<Node>(bound_instance); + // This can't by anything else than Node, so we can omit checking if casting succeeded. + if (!bound_node->is_inside_tree()) { + return true; + } + } else { + return false; } } -} -Variant &Tween::_get_delta_val(InterpolateData &p_data) { - // What kind of data are we interpolating? - switch (p_data.type) { - case INTER_PROPERTY: - case INTER_METHOD: - // Simply return the given delta value - return p_data.delta_val; - - case FOLLOW_PROPERTY: - case FOLLOW_METHOD: { - // We're following an object, so grab that instance - Object *target = ObjectDB::get_instance(p_data.target_id); - ERR_FAIL_COND_V(target == nullptr, p_data.initial_val); - - // We want to figure out the final value - Variant final_val; - if (p_data.type == FOLLOW_PROPERTY) { - // Read the property as-is - bool valid = false; - final_val = target->get_indexed(p_data.target_key, &valid); - ERR_FAIL_COND_V(!valid, p_data.initial_val); - } else { - // We're looking at a method. Call the method on the target object - Callable::CallError error; - final_val = target->call(p_data.target_key[0], nullptr, 0, error); - ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, p_data.initial_val); - } + if (!started) { + current_step = 0; + loops_done = 0; + start_tweeners(); + started = true; + } - // If we're looking at an INT value, instead convert it to a FLOAT - // This is better for interpolation - if (final_val.get_type() == Variant::INT) { - final_val = final_val.operator real_t(); - } + float rem_delta = p_delta * speed_scale; + bool step_active = false; - // Calculate the delta based on the initial value and the final value - _calc_delta_val(p_data.initial_val, final_val, p_data.delta_val); - return p_data.delta_val; + while (rem_delta > 0 && running) { + float step_delta = rem_delta; + step_active = false; + + for (List<Ref<Tweener>>::Element *E = tweeners.write[current_step].front(); E; E = E->next()) { + // Modified inside Tweener.step(). + float temp_delta = rem_delta; + // Turns to true if any Tweener returns true (i.e. is still not finished). + step_active = E->get()->step(temp_delta) || step_active; + step_delta = MIN(temp_delta, rem_delta); } - case TARGETING_PROPERTY: - case TARGETING_METHOD: { - // Grab the initial value from the data to calculate delta - Variant initial_val = _get_initial_val(p_data); + rem_delta = step_delta; - // If we're looking at an INT value, instead convert it to a FLOAT - // This is better for interpolation - if (initial_val.get_type() == Variant::INT) { - initial_val = initial_val.operator real_t(); - } + if (!step_active) { + emit_signal("step_finished", current_step); + current_step++; - // Calculate the delta based on the initial value and the final value - _calc_delta_val(initial_val, p_data.final_val, p_data.delta_val); - return p_data.delta_val; + if (current_step == tweeners.size()) { + loops_done++; + if (loops_done == loops) { + running = false; + dead = true; + emit_signal("finished"); + } else { + emit_signal("loop_finished", loops_done); + current_step = 0; + start_tweeners(); + } + } else { + start_tweeners(); + } } + } + + return true; +} - case INTER_CALLBACK: - // Callbacks have no special delta - break; +bool Tween::should_pause() { + if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) { + Object *bound_instance = ObjectDB::get_instance(bound_node); + if (bound_instance) { + Node *bound_node = Object::cast_to<Node>(bound_instance); + return !bound_node->can_process(); + } } - // If we've made it here, use the initial value as the delta - return p_data.initial_val; + + return pause_mode != TWEEN_PAUSE_PROCESS; } -Variant Tween::_run_equation(InterpolateData &p_data) { - // Get the initial and delta values from the data - Variant initial_val = _get_initial_val(p_data); - Variant &delta_val = _get_delta_val(p_data); - Variant result; +Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, TransitionType p_trans, EaseType p_ease) { + ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant()); + ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant()); +// Helper macro to run equation on sub-elements of the value (e.g. x and y of Vector2). #define APPLY_EQUATION(element) \ - r.element = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.duration); + r.element = run_equation(p_trans, p_ease, p_time, i.element, d.element, p_duration); - // What type of data are we interpolating? - switch (initial_val.get_type()) { - case Variant::BOOL: - // Run the boolean specific equation (checking if it is at least 0.5) - result = (_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, initial_val, delta_val, p_data.duration)) >= 0.5; - break; + switch (p_initial_val.get_type()) { + case Variant::BOOL: { + return (run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration)) >= 0.5; + } - case Variant::INT: - // Run the integer specific equation - result = (int)_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int)initial_val, (int)delta_val, p_data.duration); - break; + case Variant::INT: { + return (int)run_equation(p_trans, p_ease, p_time, (int)p_initial_val, (int)p_delta_val, p_duration); + } - case Variant::FLOAT: - // Run the FLOAT specific equation - result = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t)initial_val, (real_t)delta_val, p_data.duration); - break; + case Variant::FLOAT: { + return run_equation(p_trans, p_ease, p_time, (real_t)p_initial_val, (real_t)p_delta_val, p_duration); + } case Variant::VECTOR2: { - // Get vectors for initial and delta values - Vector2 i = initial_val; - Vector2 d = delta_val; + Vector2 i = p_initial_val; + Vector2 d = p_delta_val; Vector2 r; - // Execute the equation and mutate the r vector - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(x); APPLY_EQUATION(y); - result = r; - } break; + return r; + } + + case Variant::VECTOR2I: { + Vector2i i = p_initial_val; + Vector2i d = p_delta_val; + Vector2i r; + + APPLY_EQUATION(x); + APPLY_EQUATION(y); + return r; + } case Variant::RECT2: { - // Get the Rect2 for initial and delta value - Rect2 i = initial_val; - Rect2 d = delta_val; + Rect2 i = p_initial_val; + Rect2 d = p_delta_val; Rect2 r; - // Execute the equation for the position and size of Rect2 APPLY_EQUATION(position.x); APPLY_EQUATION(position.y); APPLY_EQUATION(size.x); APPLY_EQUATION(size.y); - result = r; - } break; + return r; + } + + case Variant::RECT2I: { + Rect2i i = p_initial_val; + Rect2i d = p_delta_val; + Rect2i r; + + APPLY_EQUATION(position.x); + APPLY_EQUATION(position.y); + APPLY_EQUATION(size.x); + APPLY_EQUATION(size.y); + return r; + } case Variant::VECTOR3: { - // Get vectors for initial and delta values - Vector3 i = initial_val; - Vector3 d = delta_val; + Vector3 i = p_initial_val; + Vector3 d = p_delta_val; Vector3 r; - // Execute the equation and mutate the r vector - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(x); APPLY_EQUATION(y); APPLY_EQUATION(z); - result = r; - } break; + return r; + } + + case Variant::VECTOR3I: { + Vector3i i = p_initial_val; + Vector3i d = p_delta_val; + Vector3i r; + + APPLY_EQUATION(x); + APPLY_EQUATION(y); + APPLY_EQUATION(z); + return r; + } case Variant::TRANSFORM2D: { - // Get the transforms for initial and delta values - Transform2D i = initial_val; - Transform2D d = delta_val; + Transform2D i = p_initial_val; + Transform2D d = p_delta_val; Transform2D r; - // Execute the equation on the transforms and mutate the r transform - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(elements[0][0]); APPLY_EQUATION(elements[0][1]); APPLY_EQUATION(elements[1][0]); APPLY_EQUATION(elements[1][1]); APPLY_EQUATION(elements[2][0]); APPLY_EQUATION(elements[2][1]); - result = r; - } break; + return r; + } case Variant::QUATERNION: { - // Get the quaternian for the initial and delta values - Quaternion i = initial_val; - Quaternion d = delta_val; + Quaternion i = p_initial_val; + Quaternion d = p_delta_val; Quaternion r; - // Execute the equation on the quaternian values and mutate the r quaternian - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(x); APPLY_EQUATION(y); APPLY_EQUATION(z); APPLY_EQUATION(w); - result = r; - } break; + return r; + } case Variant::AABB: { - // Get the AABB's for the initial and delta values - AABB i = initial_val; - AABB d = delta_val; + AABB i = p_initial_val; + AABB d = p_delta_val; AABB r; - // Execute the equation for the position and size of the AABB's and mutate the r AABB - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(position.x); APPLY_EQUATION(position.y); APPLY_EQUATION(position.z); APPLY_EQUATION(size.x); APPLY_EQUATION(size.y); APPLY_EQUATION(size.z); - result = r; - } break; + return r; + } case Variant::BASIS: { - // Get the basis for initial and delta values - Basis i = initial_val; - Basis d = delta_val; + Basis i = p_initial_val; + Basis d = p_delta_val; Basis r; - // Execute the equation on all the basis and mutate the r basis - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(elements[0][0]); APPLY_EQUATION(elements[0][1]); APPLY_EQUATION(elements[0][2]); @@ -568,17 +440,14 @@ Variant Tween::_run_equation(InterpolateData &p_data) { APPLY_EQUATION(elements[2][0]); APPLY_EQUATION(elements[2][1]); APPLY_EQUATION(elements[2][2]); - result = r; - } break; + return r; + } case Variant::TRANSFORM3D: { - // Get the transforms for the initial and delta values - Transform3D i = initial_val; - Transform3D d = delta_val; + Transform3D i = p_initial_val; + Transform3D d = p_delta_val; Transform3D r; - // Execute the equation for each of the transforms and their origin and mutate the r transform - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(basis.elements[0][0]); APPLY_EQUATION(basis.elements[0][1]); APPLY_EQUATION(basis.elements[0][2]); @@ -591,634 +460,67 @@ Variant Tween::_run_equation(InterpolateData &p_data) { APPLY_EQUATION(origin.x); APPLY_EQUATION(origin.y); APPLY_EQUATION(origin.z); - result = r; - } break; + return r; + } case Variant::COLOR: { - // Get the Color for initial and delta value - Color i = initial_val; - Color d = delta_val; + Color i = p_initial_val; + Color d = p_delta_val; Color r; - // Apply the equation on the Color RGBA, and mutate the r color - // This uses the custom APPLY_EQUATION macro defined above APPLY_EQUATION(r); APPLY_EQUATION(g); APPLY_EQUATION(b); APPLY_EQUATION(a); - result = r; - } break; - - default: { - // If unknown, just return the initial value - result = initial_val; - } break; - }; -#undef APPLY_EQUATION - // Return the result that was computed - return result; -} - -bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) { - // Get the object we want to apply the new value to - Object *object = ObjectDB::get_instance(p_data.id); - ERR_FAIL_COND_V(object == nullptr, false); - - // What kind of data are we mutating? - switch (p_data.type) { - case INTER_PROPERTY: - case FOLLOW_PROPERTY: - case TARGETING_PROPERTY: { - // Simply set the property on the object - bool valid = false; - object->set_indexed(p_data.key, value, &valid); - return valid; + return r; } - case INTER_METHOD: - case FOLLOW_METHOD: - case TARGETING_METHOD: { - // We want to call the method on the target object - Callable::CallError error; - - // Do we have a non-nil value passed in? - if (value.get_type() != Variant::NIL) { - // Pass it as an argument to the function call - Variant *arg[1] = { &value }; - object->call(p_data.key[0], (const Variant **)arg, 1, error); - } else { - // Don't pass any argument - object->call(p_data.key[0], nullptr, 0, error); - } - - // Did we get an error from the function call? - return error.error == Callable::CallError::CALL_OK; + default: { + return p_initial_val; } - - case INTER_CALLBACK: - // Nothing to apply for a callback - break; }; - // No issues found! - return true; -} - -void Tween::_tween_process(float p_delta) { - // Process all of the pending commands - _process_pending_commands(); - - // If the scale is 0, make no progress on the tweens - if (speed_scale == 0) { - return; - } - - // Update the delta and whether we are pending an update - p_delta *= speed_scale; - pending_update++; - - // Are we repeating the interpolations? - if (repeat) { - // For each interpolation... - bool repeats_finished = true; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the data from it - InterpolateData &data = E->get(); - - // Is not finished? - if (!data.finish) { - // We aren't finished yet, no need to check the rest - repeats_finished = false; - break; - } - } - - // If we are all finished, we can reset all of the tweens - if (repeats_finished) { - reset_all(); - } - } - - // Are all of the tweens complete? - int any_unfinished = 0; - - // For each tween we wish to interpolate... - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the data from it - InterpolateData &data = E->get(); - - // Is the data not active or already finished? No need to go any further - if (!data.active || data.finish) { - continue; - } - - // Track if we hit one that isn't finished yet - any_unfinished++; - - // Get the target object for this interpolation - Object *object = ObjectDB::get_instance(data.id); - if (object == nullptr) { - continue; - } - - // Are we still delaying this tween? - bool prev_delaying = data.elapsed <= data.delay; - data.elapsed += p_delta; - if (data.elapsed < data.delay) { - continue; - } else if (prev_delaying) { - // We can apply the tween's value to the data and emit that the tween has started - _apply_tween_value(data, data.initial_val); - emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); - } - - // Are we at the end of the tween? - if (data.elapsed > (data.delay + data.duration)) { - // Set the elapsed time to the end and mark this one as finished - data.elapsed = data.delay + data.duration; - data.finish = true; - } - - // Are we interpolating a callback? - if (data.type == INTER_CALLBACK) { - // Is the tween completed? - if (data.finish) { - // Are we calling this callback deferred or immediately? - if (data.call_deferred) { - // Run the deferred function callback, applying the correct number of arguments - switch (data.args) { - case 0: - object->call_deferred(data.key[0]); - break; - case 1: - object->call_deferred(data.key[0], data.arg[0]); - break; - case 2: - object->call_deferred(data.key[0], data.arg[0], data.arg[1]); - break; - case 3: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2]); - break; - case 4: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3]); - break; - case 5: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3], data.arg[4]); - break; - } - } else { - // Call the function directly with the arguments - Callable::CallError error; - Variant *arg[5] = { - &data.arg[0], - &data.arg[1], - &data.arg[2], - &data.arg[3], - &data.arg[4], - }; - object->call(data.key[0], (const Variant **)arg, data.args, error); - } - } - } else { - // We can apply the value directly - Variant result = _run_equation(data); - _apply_tween_value(data, result); - - // Emit that the tween has taken a step - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); - } - - // Is the tween now finished? - if (data.finish) { - // Set it to the final value directly - Variant final_val = _get_final_val(data); - _apply_tween_value(data, final_val); - - // Mark the tween as completed and emit the signal - data.elapsed = 0; - emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); - - // If we are not repeating the tween, remove it - if (!repeat) { - call_deferred("_remove_by_uid", data.uid); - any_unfinished--; - } - } - } - // One less update left to go - pending_update--; - - // If all tweens are completed, we no longer need to be active - if (any_unfinished == 0) { - set_active(false); - emit_signal("tween_all_completed"); - } -} - -void Tween::set_tween_process_mode(TweenProcessMode p_mode) { - tween_process_mode = p_mode; -} - -Tween::TweenProcessMode Tween::get_tween_process_mode() const { - return tween_process_mode; -} - -bool Tween::is_active() const { - return is_processing_internal() || is_physics_processing_internal(); -} - -void Tween::set_active(bool p_active) { - // Do nothing if it's the same active mode that we currently are - if (is_active() == p_active) { - return; - } - - // Depending on physics or idle, set processing - switch (tween_process_mode) { - case TWEEN_PROCESS_IDLE: - set_process_internal(p_active); - break; - case TWEEN_PROCESS_PHYSICS: - set_physics_process_internal(p_active); - break; - } -} - -bool Tween::is_repeat() const { - return repeat; -} - -void Tween::set_repeat(bool p_repeat) { - repeat = p_repeat; -} - -void Tween::set_speed_scale(float p_speed) { - speed_scale = p_speed; -} - -float Tween::get_speed_scale() const { - return speed_scale; -} - -void Tween::start() { - ERR_FAIL_COND_MSG(!is_inside_tree(), "Tween was not added to the SceneTree!"); - - // Are there any pending updates? - if (pending_update != 0) { - // Start the tweens after deferring - call_deferred("start"); - return; - } - - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - InterpolateData &data = E->get(); - data.active = true; - } - pending_update--; - - // We want to be activated - set_active(true); - - // Don't resume from current position if stop_all() function has been used - if (was_stopped) { - seek(0); - } - was_stopped = false; -} - -void Tween::reset(Object *p_object, StringName p_key) { - // Find all interpolations that use the same object and target string - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the target object - InterpolateData &data = E->get(); - Object *object = ObjectDB::get_instance(data.id); - if (object == nullptr) { - continue; - } - - // Do we have the correct object and key? - if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { - // Reset the tween to the initial state - data.elapsed = 0; - data.finish = false; - - // Also apply the initial state if there isn't a delay - if (data.delay == 0) { - _apply_tween_value(data, data.initial_val); - } - } - } - pending_update--; -} - -void Tween::reset_all() { - // Go through all interpolations - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the target data and set it back to the initial state - InterpolateData &data = E->get(); - data.elapsed = 0; - data.finish = false; - - // If there isn't a delay, apply the value to the object - if (data.delay == 0) { - _apply_tween_value(data, data.initial_val); - } - } - pending_update--; -} - -void Tween::stop(Object *p_object, StringName p_key) { - // Find the tween that has the given target object and string key - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the object the tween is targeting - InterpolateData &data = E->get(); - Object *object = ObjectDB::get_instance(data.id); - if (object == nullptr) { - continue; - } - - // Is this the correct object and does it have the given key? - if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { - // Disable the tween - data.active = false; - } - } - pending_update--; -} - -void Tween::stop_all() { - // We no longer need to be active since all tweens have been stopped - set_active(false); - was_stopped = true; - // For each interpolation... - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Simply set it inactive - InterpolateData &data = E->get(); - data.active = false; - } - pending_update--; -} - -void Tween::resume(Object *p_object, StringName p_key) { - // We need to be activated - // TODO: What if no tween is found?? - set_active(true); - - // Find the tween that uses the given target object and string key - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Grab the object - InterpolateData &data = E->get(); - Object *object = ObjectDB::get_instance(data.id); - if (object == nullptr) { - continue; - } - - // If the object and string key match, activate it - if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { - data.active = true; - } - } - pending_update--; -} - -void Tween::resume_all() { - // Set ourselves active so we can process tweens - // TODO: What if there are no tweens? We get set to active for no reason! - set_active(true); - - // For each interpolation... - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Simply grab it and set it to active - InterpolateData &data = E->get(); - data.active = true; - } - pending_update--; -} - -void Tween::remove(Object *p_object, StringName p_key) { - // If we are still updating, call this function again later - if (pending_update != 0) { - call_deferred("remove", p_object, p_key); - return; - } - - // For each interpolation... - List<List<InterpolateData>::Element *> for_removal; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the target object - InterpolateData &data = E->get(); - Object *object = ObjectDB::get_instance(data.id); - if (object == nullptr) { - continue; - } - - // If the target object and string key match, queue it for removal - if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { - for_removal.push_back(E); - } - } - - // For each interpolation we wish to remove... - for (List<List<InterpolateData>::Element *>::Element *E = for_removal.front(); E; E = E->next()) { - // Erase it - interpolates.erase(E->get()); - } -} - -void Tween::_remove_by_uid(int uid) { - // If we are still updating, call this function again later - if (pending_update != 0) { - call_deferred("_remove_by_uid", uid); - return; - } - - // Find the interpolation that matches the given UID - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - if (uid == E->get().uid) { - // It matches, erase it and stop looking - E->erase(); - break; - } - } -} - -void Tween::_push_interpolate_data(InterpolateData &p_data) { - pending_update++; - - // Add the new interpolation - p_data.uid = ++uid; - interpolates.push_back(p_data); - - pending_update--; +#undef APPLY_EQUATION } -void Tween::remove_all() { - // If we are still updating, call this function again later - if (pending_update != 0) { - call_deferred("remove_all"); - return; - } - // We no longer need to be active - set_active(false); - - // Clear out all interpolations and reset the uid - interpolates.clear(); - uid = 0; -} - -void Tween::seek(real_t p_time) { - // Go through each interpolation... - pending_update++; - for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the target data - InterpolateData &data = E->get(); - - // Update the elapsed data to be set to the target time - data.elapsed = p_time; - - // Are we at the end? - if (data.elapsed < data.delay) { - // There is still time left to go - data.finish = false; - continue; - } else if (data.elapsed >= (data.delay + data.duration)) { - // We are past the end of it, set the elapsed time to the end and mark as finished - data.elapsed = (data.delay + data.duration); - data.finish = true; - } else { - // We are not finished with this interpolation yet - data.finish = false; +Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) { + switch (p_intial_val.get_type()) { + case Variant::BOOL: { + return (int)p_final_val - (int)p_intial_val; } - // If we are a callback, do nothing special - if (data.type == INTER_CALLBACK) { - continue; + case Variant::RECT2: { + Rect2 i = p_intial_val; + Rect2 f = p_final_val; + return Rect2(f.position - i.position, f.size - i.size); } - // Run the equation on the data and apply the value - Variant result = _run_equation(data); - _apply_tween_value(data, result); - } - pending_update--; -} - -real_t Tween::tell() const { - // We want to grab the position of the furthest along tween - pending_update++; - real_t pos = 0.0; - - // For each interpolation... - for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the data and figure out if its position is further along than the previous ones - const InterpolateData &data = E->get(); - if (data.elapsed > pos) { - // Save it if so - pos = data.elapsed; + case Variant::RECT2I: { + Rect2i i = p_intial_val; + Rect2i f = p_final_val; + return Rect2i(f.position - i.position, f.size - i.size); } - } - pending_update--; - return pos; -} - -real_t Tween::get_runtime() const { - // If the tween isn't moving, it'll last forever - if (speed_scale == 0) { - return INFINITY; - } - - pending_update++; - - // For each interpolation... - real_t runtime = 0.0; - for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the tween data and see if it's runtime is greater than the previous tweens - const InterpolateData &data = E->get(); - real_t t = data.delay + data.duration; - if (t > runtime) { - // This is the longest running tween - runtime = t; - } - } - pending_update--; - - // Adjust the runtime for the current speed scale - return runtime / speed_scale; -} - -bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val) { - // Get the initial, final, and delta values - const Variant &initial_val = p_initial_val; - const Variant &final_val = p_final_val; - Variant &delta_val = p_delta_val; - - // What kind of data are we interpolating? - switch (initial_val.get_type()) { - case Variant::BOOL: - // We'll treat booleans just like integers - case Variant::INT: - // Compute the integer delta - delta_val = (int)final_val - (int)initial_val; - break; - - case Variant::FLOAT: - // Convert to FLOAT and find the delta - delta_val = (real_t)final_val - (real_t)initial_val; - break; - - case Variant::VECTOR2: - // Convert to Vectors and find the delta - delta_val = final_val.operator Vector2() - initial_val.operator Vector2(); - break; - - case Variant::RECT2: { - // Build a new Rect2 and use the new position and sizes to make a delta - Rect2 i = initial_val; - Rect2 f = final_val; - delta_val = Rect2(f.position - i.position, f.size - i.size); - } break; - - case Variant::VECTOR3: - // Convert to Vectors and find the delta - delta_val = final_val.operator Vector3() - initial_val.operator Vector3(); - break; case Variant::TRANSFORM2D: { - // Build a new transform which is the difference between the initial and final values - Transform2D i = initial_val; - Transform2D f = final_val; - Transform2D d = Transform2D(); - d[0][0] = f.elements[0][0] - i.elements[0][0]; - d[0][1] = f.elements[0][1] - i.elements[0][1]; - d[1][0] = f.elements[1][0] - i.elements[1][0]; - d[1][1] = f.elements[1][1] - i.elements[1][1]; - d[2][0] = f.elements[2][0] - i.elements[2][0]; - d[2][1] = f.elements[2][1] - i.elements[2][1]; - delta_val = d; - } break; - - case Variant::QUATERNION: - // Convert to quaternianls and find the delta - delta_val = final_val.operator Quaternion() - initial_val.operator Quaternion(); - break; + Transform2D i = p_intial_val; + Transform2D f = p_final_val; + return Transform2D(f.elements[0][0] - i.elements[0][0], + f.elements[0][1] - i.elements[0][1], + f.elements[1][0] - i.elements[1][0], + f.elements[1][1] - i.elements[1][1], + f.elements[2][0] - i.elements[2][0], + f.elements[2][1] - i.elements[2][1]); + } case Variant::AABB: { - // Build a new AABB and use the new position and sizes to make a delta - AABB i = initial_val; - AABB f = final_val; - delta_val = AABB(f.position - i.position, f.size - i.size); - } break; + AABB i = p_intial_val; + AABB f = p_final_val; + return AABB(f.position - i.position, f.size - i.size); + } case Variant::BASIS: { - // Build a new basis which is the delta between the initial and final values - Basis i = initial_val; - Basis f = final_val; - delta_val = Basis(f.elements[0][0] - i.elements[0][0], + Basis i = p_intial_val; + Basis f = p_final_val; + return Basis(f.elements[0][0] - i.elements[0][0], f.elements[0][1] - i.elements[0][1], f.elements[0][2] - i.elements[0][2], f.elements[1][0] - i.elements[1][0], @@ -1227,14 +529,12 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final f.elements[2][0] - i.elements[2][0], f.elements[2][1] - i.elements[2][1], f.elements[2][2] - i.elements[2][2]); - } break; + } case Variant::TRANSFORM3D: { - // Build a new transform which is the difference between the initial and final values - Transform3D i = initial_val; - Transform3D f = final_val; - Transform3D d; - d.set(f.basis.elements[0][0] - i.basis.elements[0][0], + Transform3D i = p_intial_val; + Transform3D f = p_final_val; + return Transform3D(f.basis.elements[0][0] - i.basis.elements[0][0], f.basis.elements[0][1] - i.basis.elements[0][1], f.basis.elements[0][2] - i.basis.elements[0][2], f.basis.elements[1][0] - i.basis.elements[1][0], @@ -1246,569 +546,342 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final f.origin.x - i.origin.x, f.origin.y - i.origin.y, f.origin.z - i.origin.z); - - delta_val = d; - } break; - - case Variant::COLOR: { - // Make a new color which is the difference between each the color's RGBA attributes - Color i = initial_val; - Color f = final_val; - delta_val = Color(f.r - i.r, f.g - i.g, f.b - i.b, f.a - i.a); - } break; + } default: { - static Variant::Type supported_types[] = { - Variant::BOOL, - Variant::INT, - Variant::FLOAT, - Variant::VECTOR2, - Variant::RECT2, - Variant::VECTOR3, - Variant::TRANSFORM2D, - Variant::QUATERNION, - Variant::AABB, - Variant::BASIS, - Variant::TRANSFORM3D, - Variant::COLOR, - }; - - int length = *(&supported_types + 1) - supported_types; - String error_msg = "Invalid parameter type. Supported types are: "; - for (int i = 0; i < length; i++) { - if (i != 0) { - error_msg += ", "; - } - error_msg += Variant::get_type_name(supported_types[i]); - } - error_msg += "."; - ERR_PRINT(error_msg); - return false; + return Variant::evaluate(Variant::OP_SUBTRACT, p_final_val, p_intial_val); } }; - return true; } -void Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // TODO: Add initialization+implementation for remaining interpolation types - // TODO: Fix this method's organization to take advantage of the type - - // Make a new interpolation data - InterpolateData data; - data.active = true; - data.type = p_interpolation_type; - data.finish = false; - data.elapsed = 0; +void Tween::_bind_methods() { + ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property); + ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval); + ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback); + ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method); + + ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step); + ClassDB::bind_method(D_METHOD("stop"), &Tween::stop); + ClassDB::bind_method(D_METHOD("pause"), &Tween::pause); + ClassDB::bind_method(D_METHOD("play"), &Tween::play); + ClassDB::bind_method(D_METHOD("kill"), &Tween::kill); + + ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running); + ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid); + ClassDB::bind_method(D_METHOD("bind_node", "node"), &Tween::bind_node); + ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Tween::set_process_mode); + ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Tween::set_pause_mode); + + ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &Tween::set_parallel, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("set_loops", "loops"), &Tween::set_loops, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &Tween::set_speed_scale); + ClassDB::bind_method(D_METHOD("set_trans", "trans"), &Tween::set_trans); + ClassDB::bind_method(D_METHOD("set_ease", "ease"), &Tween::set_ease); - // Validate and apply interpolation data + ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel); + ClassDB::bind_method(D_METHOD("chain"), &Tween::chain); - // Give it the object - ERR_FAIL_COND_MSG(p_object == nullptr, "Invalid object provided to Tween."); - data.id = p_object->get_instance_id(); + ClassDB::bind_method(D_METHOD("interpolate_value", "trans_type", "ease_type", "elapsed_time", "initial_value", "delta_value", "duration"), &Tween::interpolate_variant); - // Validate the initial and final values - ERR_FAIL_COND_MSG(p_initial_val.get_type() != p_final_val.get_type(), "Initial value type '" + Variant::get_type_name(p_initial_val.get_type()) + "' does not match final value type '" + Variant::get_type_name(p_final_val.get_type()) + "'."); - data.initial_val = p_initial_val; - data.final_val = p_final_val; + ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx"))); + ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count"))); + ADD_SIGNAL(MethodInfo("finished")); - // Check the Duration - ERR_FAIL_COND_MSG(p_duration < 0, "Only non-negative duration values allowed in Tweens."); - data.duration = p_duration; + BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS); + BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE); - // Tween Delay - ERR_FAIL_COND_MSG(p_delay < 0, "Only non-negative delay values allowed in Tweens."); - data.delay = p_delay; + BIND_ENUM_CONSTANT(TWEEN_PAUSE_BOUND); + BIND_ENUM_CONSTANT(TWEEN_PAUSE_STOP); + BIND_ENUM_CONSTANT(TWEEN_PAUSE_PROCESS); - // Transition type - ERR_FAIL_COND_MSG(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, "Invalid transition type provided to Tween."); - data.trans_type = p_trans_type; + BIND_ENUM_CONSTANT(TRANS_LINEAR); + BIND_ENUM_CONSTANT(TRANS_SINE); + BIND_ENUM_CONSTANT(TRANS_QUINT); + BIND_ENUM_CONSTANT(TRANS_QUART); + BIND_ENUM_CONSTANT(TRANS_QUAD); + BIND_ENUM_CONSTANT(TRANS_EXPO); + BIND_ENUM_CONSTANT(TRANS_ELASTIC); + BIND_ENUM_CONSTANT(TRANS_CUBIC); + BIND_ENUM_CONSTANT(TRANS_CIRC); + BIND_ENUM_CONSTANT(TRANS_BOUNCE); + BIND_ENUM_CONSTANT(TRANS_BACK); - // Easing type - ERR_FAIL_COND_MSG(p_ease_type < 0 || p_ease_type >= EASE_COUNT, "Invalid easing type provided to Tween."); - data.ease_type = p_ease_type; + BIND_ENUM_CONSTANT(EASE_IN); + BIND_ENUM_CONSTANT(EASE_OUT); + BIND_ENUM_CONSTANT(EASE_IN_OUT); + BIND_ENUM_CONSTANT(EASE_OUT_IN); +} - // Is the property defined? - if (p_property) { - // Check that the object actually contains the given property - bool prop_valid = false; - p_object->get_indexed(p_property->get_subnames(), &prop_valid); - ERR_FAIL_COND_MSG(!prop_valid, "Tween target object has no property named: " + p_property->get_concatenated_subnames() + "."); +Ref<PropertyTweener> PropertyTweener::from(Variant p_value) { + initial_val = p_value; + do_continue = false; + return this; +} - data.key = p_property->get_subnames(); - data.concatenated_key = p_property->get_concatenated_subnames(); - } +Ref<PropertyTweener> PropertyTweener::from_current() { + do_continue = false; + return this; +} - // Is the method defined? - if (p_method) { - // Does the object even have the requested method? - ERR_FAIL_COND_MSG(!p_object->has_method(*p_method), "Tween target object has no method named: " + *p_method + "."); +Ref<PropertyTweener> PropertyTweener::as_relative() { + relative = true; + return this; +} - data.key.push_back(*p_method); - data.concatenated_key = *p_method; - } +Ref<PropertyTweener> PropertyTweener::set_trans(Tween::TransitionType p_trans) { + trans_type = p_trans; + return this; +} - // Is there not a valid delta? - if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val)) { - return; - } +Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) { + ease_type = p_ease; + return this; +} - // Add this interpolation to the total - _push_interpolate_data(data); +Ref<PropertyTweener> PropertyTweener::set_delay(float p_delay) { + delay = p_delay; + return this; } -void Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are busy updating, call this function again later - if (pending_update != 0) { - _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); +void PropertyTweener::start() { + elapsed_time = 0; + finished = false; + + Object *target_instance = ObjectDB::get_instance(target); + if (!target_instance) { + WARN_PRINT("Target object freed before starting, aborting Tweener."); return; } - // Check that the target object is valid - ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name())); - - // Get the property from the node path - p_property = p_property.get_as_property_path(); - - // If no initial value given, grab the initial value from the object - // TODO: Is this documented? This is very useful and removes a lot of clutter from tweens! - if (p_initial_val.get_type() == Variant::NIL) { - p_initial_val = p_object->get_indexed(p_property.get_subnames()); + if (do_continue) { + initial_val = target_instance->get_indexed(property); } - // Convert any integers into REALs as they are better for interpolation - if (p_initial_val.get_type() == Variant::INT) { - p_initial_val = p_initial_val.operator real_t(); - } - if (p_final_val.get_type() == Variant::INT) { - p_final_val = p_final_val.operator real_t(); + if (relative) { + final_val = Variant::evaluate(Variant::Operator::OP_ADD, initial_val, base_final_val); } - // Build the interpolation data - _build_interpolation(INTER_PROPERTY, p_object, &p_property, nullptr, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); + delta_val = tween->calculate_delta_value(initial_val, final_val); } -void Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are busy updating, call this function again later - if (pending_update != 0) { - _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); - return; +bool PropertyTweener::step(float &r_delta) { + if (finished) { + // This is needed in case there's a parallel Tweener with longer duration. + return false; } - // Check that the target object is valid - ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name())); - - // Convert any integers into REALs as they are better for interpolation - if (p_initial_val.get_type() == Variant::INT) { - p_initial_val = p_initial_val.operator real_t(); - } - if (p_final_val.get_type() == Variant::INT) { - p_final_val = p_final_val.operator real_t(); + Object *target_instance = ObjectDB::get_instance(target); + if (!target_instance) { + return false; } + elapsed_time += r_delta; - // Build the interpolation data - _build_interpolation(INTER_METHOD, p_object, nullptr, &p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); -} - -void Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) { - // If we are already updating, call this function again later - if (pending_update != 0) { - _add_pending_command("interpolate_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); - return; + if (elapsed_time < delay) { + r_delta = 0; + return true; } - // Check that the target object is valid - ERR_FAIL_COND(p_object == nullptr); - - // Duration cannot be negative - ERR_FAIL_COND(p_duration < 0); - - // Check whether the object even has the callback - ERR_FAIL_COND_MSG(!p_object->has_method(p_callback), "Object has no callback named: " + p_callback + "."); - - // Build a new InterpolationData - InterpolateData data; - data.active = true; - data.type = INTER_CALLBACK; - data.finish = false; - data.call_deferred = false; - data.elapsed = 0; - - // Give the data it's configuration - data.id = p_object->get_instance_id(); - data.key.push_back(p_callback); - data.concatenated_key = p_callback; - data.duration = p_duration; - data.delay = 0; - - // Add arguments to the interpolation - int args = 0; - if (p_arg5.get_type() != Variant::NIL) { - args = 5; - } else if (p_arg4.get_type() != Variant::NIL) { - args = 4; - } else if (p_arg3.get_type() != Variant::NIL) { - args = 3; - } else if (p_arg2.get_type() != Variant::NIL) { - args = 2; - } else if (p_arg1.get_type() != Variant::NIL) { - args = 1; + float time = MIN(elapsed_time - delay, duration); + target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type)); + + if (time < duration) { + r_delta = 0; + return true; } else { - args = 0; + finished = true; + r_delta = elapsed_time - delay - duration; + emit_signal("finished"); + return false; } - - data.args = args; - data.arg[0] = p_arg1; - data.arg[1] = p_arg2; - data.arg[2] = p_arg3; - data.arg[3] = p_arg4; - data.arg[4] = p_arg5; - - // Add the new interpolation - _push_interpolate_data(data); } -void Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) { - // If we are already updating, call this function again later - if (pending_update != 0) { - _add_pending_command("interpolate_deferred_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); - return; +void PropertyTweener::set_tween(Ref<Tween> p_tween) { + tween = p_tween; + if (trans_type == Tween::TRANS_MAX) { + trans_type = tween->get_trans(); } - - // Check that the target object is valid - ERR_FAIL_COND(p_object == nullptr); - - // No negative durations allowed - ERR_FAIL_COND(p_duration < 0); - - // Confirm the callback exists on the object - ERR_FAIL_COND_MSG(!p_object->has_method(p_callback), "Object has no callback named: " + p_callback + "."); - - // Create a new InterpolateData for the callback - InterpolateData data; - data.active = true; - data.type = INTER_CALLBACK; - data.finish = false; - data.call_deferred = true; - data.elapsed = 0; - - // Give the data it's configuration - data.id = p_object->get_instance_id(); - data.key.push_back(p_callback); - data.concatenated_key = p_callback; - data.duration = p_duration; - data.delay = 0; - - // Collect arguments for the callback - int args = 0; - if (p_arg5.get_type() != Variant::NIL) { - args = 5; - } else if (p_arg4.get_type() != Variant::NIL) { - args = 4; - } else if (p_arg3.get_type() != Variant::NIL) { - args = 3; - } else if (p_arg2.get_type() != Variant::NIL) { - args = 2; - } else if (p_arg1.get_type() != Variant::NIL) { - args = 1; - } else { - args = 0; + if (ease_type == Tween::EASE_MAX) { + ease_type = tween->get_ease(); } +} - data.args = args; - data.arg[0] = p_arg1; - data.arg[1] = p_arg2; - data.arg[2] = p_arg3; - data.arg[3] = p_arg4; - data.arg[4] = p_arg5; +void PropertyTweener::_bind_methods() { + ClassDB::bind_method(D_METHOD("from", "value"), &PropertyTweener::from); + ClassDB::bind_method(D_METHOD("from_current"), &PropertyTweener::from_current); + ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative); + ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans); + ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease); + ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay); +} - // Add the new interpolation - _push_interpolate_data(data); +PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration) { + target = p_target->get_instance_id(); + property = p_property.get_as_property_path().get_subnames(); + initial_val = p_target->get_indexed(property); + base_final_val = p_to; + final_val = base_final_val; + duration = p_duration; } -void Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are already updating, call this function again later - if (pending_update != 0) { - _add_pending_command("follow_property", p_object, p_property, p_initial_val, p_target, p_target_property, p_duration, p_trans_type, p_ease_type, p_delay); - return; - } +PropertyTweener::PropertyTweener() { + ERR_FAIL_MSG("Can't create empty PropertyTweener. Use get_tree().tween_property() or tween_property() instead."); +} - // Get the two properties from their paths - p_property = p_property.get_as_property_path(); - p_target_property = p_target_property.get_as_property_path(); +void IntervalTweener::start() { + elapsed_time = 0; + finished = false; +} - // If no initial value is given, grab it from the source object - // TODO: Is this documented? It's really helpful for decluttering tweens - if (p_initial_val.get_type() == Variant::NIL) { - p_initial_val = p_object->get_indexed(p_property.get_subnames()); +bool IntervalTweener::step(float &r_delta) { + if (finished) { + return false; } - // Convert initial INT values to FLOAT as they are better for interpolation - if (p_initial_val.get_type() == Variant::INT) { - p_initial_val = p_initial_val.operator real_t(); + elapsed_time += r_delta; + + if (elapsed_time < duration) { + r_delta = 0; + return true; + } else { + finished = true; + r_delta = elapsed_time - duration; + emit_signal("finished"); + return false; } +} - // Confirm the source and target objects are valid - ERR_FAIL_COND(p_object == nullptr); - ERR_FAIL_COND(p_target == nullptr); +IntervalTweener::IntervalTweener(float p_time) { + duration = p_time; +} - // No negative durations - ERR_FAIL_COND(p_duration < 0); +IntervalTweener::IntervalTweener() { + ERR_FAIL_MSG("Can't create empty IntervalTweener. Use get_tree().tween_interval() instead."); +} - // Ensure transition and easing types are valid - ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT); - ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT); +Ref<CallbackTweener> CallbackTweener::set_delay(float p_delay) { + delay = p_delay; + return this; +} - // No negative delays - ERR_FAIL_COND(p_delay < 0); +void CallbackTweener::start() { + elapsed_time = 0; + finished = false; +} - // Confirm the source and target objects have the desired properties - bool prop_valid = false; - p_object->get_indexed(p_property.get_subnames(), &prop_valid); - ERR_FAIL_COND(!prop_valid); +bool CallbackTweener::step(float &r_delta) { + if (finished) { + return false; + } - bool target_prop_valid = false; - Variant target_val = p_target->get_indexed(p_target_property.get_subnames(), &target_prop_valid); - ERR_FAIL_COND(!target_prop_valid); + elapsed_time += r_delta; + if (elapsed_time >= delay) { + Variant result; + Callable::CallError ce; + callback.call(nullptr, 0, result, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(this, callback.get_method(), nullptr, 0, ce)); + } - // Convert target INT to FLOAT since it is better for interpolation - if (target_val.get_type() == Variant::INT) { - target_val = target_val.operator real_t(); + finished = true; + r_delta = elapsed_time - delay; + emit_signal("finished"); + return false; } - // Verify that the target value and initial value are the same type - ERR_FAIL_COND(target_val.get_type() != p_initial_val.get_type()); - - // Create a new InterpolateData - InterpolateData data; - data.active = true; - data.type = FOLLOW_PROPERTY; - data.finish = false; - data.elapsed = 0; - - // Give the InterpolateData it's configuration - data.id = p_object->get_instance_id(); - data.key = p_property.get_subnames(); - data.concatenated_key = p_property.get_concatenated_subnames(); - data.initial_val = p_initial_val; - data.target_id = p_target->get_instance_id(); - data.target_key = p_target_property.get_subnames(); - data.duration = p_duration; - data.trans_type = p_trans_type; - data.ease_type = p_ease_type; - data.delay = p_delay; - - // Add the interpolation - _push_interpolate_data(data); -} - -void Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are currently updating, call this function again later - if (pending_update != 0) { - _add_pending_command("follow_method", p_object, p_method, p_initial_val, p_target, p_target_method, p_duration, p_trans_type, p_ease_type, p_delay); - return; - } - // Convert initial INT values to FLOAT as they are better for interpolation - if (p_initial_val.get_type() == Variant::INT) { - p_initial_val = p_initial_val.operator real_t(); - } + r_delta = 0; + return true; +} - // Verify the source and target objects are valid - ERR_FAIL_COND(p_object == nullptr); - ERR_FAIL_COND(p_target == nullptr); +void CallbackTweener::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_delay", "delay"), &CallbackTweener::set_delay); +} - // No negative durations - ERR_FAIL_COND(p_duration < 0); +CallbackTweener::CallbackTweener(Callable p_callback) { + callback = p_callback; +} - // Ensure that the transition and ease types are valid - ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT); - ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT); +CallbackTweener::CallbackTweener() { + ERR_FAIL_MSG("Can't create empty CallbackTweener. Use get_tree().tween_callback() instead."); +} - // No negative delays - ERR_FAIL_COND(p_delay < 0); +Ref<MethodTweener> MethodTweener::set_delay(float p_delay) { + delay = p_delay; + return this; +} - // Confirm both objects have the target methods - ERR_FAIL_COND_MSG(!p_object->has_method(p_method), "Object has no method named: " + p_method + "."); - ERR_FAIL_COND_MSG(!p_target->has_method(p_target_method), "Target has no method named: " + p_target_method + "."); +Ref<MethodTweener> MethodTweener::set_trans(Tween::TransitionType p_trans) { + trans_type = p_trans; + return this; +} - // Call the method to get the target value - Callable::CallError error; - Variant target_val = p_target->call(p_target_method, nullptr, 0, error); - ERR_FAIL_COND(error.error != Callable::CallError::CALL_OK); +Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) { + ease_type = p_ease; + return this; +} - // Convert target INT values to FLOAT as they are better for interpolation - if (target_val.get_type() == Variant::INT) { - target_val = target_val.operator real_t(); - } - ERR_FAIL_COND(target_val.get_type() != p_initial_val.get_type()); - - // Make the new InterpolateData for the method follow - InterpolateData data; - data.active = true; - data.type = FOLLOW_METHOD; - data.finish = false; - data.elapsed = 0; - - // Give the data it's configuration - data.id = p_object->get_instance_id(); - data.key.push_back(p_method); - data.concatenated_key = p_method; - data.initial_val = p_initial_val; - data.target_id = p_target->get_instance_id(); - data.target_key.push_back(p_target_method); - data.duration = p_duration; - data.trans_type = p_trans_type; - data.ease_type = p_ease_type; - data.delay = p_delay; - - // Add the new interpolation - _push_interpolate_data(data); -} - -void Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are currently updating, call this function again later - if (pending_update != 0) { - _add_pending_command("targeting_property", p_object, p_property, p_initial, p_initial_property, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); - return; - } - // Grab the target property and the target property - p_property = p_property.get_as_property_path(); - p_initial_property = p_initial_property.get_as_property_path(); +void MethodTweener::start() { + elapsed_time = 0; + finished = false; +} - // Convert the initial INT values to FLOAT as they are better for Interpolation - if (p_final_val.get_type() == Variant::INT) { - p_final_val = p_final_val.operator real_t(); +bool MethodTweener::step(float &r_delta) { + if (finished) { + return false; } - // Verify both objects are valid - ERR_FAIL_COND(p_object == nullptr); - ERR_FAIL_COND(p_initial == nullptr); - - // No negative durations - ERR_FAIL_COND(p_duration < 0); - - // Ensure transition and easing types are valid - ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT); - ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT); - - // No negative delays - ERR_FAIL_COND(p_delay < 0); - - // Ensure the initial and target properties exist on their objects - bool prop_valid = false; - p_object->get_indexed(p_property.get_subnames(), &prop_valid); - ERR_FAIL_COND(!prop_valid); + elapsed_time += r_delta; - bool initial_prop_valid = false; - Variant initial_val = p_initial->get_indexed(p_initial_property.get_subnames(), &initial_prop_valid); - ERR_FAIL_COND(!initial_prop_valid); - - // Convert the initial INT value to FLOAT as it is better for interpolation - if (initial_val.get_type() == Variant::INT) { - initial_val = initial_val.operator real_t(); - } - ERR_FAIL_COND(initial_val.get_type() != p_final_val.get_type()); - - // Build the InterpolateData object - InterpolateData data; - data.active = true; - data.type = TARGETING_PROPERTY; - data.finish = false; - data.elapsed = 0; - - // Give the data it's configuration - data.id = p_object->get_instance_id(); - data.key = p_property.get_subnames(); - data.concatenated_key = p_property.get_concatenated_subnames(); - data.target_id = p_initial->get_instance_id(); - data.target_key = p_initial_property.get_subnames(); - data.initial_val = initial_val; - data.final_val = p_final_val; - data.duration = p_duration; - data.trans_type = p_trans_type; - data.ease_type = p_ease_type; - data.delay = p_delay; - - // Ensure there is a valid delta - if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val)) { - return; + if (elapsed_time < delay) { + r_delta = 0; + return true; } - // Add the interpolation - _push_interpolate_data(data); -} + float time = MIN(elapsed_time - delay, duration); + Variant current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type); + const Variant **argptr = (const Variant **)alloca(sizeof(Variant *)); + argptr[0] = ¤t_val; -void Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { - // If we are currently updating, call this function again later - if (pending_update != 0) { - _add_pending_command("targeting_method", p_object, p_method, p_initial, p_initial_method, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); - return; + Variant result; + Callable::CallError ce; + callback.call(argptr, 1, result, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(this, callback.get_method(), argptr, 1, ce)); } - // Convert final INT values to FLOAT as they are better for interpolation - if (p_final_val.get_type() == Variant::INT) { - p_final_val = p_final_val.operator real_t(); + if (time < duration) { + r_delta = 0; + return true; + } else { + finished = true; + r_delta = elapsed_time - delay - duration; + emit_signal("finished"); + return false; } +} - // Make sure the given objects are valid - ERR_FAIL_COND(p_object == nullptr); - ERR_FAIL_COND(p_initial == nullptr); - - // No negative durations - ERR_FAIL_COND(p_duration < 0); - - // Ensure transition and easing types are valid - ERR_FAIL_COND(p_trans_type < 0 || p_trans_type >= TRANS_COUNT); - ERR_FAIL_COND(p_ease_type < 0 || p_ease_type >= EASE_COUNT); - - // No negative delays - ERR_FAIL_COND(p_delay < 0); - - // Make sure both objects have the given method - ERR_FAIL_COND_MSG(!p_object->has_method(p_method), "Object has no method named: " + p_method + "."); - ERR_FAIL_COND_MSG(!p_initial->has_method(p_initial_method), "Initial Object has no method named: " + p_initial_method + "."); - - // Call the method to get the initial value - Callable::CallError error; - Variant initial_val = p_initial->call(p_initial_method, nullptr, 0, error); - ERR_FAIL_COND(error.error != Callable::CallError::CALL_OK); - - // Convert initial INT values to FLOAT as they aer better for interpolation - if (initial_val.get_type() == Variant::INT) { - initial_val = initial_val.operator real_t(); +void MethodTweener::set_tween(Ref<Tween> p_tween) { + tween = p_tween; + if (trans_type == Tween::TRANS_MAX) { + trans_type = tween->get_trans(); } - ERR_FAIL_COND(initial_val.get_type() != p_final_val.get_type()); - - // Build the new InterpolateData object - InterpolateData data; - data.active = true; - data.type = TARGETING_METHOD; - data.finish = false; - data.elapsed = 0; - - // Configure the data - data.id = p_object->get_instance_id(); - data.key.push_back(p_method); - data.concatenated_key = p_method; - data.target_id = p_initial->get_instance_id(); - data.target_key.push_back(p_initial_method); - data.initial_val = initial_val; - data.final_val = p_final_val; - data.duration = p_duration; - data.trans_type = p_trans_type; - data.ease_type = p_ease_type; - data.delay = p_delay; - - // Ensure there is a valid delta - if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val)) { - return; + if (ease_type == Tween::EASE_MAX) { + ease_type = tween->get_ease(); } +} - // Add the interpolation - _push_interpolate_data(data); +void MethodTweener::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_delay", "delay"), &MethodTweener::set_delay); + ClassDB::bind_method(D_METHOD("set_trans", "trans"), &MethodTweener::set_trans); + ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease); } -Tween::Tween() { +MethodTweener::MethodTweener(Callable p_callback, float p_from, float p_to, float p_duration) { + callback = p_callback; + initial_val = p_from; + delta_val = tween->calculate_delta_value(p_from, p_to); + duration = p_duration; } -Tween::~Tween() { +MethodTweener::MethodTweener() { + ERR_FAIL_MSG("Can't create empty MethodTweener. Use get_tree().tween_method() instead."); } diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 142c0c65e0..947cdb7c2d 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -31,10 +31,33 @@ #ifndef TWEEN_H #define TWEEN_H -#include "scene/main/node.h" +#include "core/object/ref_counted.h" -class Tween : public Node { - GDCLASS(Tween, Node); +class Tween; +class Node; + +class Tweener : public RefCounted { + GDCLASS(Tweener, RefCounted); + +public: + virtual void set_tween(Ref<Tween> p_tween); + virtual void start() = 0; + virtual bool step(float &r_delta) = 0; + +protected: + static void _bind_methods(); + Ref<Tween> tween; + float elapsed_time = 0; + bool finished = false; +}; + +class PropertyTweener; +class IntervalTweener; +class CallbackTweener; +class MethodTweener; + +class Tween : public RefCounted { + GDCLASS(Tween, RefCounted); public: enum TweenProcessMode { @@ -42,6 +65,12 @@ public: TWEEN_PROCESS_IDLE, }; + enum TweenPauseMode { + TWEEN_PAUSE_BOUND, + TWEEN_PAUSE_STOP, + TWEEN_PAUSE_PROCESS, + }; + enum TransitionType { TRANS_LINEAR, TRANS_SINE, @@ -54,8 +83,7 @@ public: TRANS_CIRC, TRANS_BOUNCE, TRANS_BACK, - - TRANS_COUNT, + TRANS_MAX }; enum EaseType { @@ -63,130 +91,187 @@ public: EASE_OUT, EASE_IN_OUT, EASE_OUT_IN, - - EASE_COUNT, + EASE_MAX }; private: - enum InterpolateType { - INTER_PROPERTY, - INTER_METHOD, - FOLLOW_PROPERTY, - FOLLOW_METHOD, - TARGETING_PROPERTY, - TARGETING_METHOD, - INTER_CALLBACK, - }; + TweenProcessMode process_mode = TweenProcessMode::TWEEN_PROCESS_IDLE; + TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_STOP; + TransitionType default_transition = TransitionType::TRANS_LINEAR; + EaseType default_ease = EaseType::EASE_IN_OUT; + ObjectID bound_node; - struct InterpolateData { - bool active = false; - InterpolateType type = INTER_CALLBACK; - bool finish = false; - bool call_deferred = false; - real_t elapsed = 0.0; - ObjectID id; - Vector<StringName> key; - StringName concatenated_key; - Variant initial_val; - Variant delta_val; - Variant final_val; - ObjectID target_id; - Vector<StringName> target_key; - real_t duration = 0.0; - TransitionType trans_type = TransitionType::TRANS_BACK; - EaseType ease_type = EaseType::EASE_COUNT; - real_t delay = 0.0; - int args = 0; - Variant arg[5]; - int uid = 0; - }; + Vector<List<Ref<Tweener>>> tweeners; + int current_step = -1; + int loops = 1; + int loops_done = 0; + float speed_scale = 1; - String autoplay; - TweenProcessMode tween_process_mode = TWEEN_PROCESS_IDLE; - bool repeat = false; - float speed_scale = 1.0; - mutable int pending_update = 0; - int uid = 0; - bool was_stopped = false; + bool is_bound = false; + bool started = false; + bool running = true; + bool dead = false; + bool invalid = true; + bool default_parallel = false; + bool parallel_enabled = false; - List<InterpolateData> interpolates; + typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d); + static interpolater interpolaters[TRANS_MAX][EASE_MAX]; - struct PendingCommand { - StringName key; - int args = 0; - Variant arg[10]; - }; - List<PendingCommand> pending_commands; + void start_tweeners(); - void _add_pending_command(StringName p_key, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant(), const Variant &p_arg9 = Variant(), const Variant &p_arg10 = Variant()); - void _process_pending_commands(); +protected: + static void _bind_methods(); - typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d); - static interpolater interpolaters[TRANS_COUNT][EASE_COUNT]; +public: + Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration); + Ref<IntervalTweener> tween_interval(float p_time); + Ref<CallbackTweener> tween_callback(Callable p_callback); + Ref<MethodTweener> tween_method(Callable p_callback, float p_from, float p_to, float p_duration); + Ref<Tween> append(Ref<Tweener> p_tweener); - real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); - Variant &_get_delta_val(InterpolateData &p_data); - Variant _get_initial_val(const InterpolateData &p_data) const; - Variant _get_final_val(const InterpolateData &p_data) const; - Variant _run_equation(InterpolateData &p_data); - bool _calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val); - bool _apply_tween_value(InterpolateData &p_data, Variant &value); + bool custom_step(float p_delta); + void stop(); + void pause(); + void play(); + void kill(); - void _tween_process(float p_delta); - void _remove_by_uid(int uid); - void _push_interpolate_data(InterpolateData &p_data); - void _build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay); + bool is_running(); + void set_valid(bool p_valid); + bool is_valid(); -protected: - 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; - void _notification(int p_what); + Ref<Tween> bind_node(Node *p_node); + Ref<Tween> set_process_mode(TweenProcessMode p_mode); + TweenProcessMode get_process_mode(); + Ref<Tween> set_pause_mode(TweenPauseMode p_mode); + TweenPauseMode get_pause_mode(); - static void _bind_methods(); + Ref<Tween> set_parallel(bool p_parallel); + Ref<Tween> set_loops(int p_loops); + Ref<Tween> set_speed_scale(float p_speed); + Ref<Tween> set_trans(TransitionType p_trans); + TransitionType get_trans(); + Ref<Tween> set_ease(EaseType p_ease); + EaseType get_ease(); -public: - bool is_active() const; - void set_active(bool p_active); - - bool is_repeat() const; - void set_repeat(bool p_repeat); - - void set_tween_process_mode(TweenProcessMode p_mode); - TweenProcessMode get_tween_process_mode() const; - - void set_speed_scale(float p_speed); - float get_speed_scale() const; - - void start(); - void reset(Object *p_object, StringName p_key); - void reset_all(); - void stop(Object *p_object, StringName p_key); - void stop_all(); - void resume(Object *p_object, StringName p_key); - void resume_all(); - void remove(Object *p_object, StringName p_key); - void remove_all(); - - void seek(real_t p_time); - real_t tell() const; - real_t get_runtime() const; - - void interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - void interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - void interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE); - void interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE); - void follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - void follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - void targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - void targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type = TRANS_LINEAR, EaseType p_ease_type = EASE_IN_OUT, real_t p_delay = 0); - - Tween(); - ~Tween(); + Ref<Tween> parallel(); + Ref<Tween> chain(); + + real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); + Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); + Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val); + + bool step(float p_delta); + bool should_pause(); + + Tween() {} }; +VARIANT_ENUM_CAST(Tween::TweenPauseMode); VARIANT_ENUM_CAST(Tween::TweenProcessMode); VARIANT_ENUM_CAST(Tween::TransitionType); VARIANT_ENUM_CAST(Tween::EaseType); +class PropertyTweener : public Tweener { + GDCLASS(PropertyTweener, Tweener); + +public: + Ref<PropertyTweener> from(Variant p_value); + Ref<PropertyTweener> from_current(); + Ref<PropertyTweener> as_relative(); + Ref<PropertyTweener> set_trans(Tween::TransitionType p_trans); + Ref<PropertyTweener> set_ease(Tween::EaseType p_ease); + Ref<PropertyTweener> set_delay(float p_delay); + + void set_tween(Ref<Tween> p_tween) override; + void start() override; + bool step(float &r_delta) override; + + PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration); + PropertyTweener(); + +protected: + static void _bind_methods(); + +private: + ObjectID target; + Vector<StringName> property; + Variant initial_val; + Variant base_final_val; + Variant final_val; + Variant delta_val; + + float duration = 0; + Tween::TransitionType trans_type = Tween::TRANS_MAX; // This is set inside set_tween(); + Tween::EaseType ease_type = Tween::EASE_MAX; + + float delay = 0; + bool do_continue = true; + bool relative = false; +}; + +class IntervalTweener : public Tweener { + GDCLASS(IntervalTweener, Tweener); + +public: + void start() override; + bool step(float &r_delta) override; + + IntervalTweener(float p_time); + IntervalTweener(); + +private: + float duration = 0; +}; + +class CallbackTweener : public Tweener { + GDCLASS(CallbackTweener, Tweener); + +public: + Ref<CallbackTweener> set_delay(float p_delay); + + void start() override; + bool step(float &r_delta) override; + + CallbackTweener(Callable p_callback); + CallbackTweener(); + +protected: + static void _bind_methods(); + +private: + Callable callback; + float delay = 0; +}; + +class MethodTweener : public Tweener { + GDCLASS(MethodTweener, Tweener); + +public: + Ref<MethodTweener> set_trans(Tween::TransitionType p_trans); + Ref<MethodTweener> set_ease(Tween::EaseType p_ease); + Ref<MethodTweener> set_delay(float p_delay); + + void set_tween(Ref<Tween> p_tween) override; + void start() override; + bool step(float &r_delta) override; + + MethodTweener(Callable p_callback, float p_from, float p_to, float p_duration); + MethodTweener(); + +protected: + static void _bind_methods(); + +private: + float duration = 0; + float delay = 0; + Tween::TransitionType trans_type = Tween::TRANS_MAX; + Tween::EaseType ease_type = Tween::EASE_MAX; + + Ref<Tween> tween; + Variant initial_val; + Variant delta_val; + Callable callback; +}; + #endif diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 11ce9b2ddc..1e121ab6e5 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -525,7 +525,7 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -569,7 +569,7 @@ void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_A for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -652,7 +652,7 @@ void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_typ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -661,7 +661,7 @@ void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_typ } Node *n2 = n->get_node(p_parent); - Node *no = Object::cast_to<Node>(ClassDB::instance(p_type)); + Node *no = Object::cast_to<Node>(ClassDB::instantiate(p_type)); if (!no) { continue; } @@ -696,7 +696,7 @@ void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_p for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -705,7 +705,7 @@ void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_p } Node *n2 = n->get_node(p_parent); - Node *no = ps->instance(); + Node *no = ps->instantiate(); if (!no) { continue; } @@ -736,7 +736,7 @@ void LiveEditor::_remove_node_func(const NodePath &p_at) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -772,7 +772,7 @@ void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_kee Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -811,7 +811,7 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -862,7 +862,7 @@ void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -901,7 +901,7 @@ void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 66155958cf..c1ae0479f5 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -391,7 +391,7 @@ bool BaseButton::_is_focus_owner_in_shorcut_context() const { Control *vp_focus = get_focus_owner(); // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. - return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_a_parent_of(vp_focus)); + return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); } void BaseButton::_bind_methods() { diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index c0df5271b4..bcc273114b 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -49,9 +49,14 @@ Size2 Button::get_minimum_size() const { if (!_icon.is_null()) { minsize.height = MAX(minsize.height, _icon->get_height()); - minsize.width += _icon->get_width(); - if (xl_text != "") { - minsize.width += get_theme_constant("hseparation"); + + if (icon_align != ALIGN_CENTER) { + minsize.width += _icon->get_width(); + if (xl_text != "") { + minsize.width += get_theme_constant("hseparation"); + } + } else { + minsize.width = MAX(minsize.width, _icon->get_width()); } } } @@ -202,6 +207,21 @@ void Button::_notification(int p_what) { } Rect2 icon_region = Rect2(); + TextAlign icon_align_rtl_checked = icon_align; + TextAlign align_rtl_checked = align; + // Swap icon and text alignment sides if right-to-left layout is set. + if (rtl) { + if (icon_align == ALIGN_RIGHT) { + icon_align_rtl_checked = ALIGN_LEFT; + } else if (icon_align == ALIGN_LEFT) { + icon_align_rtl_checked = ALIGN_RIGHT; + } + if (align == ALIGN_RIGHT) { + align_rtl_checked = ALIGN_LEFT; + } else if (align == ALIGN_LEFT) { + align_rtl_checked = ALIGN_RIGHT; + } + } if (!_icon.is_null()) { int valign = size.height - style->get_minimum_size().y; if (is_disabled()) { @@ -209,20 +229,27 @@ void Button::_notification(int p_what) { } float icon_ofs_region = 0.0; - if (rtl) { - if (_internal_margin[SIDE_RIGHT] > 0) { - icon_ofs_region = _internal_margin[SIDE_RIGHT] + get_theme_constant("hseparation"); - } - } else { + Point2 style_offset; + Size2 icon_size = _icon->get_size(); + if (icon_align_rtl_checked == ALIGN_LEFT) { + style_offset.x = style->get_margin(SIDE_LEFT); if (_internal_margin[SIDE_LEFT] > 0) { icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); } + } else if (icon_align_rtl_checked == ALIGN_CENTER) { + style_offset.x = 0.0; + } else if (icon_align_rtl_checked == ALIGN_RIGHT) { + style_offset.x = -style->get_margin(SIDE_RIGHT); + if (_internal_margin[SIDE_RIGHT] > 0) { + icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); + } } + style_offset.y = style->get_margin(SIDE_TOP); if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; _size.width -= get_theme_constant("hseparation") + icon_ofs_region; - if (!clip_text) { + if (!clip_text && icon_align_rtl_checked != ALIGN_CENTER) { _size.width -= text_buf->get_size().width; } float icon_width = _icon->get_width() * _size.height / _icon->get_height(); @@ -233,21 +260,26 @@ void Button::_notification(int p_what) { icon_height = _icon->get_height() * icon_width / _icon->get_width(); } - if (rtl) { - icon_region = Rect2(Point2(size.width - (icon_ofs_region + icon_width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); - } else { - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); - } + icon_size = Size2(icon_width, icon_height); + } + + if (icon_align_rtl_checked == ALIGN_LEFT) { + icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size); + } else if (icon_align_rtl_checked == ALIGN_CENTER) { + icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), Math::floor((valign - icon_size.y) * 0.5)), icon_size); } else { - if (rtl) { - icon_region = Rect2(Point2(size.width - (icon_ofs_region + _icon->get_size().width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); - } else { - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); - } + icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, Math::floor((valign - icon_size.y) * 0.5)), icon_size); + } + + if (icon_region.size.width > 0) { + draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); } } Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant("hseparation"), 0) : Point2(); + if (align_rtl_checked == ALIGN_CENTER && icon_align_rtl_checked == ALIGN_CENTER) { + icon_ofs.x = 0.0; + } int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; text_buf->set_width(clip_text ? text_clip : -1); @@ -262,20 +294,15 @@ void Button::_notification(int p_what) { Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; - switch (align) { + switch (align_rtl_checked) { case ALIGN_LEFT: { - if (rtl) { - if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); - } else { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; - } + if (icon_align_rtl_checked != ALIGN_LEFT) { + icon_ofs.x = 0.0; + } + if (_internal_margin[SIDE_LEFT] > 0) { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); } else { - if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; - } + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; } text_ofs.y += style->get_offset().y; } break; @@ -283,31 +310,24 @@ void Button::_notification(int p_what) { if (text_ofs.x < 0) { text_ofs.x = 0; } - text_ofs += icon_ofs; + if (icon_align_rtl_checked == ALIGN_LEFT) { + text_ofs += icon_ofs; + } text_ofs += style->get_offset(); } break; case ALIGN_RIGHT: { - if (rtl) { - if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; - } + if (_internal_margin[SIDE_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); } else { - if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); - } else { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; - } + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; } text_ofs.y += style->get_offset().y; + if (icon_align_rtl_checked == ALIGN_RIGHT) { + text_ofs.x -= icon_ofs.x; + } } break; } - if (rtl) { - text_ofs.x -= icon_ofs.x; - } - Color font_outline_color = get_theme_color("font_outline_color"); int outline_size = get_theme_constant("outline_size"); if (outline_size > 0 && font_outline_color.a > 0) { @@ -315,10 +335,6 @@ void Button::_notification(int p_what) { } text_buf->draw(ci, text_ofs, color); - - if (!_icon.is_null() && icon_region.size.width > 0) { - draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); - } } break; } } @@ -457,6 +473,16 @@ Button::TextAlign Button::get_text_align() const { return align; } +void Button::set_icon_align(TextAlign p_align) { + icon_align = p_align; + minimum_size_changed(); + update(); +} + +Button::TextAlign Button::get_icon_align() const { + return icon_align; +} + bool Button::_set(const StringName &p_name, const Variant &p_value) { String str = p_name; if (str.begins_with("opentype_features/")) { @@ -519,14 +545,16 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); - ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); - ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); + ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); ClassDB::bind_method(D_METHOD("set_text_align", "align"), &Button::set_text_align); ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align); - ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat); + ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align); + ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align); + ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -539,11 +567,12 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_align", "get_icon_align"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); } Button::Button(const String &p_text) { - text_buf.instance(); + text_buf.instantiate(); text_buf->set_flags(TextServer::BREAK_MANDATORY); set_mouse_filter(MOUSE_FILTER_STOP); diff --git a/scene/gui/button.h b/scene/gui/button.h index d968f63f51..253d079680 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -58,6 +58,7 @@ private: bool expand_icon = false; bool clip_text = false; TextAlign align = ALIGN_CENTER; + TextAlign icon_align = ALIGN_LEFT; float _internal_margin[4] = {}; void _shape(); @@ -102,6 +103,9 @@ public: void set_text_align(TextAlign p_align); TextAlign get_text_align() const; + void set_icon_align(TextAlign p_align); + TextAlign get_icon_align() const; + Button(const String &p_text = String()); ~Button(); }; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 5b720945b8..ba1534ed5c 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -122,7 +122,7 @@ void CodeEdit::_notification(int p_what) { ERR_CONTINUE(l < 0 || l >= code_completion_options_count); Ref<TextLine> tl; - tl.instance(); + tl.instantiate(); tl->add_string(code_completion_options[l].display, cache.font, cache.font_size); int yofs = (row_height - tl->get_size().y) / 2; @@ -248,6 +248,8 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } update(); } break; + default: + break; } return; } @@ -358,7 +360,7 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } if (k->is_action("ui_text_backspace", true)) { - backspace_at_cursor(); + backspace(); _filter_code_completion_candidates(); accept_event(); return; @@ -385,14 +387,34 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { set_code_hint(""); } - /* Override input to unfold lines where needed. */ - if (!is_readonly()) { - if (k->is_action("ui_text_newline_above", true) || k->is_action("ui_text_newline_blank", true) || k->is_action("ui_text_newline", true)) { - unfold_line(cursor_get_line()); - } - if (cursor_get_line() > 0 && k->is_action("ui_text_backspace", true)) { - unfold_line(cursor_get_line() - 1); - } + /* Indentation */ + if (k->is_action("ui_text_indent", true)) { + do_indent(); + accept_event(); + return; + } + + if (k->is_action("ui_text_dedent", true)) { + do_unindent(); + accept_event(); + return; + } + + // Override new line actions, for auto indent + if (k->is_action("ui_text_newline_above", true)) { + _new_line(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_newline_blank", true)) { + _new_line(false); + accept_event(); + return; + } + if (k->is_action("ui_text_newline", true)) { + _new_line(); + accept_event(); + return; } /* Remove shift otherwise actions will not match. */ @@ -437,6 +459,443 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { return TextEdit::get_cursor_shape(p_pos); } +/* Indent management */ +void CodeEdit::set_indent_size(const int p_size) { + ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); + if (indent_size == p_size) { + return; + } + + indent_size = p_size; + if (indent_using_spaces) { + indent_text = String(" ").repeat(p_size); + } else { + indent_text = "\t"; + } + set_tab_size(p_size); +} + +int CodeEdit::get_indent_size() const { + return indent_size; +} + +void CodeEdit::set_indent_using_spaces(const bool p_use_spaces) { + indent_using_spaces = p_use_spaces; + if (indent_using_spaces) { + indent_text = String(" ").repeat(indent_size); + } else { + indent_text = "\t"; + } +} + +bool CodeEdit::is_indent_using_spaces() const { + return indent_using_spaces; +} + +void CodeEdit::set_auto_indent_enabled(bool p_enabled) { + auto_indent = p_enabled; +} + +bool CodeEdit::is_auto_indent_enabled() const { + return auto_indent; +} + +void CodeEdit::set_auto_indent_prefixes(const TypedArray<String> &p_prefixes) { + auto_indent_prefixes.clear(); + for (int i = 0; i < p_prefixes.size(); i++) { + const String prefix = p_prefixes[i]; + auto_indent_prefixes.insert(prefix[0]); + } +} + +TypedArray<String> CodeEdit::get_auto_indent_prefixes() const { + TypedArray<String> prefixes; + for (const Set<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) { + prefixes.push_back(String::chr(E->get())); + } + return prefixes; +} + +void CodeEdit::do_indent() { + if (is_readonly()) { + return; + } + + if (is_selection_active()) { + indent_lines(); + return; + } + + if (!indent_using_spaces) { + _insert_text_at_cursor("\t"); + return; + } + + int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor_get_column()); + if (spaces_to_add > 0) { + _insert_text_at_cursor(String(" ").repeat(spaces_to_add)); + } +} + +void CodeEdit::indent_lines() { + if (is_readonly()) { + return; + } + + begin_complex_operation(); + + /* This value informs us by how much we changed selection position by indenting right. */ + /* Default is 1 for tab indentation. */ + int selection_offset = 1; + + int start_line = cursor_get_line(); + int end_line = start_line; + if (is_selection_active()) { + start_line = get_selection_from_line(); + end_line = get_selection_to_line(); + + /* Ignore the last line if the selection is not past the first column. */ + if (get_selection_to_column() == 0) { + selection_offset = 0; + end_line--; + } + } + + for (int i = start_line; i <= end_line; i++) { + const String line_text = get_line(i); + if (line_text.size() == 0 && is_selection_active()) { + continue; + } + + if (!indent_using_spaces) { + set_line(i, '\t' + line_text); + continue; + } + + /* We don't really care where selection is - we just need to know indentation level at the beginning of the line. */ + /* Since we will add this many spaces, we want to move the whole selection and caret by this much. */ + int spaces_to_add = _calculate_spaces_till_next_right_indent(get_first_non_whitespace_column(i)); + set_line(i, String(" ").repeat(spaces_to_add) + line_text); + selection_offset = spaces_to_add; + } + + /* Fix selection and caret being off after shifting selection right.*/ + if (is_selection_active()) { + select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset); + } + cursor_set_column(cursor_get_column() + selection_offset, false); + + end_complex_operation(); +} + +void CodeEdit::do_unindent() { + if (is_readonly()) { + return; + } + + int cc = cursor_get_column(); + + if (is_selection_active() || cc <= 0) { + unindent_lines(); + return; + } + + int cl = cursor_get_line(); + const String &line = get_line(cl); + + if (line[cc - 1] == '\t') { + _remove_text(cl, cc - 1, cl, cc); + cursor_set_column(MAX(0, cc - 1)); + return; + } + + if (line[cc - 1] != ' ') { + return; + } + + int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); + if (spaces_to_remove > 0) { + for (int i = 1; i <= spaces_to_remove; i++) { + if (line[cc - i] != ' ') { + spaces_to_remove = i - 1; + break; + } + } + _remove_text(cl, cc - spaces_to_remove, cl, cc); + cursor_set_column(MAX(0, cc - spaces_to_remove)); + } +} + +void CodeEdit::unindent_lines() { + if (is_readonly()) { + return; + } + + begin_complex_operation(); + + /* Moving caret and selection after unindenting can get tricky because */ + /* changing content of line can move caret and selection on its own (if new line ends before previous position of either), */ + /* therefore we just remember initial values and at the end of the operation offset them by number of removed characters. */ + int removed_characters = 0; + int initial_selection_end_column = 0; + int initial_cursor_column = cursor_get_column(); + + int start_line = cursor_get_line(); + int end_line = start_line; + if (is_selection_active()) { + start_line = get_selection_from_line(); + end_line = get_selection_to_line(); + + /* Ignore the last line if the selection is not past the first column. */ + initial_selection_end_column = get_selection_to_column(); + if (initial_selection_end_column == 0) { + end_line--; + } + } + + bool first_line_edited = false; + bool last_line_edited = false; + + for (int i = start_line; i <= end_line; i++) { + String line_text = get_line(i); + + if (line_text.begins_with("\t")) { + line_text = line_text.substr(1, line_text.length()); + + set_line(i, line_text); + removed_characters = 1; + + first_line_edited = (i == start_line) ? true : first_line_edited; + last_line_edited = (i == end_line) ? true : last_line_edited; + continue; + } + + if (line_text.begins_with(" ")) { + /* When unindenting we aim to remove spaces before line that has selection no matter what is selected, */ + /* Here we remove only enough spaces to align text to nearest full multiple of indentation_size. */ + /* In case where selection begins at the start of indentation_size multiple we remove whole indentation level. */ + int spaces_to_remove = _calculate_spaces_till_next_left_indent(get_first_non_whitespace_column(i)); + line_text = line_text.substr(spaces_to_remove, line_text.length()); + + set_line(i, line_text); + removed_characters = spaces_to_remove; + + first_line_edited = (i == start_line) ? true : first_line_edited; + last_line_edited = (i == end_line) ? true : last_line_edited; + } + } + + if (is_selection_active()) { + /* Fix selection being off by one on the first line. */ + if (first_line_edited) { + select(get_selection_from_line(), get_selection_from_column() - removed_characters, get_selection_to_line(), initial_selection_end_column); + } + + /* Fix selection being off by one on the last line. */ + if (last_line_edited) { + select(get_selection_from_line(), get_selection_from_column(), get_selection_to_line(), initial_selection_end_column - removed_characters); + } + } + cursor_set_column(initial_cursor_column - removed_characters, false); + + end_complex_operation(); +} + +int CodeEdit::_calculate_spaces_till_next_left_indent(int p_column) const { + int spaces_till_indent = p_column % indent_size; + if (spaces_till_indent == 0) { + spaces_till_indent = indent_size; + } + return spaces_till_indent; +} + +int CodeEdit::_calculate_spaces_till_next_right_indent(int p_column) const { + return indent_size - p_column % indent_size; +} + +/* TODO: remove once brace completion is refactored. */ +static char32_t _get_right_pair_symbol(char32_t c) { + if (c == '"') { + return '"'; + } + if (c == '\'') { + return '\''; + } + if (c == '(') { + return ')'; + } + if (c == '[') { + return ']'; + } + if (c == '{') { + return '}'; + } + return 0; +} + +static bool _is_pair_left_symbol(char32_t c) { + return c == '"' || + c == '\'' || + c == '(' || + c == '[' || + c == '{'; +} + +void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { + if (is_readonly()) { + return; + } + + const int cc = cursor_get_column(); + const int cl = cursor_get_line(); + const String line = get_line(cl); + + String ins = "\n"; + + /* Append current indentation. */ + int space_count = 0; + int line_col = 0; + for (; line_col < cc; line_col++) { + if (line[line_col] == '\t') { + ins += indent_text; + space_count = 0; + continue; + } + + if (line[line_col] == ' ') { + space_count++; + + if (space_count == indent_size) { + ins += indent_text; + space_count = 0; + } + continue; + } + break; + } + + if (is_line_folded(cl)) { + unfold_line(cl); + } + + /* Indent once again if the previous line needs it, ie ':'. */ + /* Then add an addition new line for any closing pairs aka '()'. */ + /* Skip this in comments or if we are going above. */ + bool brace_indent = false; + if (auto_indent && !p_above && cc > 0 && is_in_comment(cl) == -1) { + bool should_indent = false; + char32_t indent_char = ' '; + + for (; line_col < cc; line_col++) { + char32_t c = line[line_col]; + if (auto_indent_prefixes.has(c)) { + should_indent = true; + indent_char = c; + continue; + } + + /* Make sure this is the last char, trailing whitespace or comments are okay. */ + if (should_indent && (!_is_whitespace(c) && is_in_comment(cl, cc) == -1)) { + should_indent = false; + } + } + + if (should_indent) { + ins += indent_text; + + /* TODO: Change when brace completion is refactored. */ + char32_t closing_char = _get_right_pair_symbol(indent_char); + if (closing_char != 0 && closing_char == line[cc]) { + /* No need to move the brace below if we are not taking the text with us. */ + if (p_split_current_line) { + brace_indent = true; + ins += "\n" + ins.substr(1, ins.length() - 2); + } else { + brace_indent = false; + ins = "\n" + ins.substr(1, ins.length() - 2); + } + } + } + } + + begin_complex_operation(); + + bool first_line = false; + if (!p_split_current_line) { + if (p_above) { + if (cl > 0) { + cursor_set_line(cl - 1, false); + cursor_set_column(get_line(cursor_get_line()).length()); + } else { + cursor_set_column(0); + first_line = true; + } + } else { + cursor_set_column(line.length()); + } + } + + insert_text_at_cursor(ins); + + if (first_line) { + cursor_set_line(0); + } else if (brace_indent) { + cursor_set_line(cursor_get_line() - 1, false); + cursor_set_column(get_line(cursor_get_line()).length()); + } + + end_complex_operation(); +} + +void CodeEdit::backspace() { + if (is_readonly()) { + return; + } + + int cc = cursor_get_column(); + int cl = cursor_get_line(); + + if (cc == 0 && cl == 0) { + return; + } + + if (is_selection_active()) { + delete_selection(); + return; + } + + if (cl > 0 && is_line_hidden(cl - 1)) { + unfold_line(cursor_get_line() - 1); + } + + int prev_line = cc ? cl : cl - 1; + int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length()); + + merge_gutters(cl, prev_line); + + /* TODO: Change when brace completion is refactored. */ + if (auto_brace_completion_enabled && cc > 0 && _is_pair_left_symbol(get_line(cl)[cc - 1])) { + _consume_backspace_for_pair_symbol(prev_line, prev_column); + cursor_set_line(prev_line, false, true); + cursor_set_column(prev_column); + return; + } + + /* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */ + /* same way as tabs. */ + if (indent_using_spaces && cc != 0) { + if (get_first_non_whitespace_column(cl) > cc) { + prev_column = cc - _calculate_spaces_till_next_left_indent(cc); + prev_line = cl; + } + } + + _remove_text(prev_line, prev_column, cl, cc); + + cursor_set_line(prev_line, false, true); + cursor_set_column(prev_column); +} + /* Main Gutter */ void CodeEdit::_update_draw_main_gutter() { set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines); @@ -615,7 +1074,7 @@ bool CodeEdit::is_line_numbers_zero_padded() const { void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); Ref<TextLine> tl; - tl.instance(); + tl.instantiate(); tl->add_string(fc, cache.font, cache.font_size); int yofs = p_region.position.y + (get_row_height() - tl->get_size().y) / 2; Color number_color = get_line_gutter_item_color(p_line, line_number_gutter); @@ -1284,6 +1743,25 @@ void CodeEdit::cancel_code_completion() { } void CodeEdit::_bind_methods() { + /* Indent management */ + ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size); + ClassDB::bind_method(D_METHOD("get_indent_size"), &CodeEdit::get_indent_size); + + ClassDB::bind_method(D_METHOD("set_indent_using_spaces", "use_spaces"), &CodeEdit::set_indent_using_spaces); + ClassDB::bind_method(D_METHOD("is_indent_using_spaces"), &CodeEdit::is_indent_using_spaces); + + ClassDB::bind_method(D_METHOD("set_auto_indent_enabled", "enable"), &CodeEdit::set_auto_indent_enabled); + ClassDB::bind_method(D_METHOD("is_auto_indent_enabled"), &CodeEdit::is_auto_indent_enabled); + + ClassDB::bind_method(D_METHOD("set_auto_indent_prefixes", "prefixes"), &CodeEdit::set_auto_indent_prefixes); + ClassDB::bind_method(D_METHOD("get_auto_indent_prefixes"), &CodeEdit::get_auto_indent_prefixes); + + ClassDB::bind_method(D_METHOD("do_indent"), &CodeEdit::do_indent); + ClassDB::bind_method(D_METHOD("do_unindent"), &CodeEdit::do_unindent); + + ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines); + ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines); + /* Main Gutter */ ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback); @@ -1434,6 +1912,12 @@ void CodeEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "code_completion_enabled"), "set_code_completion_enabled", "is_code_completion_enabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "code_completion_prefixes"), "set_code_completion_prefixes", "get_code_comletion_prefixes"); + ADD_GROUP("Indentation", "indent_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "indent_size"), "set_indent_size", "get_indent_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_use_spaces"), "set_indent_using_spaces", "is_indent_using_spaces"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes"); + /* Signals */ ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("request_code_completion")); @@ -2060,6 +2544,12 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { } CodeEdit::CodeEdit() { + /* Indent management */ + auto_indent_prefixes.insert(':'); + auto_indent_prefixes.insert('{'); + auto_indent_prefixes.insert('['); + auto_indent_prefixes.insert('('); + /* Text Direction */ set_layout_direction(LAYOUT_DIRECTION_LTR); set_text_direction(TEXT_DIRECTION_LTR); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 52b3f52a03..25b518402b 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -53,6 +53,19 @@ public: }; private: + /* Indent management */ + int indent_size = 4; + String indent_text = "\t"; + + bool auto_indent = false; + Set<char32_t> auto_indent_prefixes; + + bool indent_using_spaces = false; + int _calculate_spaces_till_next_left_indent(int p_column) const; + int _calculate_spaces_till_next_right_indent(int p_column) const; + + void _new_line(bool p_split_current_line = true, bool p_above = false); + /* Main Gutter */ enum MainGutterType { MAIN_GUTTER_BREAKPOINT = 0x01, @@ -206,6 +219,27 @@ protected: public: virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + /* Indent management */ + void set_indent_size(const int p_size); + int get_indent_size() const; + + void set_indent_using_spaces(const bool p_use_spaces); + bool is_indent_using_spaces() const; + + void set_auto_indent_enabled(bool p_enabled); + bool is_auto_indent_enabled() const; + + void set_auto_indent_prefixes(const TypedArray<String> &p_prefixes); + TypedArray<String> get_auto_indent_prefixes() const; + + void do_indent(); + void do_unindent(); + + void indent_lines(); + void unindent_lines(); + + virtual void backspace() override; + /* Main Gutter */ void set_draw_breakpoints_gutter(bool p_draw); bool is_drawing_breakpoints_gutter() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index f394b9e3a5..659d14ae70 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -40,6 +40,8 @@ #endif #include "scene/main/window.h" +List<Color> ColorPicker::preset_cache; + void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { @@ -57,11 +59,17 @@ void ColorPicker::_notification(int p_what) { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - PackedColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PackedColorArray()); + if (preset_cache.is_empty()) { + PackedColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PackedColorArray()); + for (int i = 0; i < saved_presets.size(); i++) { + preset_cache.push_back(saved_presets[i]); + } + } - for (int i = 0; i < saved_presets.size(); i++) { - add_preset(saved_presets[i]); + for (int i = 0; i < preset_cache.size(); i++) { + presets.push_back(preset_cache[i]); } + preset->update(); } #endif } break; @@ -88,7 +96,7 @@ Ref<Shader> ColorPicker::wheel_shader; Ref<Shader> ColorPicker::circle_shader; void ColorPicker::init_shaders() { - wheel_shader.instance(); + wheel_shader.instantiate(); wheel_shader->set_code( "shader_type canvas_item;" "void fragment() {" @@ -107,7 +115,7 @@ void ColorPicker::init_shaders() { " COLOR = vec4(clamp((abs(fract(((a - TAU) / TAU) + vec3(3.0, 2.0, 1.0) / 3.0) * 6.0 - 3.0) - 1.0), 0.0, 1.0), (b + b2 + b3 + b4) / 4.00);" "}"); - circle_shader.instance(); + circle_shader.instantiate(); circle_shader->set_code( "shader_type canvas_item;" "uniform float v = 1.0;" @@ -413,6 +421,7 @@ void ColorPicker::add_preset(const Color &p_color) { presets.move_to_back(presets.find(p_color)); } else { presets.push_back(p_color); + preset_cache.push_back(p_color); } preset->update(); @@ -427,6 +436,7 @@ void ColorPicker::add_preset(const Color &p_color) { void ColorPicker::erase_preset(const Color &p_color) { if (presets.find(p_color)) { presets.erase(presets.find(p_color)); + preset_cache.erase(preset_cache.find(p_color)); preset->update(); #ifdef TOOLS_ENABLED @@ -1213,9 +1223,9 @@ ColorPicker::ColorPicker() : wheel_edit->set_custom_minimum_size(Size2(get_theme_constant("sv_width"), get_theme_constant("sv_height"))); hb_edit->add_child(wheel_edit); - wheel_mat.instance(); + wheel_mat.instantiate(); wheel_mat->set_shader(wheel_shader); - circle_mat.instance(); + circle_mat.instantiate(); circle_mat->set_shader(circle_shader); MarginContainer *wheel_margin(memnew(MarginContainer)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 3bd2ff9375..60da3957aa 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -58,6 +58,7 @@ public: private: static Ref<Shader> wheel_shader; static Ref<Shader> circle_shader; + static List<Color> preset_cache; Control *screen = nullptr; Control *uv_edit = memnew(Control); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 64a3f1ca77..d42b505f7b 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -650,27 +650,11 @@ void Control::_notification(int p_notification) { } } -bool Control::clips_input() const { - if (get_script_instance()) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_clips_input); - } - return false; -} - bool Control::has_point(const Point2 &p_point) const { - if (get_script_instance()) { - Variant v = p_point; - const Variant *p = &v; - Callable::CallError ce; - Variant ret = get_script_instance()->call(SceneStringNames::get_singleton()->_has_point, &p, 1, ce); - if (ce.error == Callable::CallError::CALL_OK) { - return ret; - } + bool ret; + if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { + return ret; } - /*if (has_stylebox("mask")) { - Ref<StyleBox> mask = get_stylebox("mask"); - return mask->test_mask(p_point,Rect2(Point2(),get_size())); - }*/ return Rect2(Point2(), get_size()).has_point(p_point); } @@ -1084,7 +1068,7 @@ Rect2 Control::get_parent_anchorable_rect() const { } else { #ifdef TOOLS_ENABLED Node *edited_root = get_tree()->get_edited_scene_root(); - if (edited_root && (this == edited_root || edited_root->is_a_parent_of(this))) { + if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) { parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); } else { parent_rect = get_viewport()->get_visible_rect(); @@ -1504,7 +1488,7 @@ Point2 Control::get_global_position() const { Point2 Control::get_screen_position() const { ERR_FAIL_COND_V(!is_inside_tree(), Point2()); - Point2 global_pos = get_global_position(); + Point2 global_pos = get_viewport()->get_canvas_transform().xform(get_global_position()); Window *w = Object::cast_to<Window>(get_viewport()); if (w && !w->is_embedding_subwindows()) { global_pos += w->get_position(); @@ -2473,14 +2457,6 @@ real_t Control::get_rotation() const { return data.rotation; } -void Control::set_rotation_degrees(real_t p_degrees) { - set_rotation(Math::deg2rad(p_degrees)); -} - -real_t Control::get_rotation_degrees() const { - return Math::rad2deg(get_rotation()); -} - void Control::_override_changed() { notification(NOTIFICATION_THEME_CHANGED); emit_signal(SceneStringNames::get_singleton()->theme_changed); @@ -2648,7 +2624,6 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_offsets"), &Control::set_global_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset); @@ -2657,7 +2632,6 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); ClassDB::bind_method(D_METHOD("get_size"), &Control::get_size); ClassDB::bind_method(D_METHOD("get_rotation"), &Control::get_rotation); - ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Control::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_scale"), &Control::get_scale); ClassDB::bind_method(D_METHOD("get_pivot_offset"), &Control::get_pivot_offset); ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size); @@ -2784,7 +2758,6 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo( PropertyInfo(Variant::OBJECT, "control", PROPERTY_HINT_RESOURCE_TYPE, "Control"), "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); @@ -2810,8 +2783,7 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); @@ -2940,5 +2912,5 @@ void Control::_bind_methods() { ADD_SIGNAL(MethodInfo("minimum_size_changed")); ADD_SIGNAL(MethodInfo("theme_changed")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_point", PropertyInfo(Variant::VECTOR2, "point"))); + GDVIRTUAL_BIND(_has_point); } diff --git a/scene/gui/control.h b/scene/gui/control.h index a05025c32d..0642686a9f 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -32,6 +32,7 @@ #define CONTROL_H #include "core/math/transform_2d.h" +#include "core/object/gdvirtual.gen.inc" #include "core/templates/rid.h" #include "scene/gui/shortcut.h" #include "scene/main/canvas_item.h" @@ -264,6 +265,7 @@ private: static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; + GDVIRTUAL1RC(bool, _has_point, Vector2) protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; @@ -329,7 +331,6 @@ public: virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; virtual bool has_point(const Point2 &p_point) const; - virtual bool clips_input() const; virtual void set_drag_forwarding(Control *p_target); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; @@ -384,9 +385,7 @@ public: void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. void set_rotation(real_t p_radians); - void set_rotation_degrees(real_t p_degrees); real_t get_rotation() const; - real_t get_rotation_degrees() const; void set_h_grow_direction(GrowDirection p_direction); GrowDirection get_h_grow_direction() const; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index f63ae7569f..dceab00607 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -263,6 +263,28 @@ Button *AcceptDialog::add_cancel_button(const String &p_cancel) { return b; } +void AcceptDialog::remove_button(Control *p_button) { + Button *button = Object::cast_to<Button>(p_button); + ERR_FAIL_NULL(button); + ERR_FAIL_COND_MSG(button->get_parent() != hbc, vformat("Cannot remove button %s as it does not belong to this dialog.", button->get_name())); + ERR_FAIL_COND_MSG(button == ok, "Cannot remove dialog's OK button."); + + Node *right_spacer = hbc->get_child(button->get_index() + 1); + // Should always be valid but let's avoid crashing + if (right_spacer) { + hbc->remove_child(right_spacer); + memdelete(right_spacer); + } + hbc->remove_child(button); + + if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_custom_action))) { + button->disconnect("pressed", callable_mp(this, &AcceptDialog::_custom_action)); + } + if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed))) { + button->disconnect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); + } +} + void AcceptDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ok_button"), &AcceptDialog::get_ok_button); ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label); @@ -270,6 +292,7 @@ void AcceptDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok); ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL("")); ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button); + ClassDB::bind_method(D_METHOD("remove_button", "button"), &AcceptDialog::remove_button); ClassDB::bind_method(D_METHOD("register_text_enter", "line_edit"), &AcceptDialog::register_text_enter); ClassDB::bind_method(D_METHOD("set_text", "text"), &AcceptDialog::set_text); ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text); diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index d389806fff..8e803a2a7c 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -82,6 +82,7 @@ public: Button *get_ok_button() { return ok; } Button *add_button(const String &p_text, bool p_right = false, const String &p_action = ""); Button *add_cancel_button(const String &p_cancel = ""); + void remove_button(Control *p_button); void set_hide_on_ok(bool p_hide); bool get_hide_on_ok() const; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 5ef89e38f0..39aa6749e7 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -239,10 +239,6 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const } } -bool GraphEdit::clips_input() const { - return true; -} - void GraphEdit::get_connection_list(List<Connection> *r_connections) const { *r_connections = connections; } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index e8300f901c..5251de1722 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -235,7 +235,6 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; void _notification(int p_what); - virtual bool clips_input() const override; public: Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 77c502cf8d..836bffdf46 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -757,7 +757,7 @@ void GraphNode::_connpos_update() { continue; } - Size2i size = c->get_combined_minimum_size(); + Size2i size = c->get_rect().size; int y = sb->get_margin(SIDE_TOP) + vofs; int h = size.y; @@ -1021,6 +1021,6 @@ void GraphNode::_bind_methods() { } GraphNode::GraphNode() { - title_buf.instance(); + title_buf.instantiate(); set_mouse_filter(MOUSE_FILTER_STOP); } diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 150980b2e9..b0d54bf8c9 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -57,7 +57,7 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); item.text = p_item; - item.text_buf.instance(); + item.text_buf.instantiate(); item.selectable = p_selectable; item.selected = false; item.disabled = false; @@ -80,7 +80,7 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) { item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); //item.text=p_item; - item.text_buf.instance(); + item.text_buf.instantiate(); item.selectable = p_selectable; item.selected = false; item.disabled = false; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 0ce0130ad5..6580d794d1 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -453,6 +453,7 @@ void Label::set_text(const String &p_string) { visible_chars = get_total_character_count() * percent_visible; } update(); + minimum_size_changed(); } void Label::set_text_direction(Control::TextDirection p_text_direction) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 089893e63b..f2d0d9bb22 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1470,19 +1470,23 @@ int LineEdit::get_scroll_offset() const { } void LineEdit::insert_text_at_caret(String p_text) { - if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { - String pre = text.substr(0, caret_column); - String post = text.substr(caret_column, text.length() - caret_column); - text = pre + p_text + post; - _shape(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length()); - if (dir != TextServer::DIRECTION_AUTO) { - input_direction = (TextDirection)dir; + if (max_length > 0) { + // Truncate text to append to fit in max_length, if needed. + int available_chars = max_length - text.length(); + if (p_text.length() > available_chars) { + emit_signal("text_change_rejected", p_text.substr(available_chars)); + p_text = p_text.substr(0, available_chars); } - set_caret_column(caret_column + p_text.length()); - } else { - emit_signal("text_change_rejected"); } + String pre = text.substr(0, caret_column); + String post = text.substr(caret_column, text.length() - caret_column); + text = pre + p_text + post; + _shape(); + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length()); + if (dir != TextServer::DIRECTION_AUTO) { + input_direction = (TextDirection)dir; + } + set_caret_column(caret_column + p_text.length()); } void LineEdit::clear_internal() { @@ -2158,7 +2162,7 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); - ADD_SIGNAL(MethodInfo("text_change_rejected")); + ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring"))); ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text"))); BIND_ENUM_CONSTANT(ALIGN_LEFT); @@ -2198,7 +2202,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); @@ -2221,7 +2225,7 @@ void LineEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); } diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index d45ffde715..ee0618a991 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -301,7 +301,7 @@ void LinkButton::_bind_methods() { } LinkButton::LinkButton() { - text_buf.instance(); + text_buf.instantiate(); set_focus_mode(FOCUS_NONE); set_default_cursor_shape(CURSOR_POINTING_HAND); } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index e4cbe984c9..74718395d3 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -80,8 +80,8 @@ class PopupMenu : public Popup { } Item() { - text_buf.instance(); - accel_text_buf.instance(); + text_buf.instantiate(); + accel_text_buf.instantiate(); checkable_type = CHECKABLE_TYPE_NONE; } }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 987d05bf71..f32ad2144a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -934,6 +934,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } + Vector2 fbg_line_off = off + p_ofs; + // Draw background color box + Vector2i chr_range = TS->shaped_text_get_range(rid); + _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0); + // Draw main text. Color selection_fg = get_theme_color("font_selected_color"); Color selection_bg = get_theme_color("selection_color"); @@ -1079,6 +1084,9 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o off.x += glyphs[i].advance; } } + // Draw foreground color box + _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1); + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom(); } @@ -2036,6 +2044,36 @@ bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) return false; } +Color RichTextLabel::_find_bgcolor(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_BGCOLOR) { + ItemBGColor *color = static_cast<ItemBGColor *>(item); + return color->color; + } + + item = item->parent; + } + + return Color(0, 0, 0, 0); +} + +Color RichTextLabel::_find_fgcolor(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_FGCOLOR) { + ItemFGColor *color = static_cast<ItemFGColor *>(item); + return color->color; + } + + item = item->parent; + } + + return Color(0, 0, 0, 0); +} + bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { if (from && from != to) { if (from->type != ITEM_FONT && from->type != ITEM_COLOR && from->type != ITEM_UNDERLINE && from->type != ITEM_STRIKETHROUGH) { @@ -2546,6 +2584,22 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq _add_item(item, true); } +void RichTextLabel::push_bgcolor(const Color &p_color) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemBGColor *item = memnew(ItemBGColor); + + item->color = p_color; + _add_item(item, true); +} + +void RichTextLabel::push_fgcolor(const Color &p_color) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemFGColor *item = memnew(ItemFGColor); + + item->color = p_color; + _add_item(item, true); +} + void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) { ItemCustomFX *item = memnew(ItemCustomFX); item->custom_effect = p_custom_effect; @@ -3352,6 +3406,23 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("rainbow"); set_process_internal(true); + + } else if (tag.begins_with("bgcolor=")) { + String color_str = tag.substr(8, tag.length()); + Color color = Color::from_string(color_str, base_color); + + push_bgcolor(color); + pos = brk_end + 1; + tag_stack.push_front("bgcolor"); + + } else if (tag.begins_with("fgcolor=")) { + String color_str = tag.substr(8, tag.length()); + Color color = Color::from_string(color_str, base_color); + + push_fgcolor(color); + pos = brk_end + 1; + tag_stack.push_front("fgcolor"); + } else { Vector<String> &expr = split_tag_block; if (expr.size() < 1) { @@ -3918,6 +3989,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cell_size_override", "min_size", "max_size"), &RichTextLabel::set_cell_size_override); ClassDB::bind_method(D_METHOD("set_cell_padding", "padding"), &RichTextLabel::set_cell_padding); ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); + ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor); + ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor); ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop); ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear); @@ -4012,7 +4085,7 @@ 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::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); + 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"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); @@ -4056,6 +4129,8 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_WAVE); BIND_ENUM_CONSTANT(ITEM_TORNADO); BIND_ENUM_CONSTANT(ITEM_RAINBOW); + BIND_ENUM_CONSTANT(ITEM_BGCOLOR); + BIND_ENUM_CONSTANT(ITEM_FGCOLOR); BIND_ENUM_CONSTANT(ITEM_META); BIND_ENUM_CONSTANT(ITEM_DROPCAP); BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); @@ -4108,6 +4183,65 @@ Size2 RichTextLabel::get_minimum_size() const { return size; } +void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) { + Vector2i fbg_index = Vector2i(end, start); + Color last_color = Color(0, 0, 0, 0); + bool draw_box = false; + // Draw a box based on color tags associated with glyphs + for (int i = start; i < end; i++) { + Item *it = _get_item_at_pos(it_from, it_to, i); + Color color = Color(0, 0, 0, 0); + + if (fbg_flag == 0) { + color = _find_bgcolor(it); + } else { + color = _find_fgcolor(it); + } + + bool change_to_color = ((color.a > 0) && ((last_color.a - 0.0) < 0.01)); + bool change_from_color = (((color.a - 0.0) < 0.01) && (last_color.a > 0.0)); + bool change_color = (((color.a > 0) == (last_color.a > 0)) && (color != last_color)); + + if (change_to_color) { + fbg_index.x = MIN(i, fbg_index.x); + fbg_index.y = MAX(i, fbg_index.y); + } + + if (change_from_color || change_color) { + fbg_index.x = MIN(i, fbg_index.x); + fbg_index.y = MAX(i, fbg_index.y); + draw_box = true; + } + + if (draw_box) { + Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y); + for (int j = 0; j < sel.size(); j++) { + Vector2 rect_off = line_off + Vector2(sel[j].x, -TS->shaped_text_get_ascent(p_rid)); + Vector2 rect_size = Vector2(sel[j].y - sel[j].x, TS->shaped_text_get_size(p_rid).y); + RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color); + } + fbg_index = Vector2i(end, start); + draw_box = false; + } + + if (change_color) { + fbg_index.x = MIN(i, fbg_index.x); + fbg_index.y = MAX(i, fbg_index.y); + } + + last_color = color; + } + + if (last_color.a > 0) { + Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end); + for (int i = 0; i < sel.size(); i++) { + Vector2 rect_off = line_off + Vector2(sel[i].x, -TS->shaped_text_get_ascent(p_rid)); + Vector2 rect_size = Vector2(sel[i].y - sel[i].x, TS->shaped_text_get_size(p_rid).y); + RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color); + } + } +} + Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) { for (int i = 0; i < custom_effects.size(); i++) { if (!custom_effects[i].is_valid()) { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 9cf94b0cd7..999d8b05fd 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -75,6 +75,8 @@ public: ITEM_WAVE, ITEM_TORNADO, ITEM_RAINBOW, + ITEM_BGCOLOR, + ITEM_FGCOLOR, ITEM_META, ITEM_DROPCAP, ITEM_CUSTOMFX @@ -100,7 +102,7 @@ private: int char_offset = 0; int char_count = 0; - Line() { text_buf.instance(); } + Line() { text_buf.instantiate(); } }; struct Item { @@ -307,13 +309,23 @@ private: ItemRainbow() { type = ITEM_RAINBOW; } }; + struct ItemBGColor : public Item { + Color color; + ItemBGColor() { type = ITEM_BGCOLOR; } + }; + + struct ItemFGColor : public Item { + Color color; + ItemFGColor() { type = ITEM_FGCOLOR; } + }; + struct ItemCustomFX : public ItemFX { Ref<CharFXTransform> char_fx_transform; Ref<RichTextEffect> custom_effect; ItemCustomFX() { type = ITEM_CUSTOMFX; - char_fx_transform.instance(); + char_fx_transform.instantiate(); } virtual ~ItemCustomFX() { @@ -422,6 +434,8 @@ private: bool _find_underline(Item *p_item); bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); + Color _find_bgcolor(Item *p_item); + Color _find_fgcolor(Item *p_item); bool _find_layout_subitem(Item *from, Item *to); void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); @@ -437,6 +451,8 @@ private: Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier); virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions); + void _draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag); + bool use_bbcode = false; String bbcode; @@ -474,6 +490,8 @@ public: void push_wave(float p_frequency, float p_amplitude); void push_tornado(float p_frequency, float p_radius); void push_rainbow(float p_saturation, float p_value, float p_frequency); + void push_bgcolor(const Color &p_color); + void push_fgcolor(const Color &p_color); void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment); void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1); void set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 5f872644ab..177f146b6a 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -32,10 +32,6 @@ #include "core/os/os.h" #include "scene/main/window.h" -bool ScrollContainer::clips_input() const { - return true; -} - Size2 ScrollContainer::get_minimum_size() const { Ref<StyleBox> sb = get_theme_stylebox("bg"); Size2 min_size; @@ -239,13 +235,13 @@ void ScrollContainer::_update_scrollbar_position() { } void ScrollContainer::_gui_focus_changed(Control *p_control) { - if (follow_focus && is_a_parent_of(p_control)) { + if (follow_focus && is_ancestor_of(p_control)) { ensure_control_visible(p_control); } } void ScrollContainer::ensure_control_visible(Control *p_control) { - ERR_FAIL_COND_MSG(!is_a_parent_of(p_control), "Must be a parent of the control."); + ERR_FAIL_COND_MSG(!is_ancestor_of(p_control), "Must be an ancestor of the control."); Rect2 global_rect = get_global_rect(); Rect2 other_rect = p_control->get_global_rect(); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index c77a0d62f5..4733fdabca 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -108,8 +108,6 @@ public: VScrollBar *get_v_scrollbar(); void ensure_control_visible(Control *p_control); - virtual bool clips_input() const override; - TypedArray<String> get_configuration_warnings() const override; ScrollContainer(); diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp index cbbcf9e069..962c6dcc60 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -42,7 +42,7 @@ Ref<InputEvent> Shortcut::get_shortcut() const { } bool Shortcut::is_shortcut(const Ref<InputEvent> &p_event) const { - return shortcut.is_valid() && shortcut->shortcut_match(p_event); + return shortcut.is_valid() && shortcut->is_match(p_event, true); } String Shortcut::get_as_text() const { diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 4ac73ae6b5..941dd30057 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -52,7 +52,7 @@ void SpinBox::_value_changed(double) { void SpinBox::_text_submitted(const String &p_string) { Ref<Expression> expr; - expr.instance(); + expr.instantiate(); String num = TS->parse_number(p_string); // Ignore the prefix and suffix in the expression @@ -140,6 +140,8 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { accept_event(); } } break; + default: + break; } } diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index df4cf9a740..9796b32c6b 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -38,7 +38,7 @@ Control *SplitContainer::_getch(int p_idx) const { for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { + if (!c || !c->is_visible()) { continue; } if (c->is_set_as_top_level()) { diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index acf0641005..133966013b 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -586,7 +586,7 @@ void TabContainer::_refresh_texts() { Control *control = Object::cast_to<Control>(tabs[i]); String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); Ref<TextLine> name; - name.instance(); + name.instantiate(); name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); text_buf.push_back(name); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 11096e7976..6f1cff9ec8 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -743,7 +743,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { Tab t; t.text = p_str; t.xl_text = tr(p_str); - t.text_buf.instance(); + t.text_buf.instantiate(); t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); t.text_buf->add_string(t.xl_text, get_theme_font("font"), get_theme_font_size("font_size"), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); t.icon = p_icon; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 6f78d586f1..6f96b530a6 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -102,14 +102,6 @@ static char32_t _get_right_pair_symbol(char32_t c) { return 0; } -static int _find_first_non_whitespace_column_of_line(const String &line) { - int left = 0; - while (left < line.length() && _is_whitespace(line[left])) { - left++; - } - return left; -} - /////////////////////////////////////////////////////////////////////////////// void TextEdit::Text::set_font(const Ref<Font> &p_font) { @@ -120,8 +112,12 @@ void TextEdit::Text::set_font_size(int p_font_size) { font_size = p_font_size; } -void TextEdit::Text::set_indent_size(int p_indent_size) { - indent_size = p_indent_size; +void TextEdit::Text::set_tab_size(int p_tab_size) { + tab_size = p_tab_size; +} + +int TextEdit::Text::get_tab_size() const { + return tab_size; } void TextEdit::Text::set_font_features(const Dictionary &p_features) { @@ -204,9 +200,9 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ } // Apply tab align. - if (indent_size > 0) { + if (tab_size > 0) { Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size); + tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); text.write[p_line].data_buf->tab_align(tabs); } } @@ -214,9 +210,9 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ void TextEdit::Text::invalidate_all_lines() { for (int i = 0; i < text.size(); i++) { text.write[i].data_buf->set_width(width); - if (indent_size > 0) { + if (tab_size > 0) { Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size); + tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); text.write[i].data_buf->tab_align(tabs); } } @@ -822,7 +818,7 @@ void TextEdit::_notification(int p_what) { if (draw_minimap) { int minimap_visible_lines = _get_minimap_visible_rows(); int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); - int minimap_tab_size = minimap_char_size.x * indent_size; + int minimap_tab_size = minimap_char_size.x * text.get_tab_size(); // calculate viewport size and y offset int viewport_height = (draw_amount - 1) * minimap_line_height; @@ -1114,7 +1110,7 @@ void TextEdit::_notification(int p_what) { } Ref<TextLine> tl; - tl.instance(); + tl.instantiate(); tl->add_string(text, cache.font, cache.font_size); int yofs = ofs_y + (row_height - tl->get_size().y) / 2; @@ -1695,7 +1691,13 @@ void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column } } -void TextEdit::backspace_at_cursor() { +void TextEdit::backspace() { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_backspace")) { + si->call("_backspace"); + return; + } + if (readonly) { return; } @@ -1704,34 +1706,15 @@ void TextEdit::backspace_at_cursor() { return; } + if (is_selection_active()) { + delete_selection(); + return; + } + int prev_line = cursor.column ? cursor.line : cursor.line - 1; int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length()); - if (cursor.line != prev_line) { - for (int i = 0; i < gutters.size(); i++) { - if (!gutters[i].overwritable) { - continue; - } - - if (text.get_line_gutter_text(cursor.line, i) != "") { - text.set_line_gutter_text(prev_line, i, text.get_line_gutter_text(cursor.line, i)); - text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i)); - } - - if (text.get_line_gutter_icon(cursor.line, i).is_valid()) { - text.set_line_gutter_icon(prev_line, i, text.get_line_gutter_icon(cursor.line, i)); - text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i)); - } - - if (text.get_line_gutter_metadata(cursor.line, i) != "") { - text.set_line_gutter_metadata(prev_line, i, text.get_line_gutter_metadata(cursor.line, i)); - } - - if (text.is_line_gutter_clickable(cursor.line, i)) { - text.set_line_gutter_clickable(prev_line, i, true); - } - } - } + merge_gutters(cursor.line, prev_line); if (is_line_hidden(cursor.line)) { set_line_as_hidden(prev_line, true); @@ -1742,168 +1725,13 @@ void TextEdit::backspace_at_cursor() { _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { _consume_backspace_for_pair_symbol(prev_line, prev_column); } else { - // Handle space indentation. - if (cursor.column != 0 && indent_using_spaces) { - // Check if there are no other chars before cursor, just indentation. - bool unindent = true; - int i = 0; - while (i < cursor.column && i < text[cursor.line].length()) { - if (!_is_whitespace(text[cursor.line][i])) { - unindent = false; - break; - } - i++; - } - - // Then we can remove all spaces as a single character. - if (unindent) { - // We want to remove spaces up to closest indent, or whole indent if cursor is pointing at it. - int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column); - prev_column = cursor.column - spaces_to_delete; - _remove_text(cursor.line, prev_column, cursor.line, cursor.column); - } else { - _remove_text(prev_line, prev_column, cursor.line, cursor.column); - } - } else { - _remove_text(prev_line, prev_column, cursor.line, cursor.column); - } + _remove_text(prev_line, prev_column, cursor.line, cursor.column); } cursor_set_line(prev_line, false, true); cursor_set_column(prev_column); } -void TextEdit::indent_selected_lines_right() { - int start_line; - int end_line; - - // This value informs us by how much we changed selection position by indenting right. - // Default is 1 for tab indentation. - int selection_offset = 1; - begin_complex_operation(); - - if (is_selection_active()) { - start_line = get_selection_from_line(); - end_line = get_selection_to_line(); - } else { - start_line = cursor.line; - end_line = start_line; - } - - // Ignore if the cursor is not past the first column. - if (is_selection_active() && get_selection_to_column() == 0) { - selection_offset = 0; - end_line--; - } - - for (int i = start_line; i <= end_line; i++) { - String line_text = get_line(i); - if (line_text.size() == 0 && is_selection_active()) { - continue; - } - if (indent_using_spaces) { - // We don't really care where selection is - we just need to know indentation level at the beginning of the line. - int left = _find_first_non_whitespace_column_of_line(line_text); - int spaces_to_add = _calculate_spaces_till_next_right_indent(left); - // Since we will add these many spaces, we want to move the whole selection and cursor by this much. - selection_offset = spaces_to_add; - for (int j = 0; j < spaces_to_add; j++) { - line_text = ' ' + line_text; - } - } else { - line_text = '\t' + line_text; - } - set_line(i, line_text); - } - - // Fix selection and cursor being off after shifting selection right. - if (is_selection_active()) { - select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset); - } - cursor_set_column(cursor.column + selection_offset, false); - end_complex_operation(); - update(); -} - -void TextEdit::indent_selected_lines_left() { - int start_line; - int end_line; - - // Moving cursor and selection after unindenting can get tricky because - // changing content of line can move cursor and selection on its own (if new line ends before previous position of either), - // therefore we just remember initial values and at the end of the operation offset them by number of removed characters. - int removed_characters = 0; - int initial_selection_end_column = selection.to_column; - int initial_cursor_column = cursor.column; - - begin_complex_operation(); - - if (is_selection_active()) { - start_line = get_selection_from_line(); - end_line = get_selection_to_line(); - } else { - start_line = cursor.line; - end_line = start_line; - } - - // Ignore if the cursor is not past the first column. - if (is_selection_active() && get_selection_to_column() == 0) { - end_line--; - } - String first_line_text = get_line(start_line); - String last_line_text = get_line(end_line); - - for (int i = start_line; i <= end_line; i++) { - String line_text = get_line(i); - - if (line_text.begins_with("\t")) { - line_text = line_text.substr(1, line_text.length()); - set_line(i, line_text); - removed_characters = 1; - } else if (line_text.begins_with(" ")) { - // When unindenting we aim to remove spaces before line that has selection no matter what is selected, - // so we start of by finding first non whitespace character of line - int left = _find_first_non_whitespace_column_of_line(line_text); - - // Here we remove only enough spaces to align text to nearest full multiple of indentation_size. - // In case where selection begins at the start of indentation_size multiple we remove whole indentation level. - int spaces_to_remove = _calculate_spaces_till_next_left_indent(left); - - line_text = line_text.substr(spaces_to_remove, line_text.length()); - set_line(i, line_text); - removed_characters = spaces_to_remove; - } - } - - if (is_selection_active()) { - // Fix selection being off by one on the first line. - if (first_line_text != get_line(start_line)) { - select(selection.from_line, selection.from_column - removed_characters, - selection.to_line, initial_selection_end_column); - } - // Fix selection being off by one on the last line. - if (last_line_text != get_line(end_line)) { - select(selection.from_line, selection.from_column, - selection.to_line, initial_selection_end_column - removed_characters); - } - } - cursor_set_column(initial_cursor_column - removed_characters, false); - end_complex_operation(); - update(); -} - -int TextEdit::_calculate_spaces_till_next_left_indent(int column) { - int spaces_till_indent = column % indent_size; - if (spaces_till_indent == 0) { - spaces_till_indent = indent_size; - } - return spaces_till_indent; -} - -int TextEdit::_calculate_spaces_till_next_right_indent(int column) { - return indent_size - column % indent_size; -} - void TextEdit::_swap_current_input_direction() { if (input_direction == TEXT_DIRECTION_LTR) { input_direction = TEXT_DIRECTION_RTL; @@ -1919,90 +1747,8 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { return; } - String ins = "\n"; - - // Keep indentation. - int space_count = 0; - for (int i = 0; i < cursor.column; i++) { - if (text[cursor.line][i] == '\t') { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } - space_count = 0; - } else if (text[cursor.line][i] == ' ') { - space_count++; - - if (space_count == indent_size) { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } - space_count = 0; - } - } else { - break; - } - } - - bool brace_indent = false; - - // No need to indent if we are going upwards. - if (auto_indent && !p_above) { - // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment - // (i.e. colon/brace precedes current cursor position). - if (cursor.column > 0) { - bool indent_char_found = false; - bool should_indent = false; - char indent_char = ':'; - char c = text[cursor.line][cursor.column]; - - for (int i = 0; i < cursor.column; i++) { - c = text[cursor.line][i]; - switch (c) { - case ':': - case '{': - case '[': - case '(': - indent_char_found = true; - should_indent = true; - indent_char = c; - continue; - } - - if (indent_char_found && is_line_comment(cursor.line)) { - should_indent = true; - break; - } else if (indent_char_found && !_is_whitespace(c)) { - should_indent = false; - indent_char_found = false; - } - } - - if (!is_line_comment(cursor.line) && should_indent) { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } - - // No need to move the brace below if we are not taking the text with us. - char32_t closing_char = _get_right_pair_symbol(indent_char); - if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column])) { - if (p_split_current_line) { - brace_indent = true; - ins += "\n" + ins.substr(1, ins.length() - 2); - } else { - brace_indent = false; - ins = "\n" + ins.substr(1, ins.length() - 2); - } - } - } - } - } begin_complex_operation(); + bool first_line = false; if (!p_split_current_line) { if (p_above) { @@ -2018,83 +1764,13 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { } } - insert_text_at_cursor(ins); + insert_text_at_cursor("\n"); if (first_line) { cursor_set_line(0); - } else if (brace_indent) { - cursor_set_line(cursor.line - 1, false); - cursor_set_column(text[cursor.line].length()); - } - end_complex_operation(); -} - -void TextEdit::_indent_right() { - if (readonly) { - return; } - if (is_selection_active()) { - indent_selected_lines_right(); - } else { - // Simple indent. - if (indent_using_spaces) { - // Insert only as much spaces as needed till next indentation level. - int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column); - String indent_to_insert = String(); - for (int i = 0; i < spaces_to_add; i++) { - indent_to_insert = ' ' + indent_to_insert; - } - _insert_text_at_cursor(indent_to_insert); - } else { - _insert_text_at_cursor("\t"); - } - } -} - -void TextEdit::_indent_left() { - if (readonly) { - return; - } - - if (is_selection_active()) { - indent_selected_lines_left(); - } else { - // Simple unindent. - int cc = cursor.column; - const String &line = text[cursor.line]; - - int left = _find_first_non_whitespace_column_of_line(line); - cc = MIN(cc, left); - - while (cc < indent_size && cc < left && line[cc] == ' ') { - cc++; - } - - if (cc > 0 && cc <= text[cursor.line].length()) { - if (text[cursor.line][cc - 1] == '\t') { - // Tabs unindentation. - _remove_text(cursor.line, cc - 1, cursor.line, cc); - if (cursor.column >= left) { - cursor_set_column(MAX(0, cursor.column - 1)); - } - update(); - } else { - // Spaces unindentation. - int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); - if (spaces_to_remove > 0) { - _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc); - if (cursor.column > left - spaces_to_remove) { // Inside text? - cursor_set_column(MAX(0, cursor.column - spaces_to_remove)); - } - update(); - } - } - } else if (cc == 0 && line.length() > 0 && line[0] == '\t') { - _remove_text(cursor.line, 0, cursor.line, 1); - update(); - } - } + end_complex_operation(); } void TextEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { @@ -2331,20 +2007,24 @@ void TextEdit::_move_cursor_page_down(bool p_select) { } } -void TextEdit::_backspace(bool p_word, bool p_all_to_left) { +void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { if (readonly) { return; } - if (is_selection_active()) { - _delete_selection(); + if (is_selection_active() || (!p_all_to_left && !p_word)) { + backspace(); return; } + if (p_all_to_left) { int cursor_current_column = cursor.column; cursor.column = 0; _remove_text(cursor.line, 0, cursor.line, cursor_current_column); - } else if (p_word) { + return; + } + + if (p_word) { int line = cursor.line; int column = cursor.column; @@ -2360,8 +2040,7 @@ void TextEdit::_backspace(bool p_word, bool p_all_to_left) { cursor_set_line(line, false); cursor_set_column(column); - } else { - backspace_at_cursor(); + return; } } @@ -2371,7 +2050,7 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { } if (is_selection_active()) { - _delete_selection(); + delete_selection(); return; } int curline_len = text[cursor.line].length(); @@ -2416,15 +2095,16 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { update(); } -void TextEdit::_delete_selection() { - if (is_selection_active()) { - selection.active = false; - update(); - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, false, false); - cursor_set_column(selection.from_column); - update(); +void TextEdit::delete_selection() { + if (!is_selection_active()) { + return; } + + selection.active = false; + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + cursor_set_line(selection.from_line, false, false); + cursor_set_column(selection.from_column); + update(); } void TextEdit::_move_cursor_document_start(bool p_select) { @@ -2459,7 +2139,7 @@ void TextEdit::_move_cursor_document_end(bool p_select) { void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection) { if (p_had_selection) { - _delete_selection(); + delete_selection(); } // Remove the old character if in insert mode and no selection. @@ -2942,31 +2622,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - // INDENTATION. - if (k->is_action("ui_text_dedent", true)) { - _indent_left(); - accept_event(); - return; - } - if (k->is_action("ui_text_indent", true)) { - _indent_right(); - accept_event(); - return; - } - // BACKSPACE AND DELETE. if (k->is_action("ui_text_backspace_all_to_left", true)) { - _backspace(false, true); + _do_backspace(false, true); accept_event(); return; } if (k->is_action("ui_text_backspace_word", true)) { - _backspace(true); + _do_backspace(true); accept_event(); return; } if (k->is_action("ui_text_backspace", true)) { - _backspace(); + _do_backspace(); accept_event(); return; } @@ -4340,14 +4008,6 @@ bool TextEdit::is_wrap_enabled() const { return wrap_enabled; } -void TextEdit::set_max_chars(int p_max_chars) { - max_chars = p_max_chars; -} - -int TextEdit::get_max_chars() const { - return max_chars; -} - void TextEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { draw_caret = true; @@ -4540,6 +4200,39 @@ bool TextEdit::is_gutter_overwritable(int p_gutter) const { return gutters[p_gutter].overwritable; } +void TextEdit::merge_gutters(int p_from_line, int p_to_line) { + ERR_FAIL_INDEX(p_from_line, text.size()); + ERR_FAIL_INDEX(p_to_line, text.size()); + if (p_from_line == p_to_line) { + return; + } + + for (int i = 0; i < gutters.size(); i++) { + if (!gutters[i].overwritable) { + continue; + } + + if (text.get_line_gutter_text(p_from_line, i) != "") { + text.set_line_gutter_text(p_to_line, i, text.get_line_gutter_text(p_from_line, i)); + text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); + } + + if (text.get_line_gutter_icon(p_from_line, i).is_valid()) { + text.set_line_gutter_icon(p_to_line, i, text.get_line_gutter_icon(p_from_line, i)); + text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); + } + + if (text.get_line_gutter_metadata(p_from_line, i) != "") { + text.set_line_gutter_metadata(p_to_line, i, text.get_line_gutter_metadata(p_from_line, i)); + } + + if (text.is_line_gutter_clickable(p_from_line, i)) { + text.set_line_gutter_clickable(p_to_line, i, true); + } + } + update(); +} + void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) { ERR_FAIL_INDEX(p_gutter, gutters.size()); ERR_FAIL_NULL(p_object); @@ -4625,18 +4318,6 @@ Color TextEdit::get_line_background_color(int p_line) { return text.get_line_background_color(p_line); } -void TextEdit::add_keyword(const String &p_keyword) { - keywords.insert(p_keyword); -} - -void TextEdit::clear_keywords() { - keywords.clear(); -} - -void TextEdit::set_auto_indent(bool p_auto_indent) { - auto_indent = p_auto_indent; -} - void TextEdit::cut() { if (readonly) { return; @@ -4652,7 +4333,7 @@ void TextEdit::cut() { _remove_text(cursor.line, 0, cursor.line + 1, 0); } else { _remove_text(cursor.line, 0, cursor.line, text[cursor.line].length()); - backspace_at_cursor(); + backspace(); cursor_set_line(cursor.line + 1); } @@ -5199,7 +4880,6 @@ int TextEdit::get_last_unhidden_line() const { int TextEdit::get_indent_level(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); - // Counts number of tabs and spaces before line starts. int tab_count = 0; int whitespace_count = 0; int line_length = text[p_line].size(); @@ -5212,28 +4892,17 @@ int TextEdit::get_indent_level(int p_line) const { break; } } - return tab_count * indent_size + whitespace_count; + return tab_count * text.get_tab_size() + whitespace_count; } -bool TextEdit::is_line_comment(int p_line) const { - // Checks to see if this line is the start of a comment. - ERR_FAIL_INDEX_V(p_line, text.size(), false); +int TextEdit::get_first_non_whitespace_column(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); - int line_length = text[p_line].size(); - for (int i = 0; i < line_length - 1; i++) { - if (_is_whitespace(text[p_line][i])) { - continue; - } - if (_is_symbol(text[p_line][i])) { - if (text[p_line][i] == '\\') { - i++; // Skip quoted anything. - continue; - } - return text[p_line][i] == '#' || (i + 1 < line_length && text[p_line][i] == '/' && text[p_line][i + 1] == '/'); - } - break; + int col = 0; + while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) { + col++; } - return false; + return col; } int TextEdit::get_line_count() const { @@ -5409,32 +5078,18 @@ void TextEdit::_push_current_op() { } } -void TextEdit::set_indent_using_spaces(const bool p_use_spaces) { - indent_using_spaces = p_use_spaces; -} - -bool TextEdit::is_indent_using_spaces() const { - return indent_using_spaces; -} - -void TextEdit::set_indent_size(const int p_size) { - ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); - if (indent_size != p_size) { - indent_size = p_size; - text.set_indent_size(p_size); - text.invalidate_all_lines(); - } - - space_indent = ""; - for (int i = 0; i < p_size; i++) { - space_indent += " "; +void TextEdit::set_tab_size(const int p_size) { + ERR_FAIL_COND_MSG(p_size <= 0, "Tab size must be greater than 0."); + if (p_size == text.get_tab_size()) { + return; } - + text.set_tab_size(p_size); + text.invalidate_all_lines(); update(); } -int TextEdit::get_indent_size() { - return indent_size; +int TextEdit::get_tab_size() const { + return text.get_tab_size(); } void TextEdit::set_draw_tabs(bool p_draw) { @@ -6027,6 +5682,11 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language); ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language); + ClassDB::bind_method(D_METHOD("get_first_non_whitespace_column", "line"), &TextEdit::get_first_non_whitespace_column); + ClassDB::bind_method(D_METHOD("get_indent_level", "line"), &TextEdit::get_indent_level); + ClassDB::bind_method(D_METHOD("set_tab_size", "size"), &TextEdit::set_tab_size); + ClassDB::bind_method(D_METHOD("get_tab_size"), &TextEdit::get_tab_size); + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); ClassDB::bind_method(D_METHOD("insert_text_at_cursor", "text"), &TextEdit::insert_text_at_cursor); @@ -6081,6 +5741,10 @@ 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("delete_selection"), &TextEdit::delete_selection); + ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace); + BIND_VMETHOD(MethodInfo("_backspace")); + ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste); @@ -6136,6 +5800,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_gutter_clickable", "gutter"), &TextEdit::is_gutter_clickable); ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable); ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable); + ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters); ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw); // Line gutters. @@ -6262,7 +5927,7 @@ TextEdit::TextEdit() { _update_caches(); set_default_cursor_shape(CURSOR_IBEAM); - text.set_indent_size(indent_size); + text.set_tab_size(text.get_tab_size()); text.clear(); h_scroll = memnew(HScrollBar); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index c04d758abb..dcd5c6d0f8 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -96,7 +96,7 @@ private: bool hidden = false; Line() { - data_buf.instance(); + data_buf.instantiate(); } }; @@ -112,11 +112,12 @@ private: int width = -1; - int indent_size = 4; + int tab_size = 4; int gutter_count = 0; public: - void set_indent_size(int p_indent_size); + void set_tab_size(int p_tab_size); + int get_tab_size() const; void set_font(const Ref<Font> &p_font); void set_font_size(int p_font_size); void set_font_features(const Dictionary &p_features); @@ -257,11 +258,7 @@ private: uint32_t version = 0; uint32_t saved_version = 0; - int max_chars = 0; bool readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly. - bool indent_using_spaces = false; - int indent_size = 4; - String space_indent = " "; Timer *caret_blink_timer; bool caret_blink_enabled = false; @@ -296,7 +293,6 @@ private: bool scroll_past_end_of_file_enabled = false; bool brace_matching_enabled = false; bool highlight_current_line = false; - bool auto_indent = false; String cut_copy_line; bool insert_mode = false; @@ -420,14 +416,9 @@ private: void _clear(); - int _calculate_spaces_till_next_left_indent(int column); - int _calculate_spaces_till_next_right_indent(int column); - // Methods used in shortcuts void _swap_current_input_direction(); void _new_line(bool p_split_current = true, bool p_above = false); - void _indent_right(); - void _indent_left(); void _move_cursor_left(bool p_select, bool p_move_by_word = false); void _move_cursor_right(bool p_select, bool p_move_by_word = false); void _move_cursor_up(bool p_select); @@ -436,9 +427,8 @@ private: void _move_cursor_to_line_end(bool p_select); void _move_cursor_page_up(bool p_select); void _move_cursor_page_down(bool p_select); - void _backspace(bool p_word = false, bool p_all_to_left = false); + void _do_backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); - void _delete_selection(); void _move_cursor_document_start(bool p_select); void _move_cursor_document_end(bool p_select); void _handle_unicode_character(uint32_t unicode, bool p_had_selection); @@ -522,6 +512,8 @@ public: void set_gutter_overwritable(int p_gutter, bool p_overwritable); bool is_gutter_overwritable(int p_gutter) const; + void merge_gutters(int p_from_line, int p_to_line); + void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback); // Line gutters. @@ -635,12 +627,9 @@ public: bool has_ime_text() const; void set_line(int line, String new_text); int get_row_height() const; - void backspace_at_cursor(); - void indent_selected_lines_left(); - void indent_selected_lines_right(); int get_indent_level(int p_line) const; - bool is_line_comment(int p_line) const; + int get_first_non_whitespace_column(int p_line) const; inline void set_scroll_pass_end_of_file(bool p_enabled) { scroll_past_end_of_file_enabled = p_enabled; @@ -653,7 +642,6 @@ public: brace_matching_enabled = p_enabled; update(); } - void set_auto_indent(bool p_auto_indent); void center_viewport_to_cursor(); @@ -689,9 +677,6 @@ public: void set_readonly(bool p_readonly); bool is_readonly() const; - void set_max_chars(int p_max_chars); - int get_max_chars() const; - void set_wrap_enabled(bool p_wrap_enabled); bool is_wrap_enabled() const; bool line_wraps(int line) const; @@ -699,6 +684,9 @@ public: void clear(); + void delete_selection(); + + virtual void backspace(); void cut(); void copy(); void paste(); @@ -730,10 +718,8 @@ public: void redo(); void clear_undo_history(); - void set_indent_using_spaces(const bool p_use_spaces); - bool is_indent_using_spaces() const; - void set_indent_size(const int p_size); - int get_indent_size(); + void set_tab_size(const int p_size); + int get_tab_size() const; void set_draw_tabs(bool p_draw); bool is_drawing_tabs() const; void set_draw_spaces(bool p_draw); @@ -744,9 +730,6 @@ public: void set_insert_mode(bool p_enabled); bool is_insert_mode() const; - void add_keyword(const String &p_keyword); - void clear_keywords(); - double get_v_scroll() const; void set_v_scroll(double p_scroll); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 46ce9d5ca9..5e5dec3579 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -221,43 +221,87 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu double width_texture = 0.0; double first_section_size = 0.0; double last_section_size = 0.0; - switch (mode) { - case FILL_LEFT_TO_RIGHT: - case FILL_RIGHT_TO_LEFT: { + switch (p_mode) { + case FILL_LEFT_TO_RIGHT: { width_total = dst_rect.size.x; width_texture = texture_size.x; first_section_size = topleft.x; last_section_size = bottomright.x; } break; - case FILL_TOP_TO_BOTTOM: - case FILL_BOTTOM_TO_TOP: { + case FILL_RIGHT_TO_LEFT: { + width_total = dst_rect.size.x; + width_texture = texture_size.x; + // In contrast to `FILL_LEFT_TO_RIGHT`, `first_section_size` and `last_section_size` should switch value. + first_section_size = bottomright.x; + last_section_size = topleft.x; + } break; + case FILL_TOP_TO_BOTTOM: { width_total = dst_rect.size.y; width_texture = texture_size.y; first_section_size = topleft.y; last_section_size = bottomright.y; } break; + case FILL_BOTTOM_TO_TOP: { + width_total = dst_rect.size.y; + width_texture = texture_size.y; + // Similar to `FILL_RIGHT_TO_LEFT`. + first_section_size = bottomright.y; + last_section_size = topleft.y; + } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { - // TODO: Implement + width_total = dst_rect.size.x; + width_texture = texture_size.x; + first_section_size = topleft.x; + last_section_size = bottomright.x; } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { - // TODO: Implement + width_total = dst_rect.size.y; + width_texture = texture_size.y; + first_section_size = topleft.y; + last_section_size = bottomright.y; } break; case FILL_CLOCKWISE: case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: { - // Those modes are circular, not relevant for nine patch + // Those modes are circular, not relevant for nine patch. } break; + case FILL_MODE_MAX: + break; } double width_filled = width_total * p_ratio; double middle_section_size = MAX(0.0, width_texture - first_section_size - last_section_size); - middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size))); - last_section_size = MAX(0.0, last_section_size - (width_total - width_filled)); - first_section_size = MIN(first_section_size, width_filled); - width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size); + // Maximum middle texture size. + double max_middle_texture_size = middle_section_size; + + // Maximum real middle texture size. + double max_middle_real_size = MAX(0.0, width_total - (first_section_size + last_section_size)); + + switch (p_mode) { + case FILL_BILINEAR_LEFT_AND_RIGHT: + case FILL_BILINEAR_TOP_AND_BOTTOM: { + last_section_size = MAX(0.0, last_section_size - (width_total - width_filled) * 0.5); + first_section_size = MAX(0.0, first_section_size - (width_total - width_filled) * 0.5); + + // When `width_filled` increases, `middle_section_size` only increases when either of `first_section_size` and `last_section_size` is zero. + // Also, it should always be smaller than or equal to `(width_total - (first_section_size + last_section_size))`. + double real_middle_size = width_filled - first_section_size - last_section_size; + middle_section_size *= MIN(max_middle_real_size, real_middle_size) / max_middle_real_size; + + width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size); + } break; + case FILL_MODE_MAX: + break; + default: { + middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size))); + last_section_size = MAX(0.0, last_section_size - (width_total - width_filled)); + first_section_size = MIN(first_section_size, width_filled); + width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size); + } + } - switch (mode) { + switch (p_mode) { case FILL_LEFT_TO_RIGHT: { src_rect.size.x = width_texture; dst_rect.size.x = width_filled; @@ -287,16 +331,32 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu bottomright.y = first_section_size; } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { - // TODO: Implement + double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x; + double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x); + src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; + src_rect.size.x = width_texture; + dst_rect.position.x += (width_total - width_filled) * 0.5; + dst_rect.size.x = width_filled; + topleft.x = first_section_size; + bottomright.x = last_section_size; } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { - // TODO: Implement + double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y; + double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y); + src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; + src_rect.size.y = width_texture; + dst_rect.position.y += (width_total - width_filled) * 0.5; + dst_rect.size.y = width_filled; + topleft.y = first_section_size; + bottomright.y = last_section_size; } break; case FILL_CLOCKWISE: case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: { - // Those modes are circular, not relevant for nine patch + // Those modes are circular, not relevant for nine patch. } break; + case FILL_MODE_MAX: + break; } } @@ -310,19 +370,34 @@ void TextureProgressBar::_notification(int p_what) { const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 }; switch (p_what) { case NOTIFICATION_DRAW: { - if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) { + if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) { if (under.is_valid()) { - draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under); + draw_nine_patch_stretched(under, mode, 1.0, tint_under); } if (progress.is_valid()) { draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress); } if (over.is_valid()) { - draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over); + draw_nine_patch_stretched(over, mode, 1.0, tint_over); } } else { if (under.is_valid()) { - draw_texture(under, Point2(), tint_under); + switch (mode) { + case FILL_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: { + if (nine_patch_stretch) { + Rect2 region = Rect2(Point2(), get_size()); + draw_texture_rect(under, region, false, tint_under); + } else { + draw_texture(under, Point2(), tint_under); + } + } break; + case FILL_MODE_MAX: + break; + default: + draw_texture(under, Point2(), tint_under); + } } if (progress.is_valid()) { Size2 s = progress->get_size(); @@ -353,7 +428,7 @@ void TextureProgressBar::_notification(int p_what) { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); - draw_texture_rect_region(progress, region, region, tint_progress); + draw_texture_rect(progress, region, false, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; @@ -416,12 +491,29 @@ void TextureProgressBar::_notification(int p_what) { Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); draw_texture_rect_region(progress, region, region, tint_progress); } break; + case FILL_MODE_MAX: + break; default: draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) { - draw_texture(over, Point2(), tint_over); + switch (mode) { + case FILL_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: { + if (nine_patch_stretch) { + Rect2 region = Rect2(Point2(), get_size()); + draw_texture_rect(over, region, false, tint_over); + } else { + draw_texture(over, Point2(), tint_over); + } + } break; + case FILL_MODE_MAX: + break; + default: + draw_texture(over, Point2(), tint_over); + } } } } break; @@ -429,7 +521,7 @@ void TextureProgressBar::_notification(int p_what) { } void TextureProgressBar::set_fill_mode(int p_fill) { - ERR_FAIL_INDEX(p_fill, 9); + ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); mode = (FillMode)p_fill; update(); } @@ -512,7 +604,7 @@ void TextureProgressBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over"); diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h index a3883a7017..d147c43a26 100644 --- a/scene/gui/texture_progress_bar.h +++ b/scene/gui/texture_progress_bar.h @@ -54,7 +54,8 @@ public: FILL_COUNTER_CLOCKWISE, FILL_BILINEAR_LEFT_AND_RIGHT, FILL_BILINEAR_TOP_AND_BOTTOM, - FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE + FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE, + FILL_MODE_MAX, }; void set_fill_mode(int p_fill); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 26d881955b..98de71d81c 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -489,11 +489,11 @@ TreeItem *TreeItem::create_child(int p_idx) { return ti; } -Tree *TreeItem::get_tree() { +Tree *TreeItem::get_tree() const { return tree; } -TreeItem *TreeItem::get_next() { +TreeItem *TreeItem::get_next() const { return next; } @@ -516,11 +516,11 @@ TreeItem *TreeItem::get_prev() { return prev; } -TreeItem *TreeItem::get_parent() { +TreeItem *TreeItem::get_parent() const { return parent; } -TreeItem *TreeItem::get_first_child() { +TreeItem *TreeItem::get_first_child() const { return first_child; } @@ -953,6 +953,56 @@ bool TreeItem::is_folding_disabled() const { return disable_folding; } +Size2 TreeItem::get_minimum_size(int p_column) { + ERR_FAIL_INDEX_V(p_column, cells.size(), Size2()); + Tree *tree = get_tree(); + ERR_FAIL_COND_V(!tree, Size2()); + + Size2 size; + + // Default offset? + //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin; + + // Text. + const TreeItem::Cell &cell = cells[p_column]; + if (!cell.text.is_empty()) { + if (cell.dirty) { + tree->update_item_cell(this, p_column); + } + Size2 text_size = cell.text_buf->get_size(); + size.width += text_size.width; + size.height = MAX(size.height, text_size.height); + } + + // Icon. + if (cell.mode == CELL_MODE_CHECK) { + size.width += tree->cache.checked->get_width() + tree->cache.hseparation; + } + if (cell.icon.is_valid()) { + Size2i icon_size = cell.get_icon_size(); + if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { + icon_size.width = cell.icon_max_w; + } + size.width += icon_size.width + tree->cache.hseparation; + size.height = MAX(size.height, icon_size.height); + } + + // Buttons. + for (int i = 0; i < cell.buttons.size(); i++) { + Ref<Texture2D> texture = cell.buttons[i].texture; + if (texture.is_valid()) { + Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); + size.width += button_size.width; + size.height = MAX(size.height, button_size.height); + } + } + if (cell.buttons.size() >= 2) { + size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + } + + return size; +} + Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1202,6 +1252,9 @@ void Tree::update_cache() { cache.item_margin = get_theme_constant("item_margin"); cache.button_margin = get_theme_constant("button_margin"); + cache.font_outline_color = get_theme_color("font_outline_color"); + cache.font_outline_size = get_theme_constant("outline_size"); + cache.draw_guides = get_theme_constant("draw_guides"); cache.guide_color = get_theme_color("guide_color"); cache.draw_relationship_lines = get_theme_constant("draw_relationship_lines"); @@ -1463,7 +1516,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int htotal = 0; int label_h = compute_item_height(p_item); - bool rtl = is_layout_rtl(); + bool rtl = cache.rtl; /* Calculate height of the label part */ label_h += cache.vseparation; @@ -1509,6 +1562,20 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } + if (!rtl && p_item->cells[i].buttons.size()) { + int button_w = 0; + for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { + Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; + button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + } + + int total_ofs = ofs - cache.offset.x; + + if (total_ofs + w > p_draw_size.width) { + w = MAX(button_w, p_draw_size.width - total_ofs); + } + } + int bw = 0; for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; @@ -1633,8 +1700,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_selected_color" : "font_color"); - Color font_outline_color = get_theme_color("font_outline_color"); - int outline_size = get_theme_constant("outline_size"); + Color font_outline_color = cache.font_outline_color; + int outline_size = cache.font_outline_size; Color icon_col = p_item->cells[i].icon_color; if (p_item->cells[i].dirty) { @@ -1846,78 +1913,76 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int prev_hl_ofs = base_ofs; while (c) { - if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) { - int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); - int parent_ofs = p_pos.x + cache.item_margin; - Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; + if (htotal >= 0) { + int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); - if (c->get_first_child() != nullptr) { - root_pos -= Point2i(cache.arrow->get_width(), 0); - } + // Draw relationship lines. + if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) { + int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); + int parent_ofs = p_pos.x + cache.item_margin; + Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - float line_width = cache.relationship_line_width; - float parent_line_width = cache.parent_hl_line_width; - float children_line_width = cache.children_hl_line_width; + if (c->get_first_child() != nullptr) { + root_pos -= Point2i(cache.arrow->get_width(), 0); + } + + float line_width = cache.relationship_line_width; + float parent_line_width = cache.parent_hl_line_width; + float children_line_width = cache.children_hl_line_width; #ifdef TOOLS_ENABLED - line_width *= Math::round(EDSCALE); - parent_line_width *= Math::round(EDSCALE); - children_line_width *= Math::round(EDSCALE); + line_width *= Math::round(EDSCALE); + parent_line_width *= Math::round(EDSCALE); + children_line_width *= Math::round(EDSCALE); #endif - Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; + Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; - int more_prev_ofs = 0; + int more_prev_ofs = 0; - if (root_pos.y + line_width >= 0) { - if (rtl) { - root_pos.x = get_size().width - root_pos.x; - parent_pos.x = get_size().width - parent_pos.x; - } + if (root_pos.y + line_width >= 0) { + if (rtl) { + root_pos.x = get_size().width - root_pos.x; + parent_pos.x = get_size().width - parent_pos.x; + } - // Order of parts on this bend: the horizontal line first, then the vertical line. - if (_is_branch_selected(c)) { - // If this item or one of its children is selected, we draw the line using parent highlight style. - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); - - more_prev_ofs = cache.parent_hl_line_margin; - prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); - } else if (p_item->is_selected(0)) { - // If parent item is selected (but this item is not), we draw the line using children highlight style. - // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. - if (_is_sibling_branch_selected(c)) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + // Order of parts on this bend: the horizontal line first, then the vertical line. + if (_is_branch_selected(c)) { + // If this item or one of its children is selected, we draw the line using parent highlight style. + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width); RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + more_prev_ofs = cache.parent_hl_line_margin; prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); + } else if (p_item->is_selected(0)) { + // If parent item is selected (but this item is not), we draw the line using children highlight style. + // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. + if (_is_sibling_branch_selected(c)) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + + prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); + } else { + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); + } } else { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); - } - } else { - // If nothing of the above is true, we draw the line using normal style. - // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. - if (_is_sibling_branch_selected(c)) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + // If nothing of the above is true, we draw the line using normal style. + // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. + if (_is_sibling_branch_selected(c)) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); - prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); - } else { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); + prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); + } else { + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); + } } } - } - if (htotal < 0) { - return -1; + prev_ofs = root_pos.y + more_prev_ofs; } - prev_ofs = root_pos.y + more_prev_ofs; - } - - if (htotal >= 0) { - int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); if (child_h < 0) { if (cache.draw_relationship_lines == 0) { @@ -2090,13 +2155,24 @@ void Tree::_range_click_timeout() { } } + if (!root) { + return; + } + click_handled = false; Ref<InputEventMouseButton> mb; - mb.instance(); + mb.instantiate(); + + int x_limit = get_size().width - cache.bg->get_minimum_size().width; + if (h_scroll->is_visible()) { + x_limit -= h_scroll->get_minimum_size().width; + } + + cache.rtl = is_layout_rtl(); propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, false, root, MOUSE_BUTTON_LEFT, mb); + propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MOUSE_BUTTON_LEFT, mb); blocked--; if (range_click_timer->is_one_shot()) { @@ -2119,7 +2195,7 @@ void Tree::_range_click_timeout() { } } -int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) { +int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) { int item_h = compute_item_height(p_item) + cache.vseparation; bool skip = (p_item == root && hide_root); @@ -2144,6 +2220,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool int col = -1; int col_ofs = 0; int col_width = 0; + + int limit_w = x_limit; + for (int i = 0; i < columns.size(); i++) { col_width = get_column_width(i); @@ -2159,6 +2238,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (x > col_width) { col_ofs += col_width; x -= col_width; + limit_w -= col_width; continue; } @@ -2172,10 +2252,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool int margin = x_ofs + cache.item_margin; //-cache.hseparation; //int lm = cache.bg->get_margin(SIDE_LEFT); col_width -= margin; + limit_w -= margin; col_ofs += margin; x -= margin; } else { col_width -= cache.hseparation; + limit_w -= cache.hseparation; x -= cache.hseparation; } @@ -2189,6 +2271,16 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool bool already_selected = c.selected; bool already_cursor = (p_item == selected_item) && col == selected_col; + if (!cache.rtl && p_item->cells[col].buttons.size()) { + int button_w = 0; + for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) { + Ref<Texture2D> b = p_item->cells[col].buttons[j].texture; + button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + } + + col_width = MAX(button_w, MIN(limit_w, col_width)); + } + for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; int w = b->get_size().width + cache.button_pressed->get_minimum_size().width; @@ -2210,6 +2302,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool //emit_signal("button_pressed"); return -1; } + col_width -= w + cache.button_margin; } @@ -2420,7 +2513,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool TreeItem *c = p_item->first_child; while (c) { - int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod); + int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, x_limit, p_double_click, c, p_button, p_mod); if (child_h < 0) { return -1; // break, stop propagating, no need to anymore @@ -3098,8 +3191,14 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { pressing_for_editor = false; propagate_mouse_activated = false; + int x_limit = get_size().width - cache.bg->get_minimum_size().width; + if (h_scroll->is_visible()) { + x_limit -= h_scroll->get_minimum_size().width; + } + + cache.rtl = is_layout_rtl(); blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, b->is_double_click(), root, b->get_button_index(), b); + propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, b->is_double_click(), root, b->get_button_index(), b); blocked--; if (pressing_for_editor) { @@ -3161,6 +3260,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } break; + default: + break; } } @@ -3276,7 +3377,7 @@ Size2 Tree::get_internal_min_size() const { size.height += get_item_height(root); } for (int i = 0; i < columns.size(); i++) { - size.width += columns[i].min_width; + size.width += get_column_minimum_width(i); } return size; @@ -3300,26 +3401,38 @@ void Tree::update_scrollbars() { h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - Size2 min = get_internal_min_size(); + Size2 internal_min_size = get_internal_min_size(); - if (min.height < size.height - hmin.height) { - v_scroll->hide(); - cache.offset.y = 0; - } else { + bool display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) > size.height; + bool display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) > size.width; + for (int i = 0; i < 2; i++) { + // Check twice, as both values are dependent on each other. + if (display_hscroll) { + display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) + hmin.height > size.height; + } + if (display_vscroll) { + display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) + vmin.width > size.width; + } + } + + if (display_vscroll) { v_scroll->show(); - v_scroll->set_max(min.height); + v_scroll->set_max(internal_min_size.height); v_scroll->set_page(size.height - hmin.height - tbh); cache.offset.y = v_scroll->get_value(); + } else { + v_scroll->hide(); + cache.offset.y = 0; } - if (min.width < size.width - vmin.width) { - h_scroll->hide(); - cache.offset.x = 0; - } else { + if (display_hscroll) { h_scroll->show(); - h_scroll->set_max(min.width); + h_scroll->set_max(internal_min_size.width); h_scroll->set_page(size.width - vmin.width); cache.offset.x = h_scroll->get_value(); + } else { + h_scroll->hide(); + cache.offset.x = 0; } } @@ -3437,6 +3550,9 @@ void Tree::_notification(int p_what) { Point2 draw_ofs; draw_ofs += bg->get_offset(); Size2 draw_size = get_size() - bg->get_minimum_size(); + if (h_scroll->is_visible()) { + draw_size.width -= h_scroll->get_minimum_size().width; + } bg->draw(ci, Rect2(Point2(), get_size())); @@ -3445,7 +3561,9 @@ void Tree::_notification(int p_what) { draw_ofs.y += tbh; draw_size.y -= tbh; - if (root) { + cache.rtl = is_layout_rtl(); + + if (root && get_size().x > 0 && get_size().y > 0) { draw_item(Point2(), draw_ofs, draw_size, root); } @@ -3456,7 +3574,7 @@ void Tree::_notification(int p_what) { Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); Ref<Font> f = cache.tb_font; Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); - if (is_layout_rtl()) { + if (cache.rtl) { tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; } sb->draw(ci, tbrect); @@ -3513,7 +3631,17 @@ void Tree::_update_all() { } Size2 Tree::get_minimum_size() const { - return Size2(1, 1); + if (h_scroll_enabled && v_scroll_enabled) { + return Size2(); + } else { + Vector2 min_size = get_internal_min_size(); + Ref<StyleBox> bg = cache.bg; + if (bg.is_valid()) { + min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); + min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); + } + return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y); + } } TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { @@ -3541,11 +3669,11 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { return ti; } -TreeItem *Tree::get_root() { +TreeItem *Tree::get_root() const { return root; } -TreeItem *Tree::get_last_item() { +TreeItem *Tree::get_last_item() const { TreeItem *last = root; while (last) { @@ -3675,13 +3803,13 @@ bool Tree::is_root_hidden() const { return hide_root; } -void Tree::set_column_min_width(int p_column, int p_min_width) { +void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) { ERR_FAIL_INDEX(p_column, columns.size()); - if (p_min_width < 1) { + if (p_min_width < 0) { return; } - columns.write[p_column].min_width = p_min_width; + columns.write[p_column].custom_min_width = p_min_width; update(); } @@ -3692,6 +3820,36 @@ void Tree::set_column_expand(int p_column, bool p_expand) { update(); } +void Tree::set_column_expand_ratio(int p_column, int p_ratio) { + ERR_FAIL_INDEX(p_column, columns.size()); + columns.write[p_column].expand_ratio = p_ratio; + update(); +} + +void Tree::set_column_clip_content(int p_column, bool p_fit) { + ERR_FAIL_INDEX(p_column, columns.size()); + + columns.write[p_column].clip_content = p_fit; + update(); +} + +bool Tree::is_column_expanding(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), false); + + return columns[p_column].expand; +} +int Tree::get_column_expand_ratio(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), 1); + + return columns[p_column].expand_ratio; +} + +bool Tree::is_column_clipping_content(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), false); + + return columns[p_column].clip_content; +} + TreeItem *Tree::get_selected() const { return selected_item; } @@ -3748,44 +3906,81 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { return nullptr; } -int Tree::get_column_width(int p_column) const { +int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - if (!columns[p_column].expand) { - return columns[p_column].min_width; + int min_width = columns[p_column].custom_min_width; + + if (show_column_titles) { + min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width); + } + + if (!columns[p_column].clip_content) { + int depth = 0; + TreeItem *next; + for (TreeItem *item = get_root(); item; item = next) { + next = item->get_next_visible(); + // Compute the depth in tree. + if (next && p_column == 0) { + if (next->get_parent() == item) { + depth += 1; + } else { + TreeItem *common_parent = item->get_parent(); + while (common_parent != next->get_parent()) { + common_parent = common_parent->get_parent(); + depth -= 1; + } + } + } + + // Get the item minimum size. + Size2 item_size = item->get_minimum_size(p_column); + if (p_column == 0) { + item_size.width += cache.item_margin * depth; + } + min_width = MAX(min_width, item_size.width); + } } - int expand_area = get_size().width; + return min_width; +} - Ref<StyleBox> bg = cache.bg; +int Tree::get_column_width(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - if (bg.is_valid()) { - expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); - } + int column_width = get_column_minimum_width(p_column); - if (v_scroll->is_visible_in_tree()) { - expand_area -= v_scroll->get_combined_minimum_size().width; - } + if (columns[p_column].expand) { + int expand_area = get_size().width; - int expanding_columns = 0; - int expanding_total = 0; + Ref<StyleBox> bg = cache.bg; - for (int i = 0; i < columns.size(); i++) { - if (!columns[i].expand) { - expand_area -= columns[i].min_width; - } else { - expanding_total += columns[i].min_width; - expanding_columns++; + if (bg.is_valid()) { + expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); } - } - if (expand_area < expanding_total) { - return columns[p_column].min_width; - } + if (v_scroll->is_visible_in_tree()) { + expand_area -= v_scroll->get_combined_minimum_size().width; + } - ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen + int expanding_total = 0; - return expand_area * columns[p_column].min_width / expanding_total; + for (int i = 0; i < columns.size(); i++) { + expand_area -= get_column_minimum_width(i); + if (columns[i].expand) { + expanding_total += columns[i].expand_ratio; + } + } + + if (expand_area >= expanding_total && expanding_total > 0) { + column_width += expand_area * columns[p_column].expand_ratio / expanding_total; + } + } + + if (p_column < columns.size() - 1) { + column_width += cache.hseparation; + } + return column_width; } void Tree::propagate_set_columns(TreeItem *p_item) { @@ -4047,6 +4242,24 @@ void Tree::scroll_to_item(TreeItem *p_item) { } } +void Tree::set_h_scroll_enabled(bool p_enable) { + h_scroll_enabled = p_enable; + minimum_size_changed(); +} + +bool Tree::is_h_scroll_enabled() const { + return h_scroll_enabled; +} + +void Tree::set_v_scroll_enabled(bool p_enable) { + v_scroll_enabled = p_enable; + minimum_size_changed(); +} + +bool Tree::is_v_scroll_enabled() const { + return v_scroll_enabled; +} + TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) { TreeItem *from = p_at; TreeItem *loop = nullptr; // Safe-guard against infinite loop. @@ -4422,8 +4635,14 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root); - ClassDB::bind_method(D_METHOD("set_column_min_width", "column", "min_width"), &Tree::set_column_min_width); + ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width); ClassDB::bind_method(D_METHOD("set_column_expand", "column", "expand"), &Tree::set_column_expand); + ClassDB::bind_method(D_METHOD("set_column_expand_ratio", "column", "ratio"), &Tree::set_column_expand_ratio); + ClassDB::bind_method(D_METHOD("set_column_clip_content", "column", "enable"), &Tree::set_column_clip_content); + ClassDB::bind_method(D_METHOD("is_column_expanding", "column"), &Tree::is_column_expanding); + ClassDB::bind_method(D_METHOD("is_column_clipping_content", "column"), &Tree::is_column_clipping_content); + ClassDB::bind_method(D_METHOD("get_column_expand_ratio", "column"), &Tree::get_column_expand_ratio); + ClassDB::bind_method(D_METHOD("get_column_width", "column"), &Tree::get_column_width); ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root); @@ -4468,6 +4687,12 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll); ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item); + ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled); + ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled); + + ClassDB::bind_method(D_METHOD("set_v_scroll_enabled", "h_scroll"), &Tree::set_v_scroll_enabled); + ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &Tree::is_v_scroll_enabled); + ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding); ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden); @@ -4487,6 +4712,8 @@ void Tree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden"); ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_h_scroll_enabled", "is_h_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_v_scroll_enabled", "is_v_scroll_enabled"); ADD_SIGNAL(MethodInfo("item_selected")); ADD_SIGNAL(MethodInfo("cell_selected")); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 0571a605a5..10e6642303 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -115,7 +115,7 @@ private: Ref<Font> custom_font; Cell() { - text_buf.instance(); + text_buf.instantiate(); } Size2 get_icon_size() const; @@ -315,16 +315,18 @@ public: void set_disable_folding(bool p_disable); bool is_folding_disabled() const; + Size2 get_minimum_size(int p_column); + /* Item manipulation */ TreeItem *create_child(int p_idx = -1); - Tree *get_tree(); + Tree *get_tree() const; TreeItem *get_prev(); - TreeItem *get_next(); - TreeItem *get_parent(); - TreeItem *get_first_child(); + TreeItem *get_next() const; + TreeItem *get_parent() const; + TreeItem *get_first_child() const; TreeItem *get_prev_visible(bool p_wrap = false); TreeItem *get_next_visible(bool p_wrap = false); @@ -408,15 +410,17 @@ private: int drop_mode_flags = 0; struct ColumnInfo { - int min_width = 1; + int custom_min_width = 0; + int expand_ratio = 1; bool expand = true; + bool clip_content = false; String title; Ref<TextLine> text_buf; Dictionary opentype_features; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; ColumnInfo() { - text_buf.instance(); + text_buf.instantiate(); } }; @@ -448,7 +452,7 @@ private: void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color); int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); - int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); + int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); void _text_editor_submit(String p_text); void _text_editor_modal_close(); void value_editor_changed(double p_value); @@ -502,6 +506,7 @@ private: Color parent_hl_line_color; Color children_hl_line_color; Color custom_button_font_highlight; + Color font_outline_color; int hseparation = 0; int vseparation = 0; @@ -516,6 +521,7 @@ private: int draw_guides = 0; int scroll_border = 0; int scroll_speed = 0; + int font_outline_size = 0; enum ClickType { CLICK_NONE, @@ -538,6 +544,8 @@ private: Point2i text_editor_position; + bool rtl = false; + } cache; int _get_title_button_height() const; @@ -546,6 +554,9 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; + bool h_scroll_enabled = true; + bool v_scroll_enabled = true; + Size2 get_internal_min_size() const; void update_cache(); void update_scrollbars(); @@ -623,12 +634,19 @@ public: void clear(); TreeItem *create_item(TreeItem *p_parent = nullptr, int p_idx = -1); - TreeItem *get_root(); - TreeItem *get_last_item(); + TreeItem *get_root() const; + TreeItem *get_last_item() const; - void set_column_min_width(int p_column, int p_min_width); + void set_column_custom_minimum_width(int p_column, int p_min_width); void set_column_expand(int p_column, bool p_expand); + void set_column_expand_ratio(int p_column, int p_ratio); + void set_column_clip_content(int p_column, bool p_fit); + int get_column_minimum_width(int p_column) const; int get_column_width(int p_column) const; + int get_column_expand_ratio(int p_column) const; + + bool is_column_expanding(int p_column) const; + bool is_column_clipping_content(int p_column) const; void set_hide_root(bool p_enabled); bool is_root_hidden() const; @@ -679,6 +697,10 @@ public: Point2 get_scroll() const; void scroll_to_item(TreeItem *p_item); + void set_h_scroll_enabled(bool p_enable); + bool is_h_scroll_enabled() const; + void set_v_scroll_enabled(bool p_enable); + bool is_v_scroll_enabled() const; void set_cursor_can_exit_tree(bool p_enable); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index ed3c0b7a56..229b5384ea 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -452,7 +452,7 @@ void VideoPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream"); //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ; ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01"), "set_volume_db", "get_volume_db"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_EXP_RANGE, "0,15,0.01", PROPERTY_USAGE_NONE), "set_volume", "get_volume"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_RANGE, "0,15,0.01,exp", PROPERTY_USAGE_NONE), "set_volume", "get_volume"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_paused", "is_paused"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand"); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 361f584a5d..f81a3ef630 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -239,7 +239,7 @@ bool CanvasItemMaterial::get_particles_anim_loop() const { void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { if (property.name.begins_with("particles_anim_") && !particles_animation) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1227,7 +1227,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); ADD_GROUP("Material", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index f699e68715..4540e42b4c 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -103,14 +103,6 @@ real_t CanvasLayer::get_rotation() const { return rot; } -void CanvasLayer::set_rotation_degrees(real_t p_degrees) { - set_rotation(Math::deg2rad(p_degrees)); -} - -real_t CanvasLayer::get_rotation_degrees() const { - return Math::rad2deg(get_rotation()); -} - void CanvasLayer::set_scale(const Vector2 &p_scale) { if (locrotscale_dirty) { _update_locrotscale(); @@ -277,9 +269,6 @@ void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &CanvasLayer::set_rotation); ClassDB::bind_method(D_METHOD("get_rotation"), &CanvasLayer::get_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &CanvasLayer::set_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &CanvasLayer::get_rotation_degrees); - ClassDB::bind_method(D_METHOD("set_scale", "scale"), &CanvasLayer::set_scale); ClassDB::bind_method(D_METHOD("get_scale"), &CanvasLayer::get_scale); @@ -298,8 +287,7 @@ void CanvasLayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer"); ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform"), "set_transform", "get_transform"); ADD_GROUP("", ""); diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 899039340a..5de1ebf18d 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -79,9 +79,6 @@ public: void set_rotation(real_t p_radians); real_t get_rotation() const; - void set_rotation_degrees(real_t p_degrees); - real_t get_rotation_degrees() const; - void set_scale(const Size2 &p_scale); Size2 get_scale() const; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 927b114fbc..775dfa4c46 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -322,7 +322,8 @@ bool HTTPRequest::_update_connection() { } else { // Did not request yet, do request - Error err = client->request_raw(method, request_string, headers, request_data); + int size = request_data.size(); + Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size); if (err != OK) { call_deferred("_request_done", RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; @@ -627,7 +628,7 @@ void HTTPRequest::_bind_methods() { } HTTPRequest::HTTPRequest() { - client.instance(); + client = Ref<HTTPClient>(HTTPClient::create()); timer = memnew(Timer); timer->set_one_shot(true); timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout)); diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 1661984e30..89dac5f5a8 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -88,7 +88,7 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene if (!ps.is_valid()) { return nullptr; } - Node *scene = ps->instance(); + Node *scene = ps->instantiate(); if (!scene) { return nullptr; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index e1abea81ef..787b283e8c 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -35,6 +35,7 @@ #include "core/object/message_queue.h" #include "core/string/print_string.h" #include "instance_placeholder.h" +#include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" @@ -402,6 +403,7 @@ void Node::set_process_mode(ProcessMode p_mode) { } bool prev_can_process = can_process(); + bool prev_enabled = _is_enabled(); data.process_mode = p_mode; @@ -416,6 +418,7 @@ void Node::set_process_mode(ProcessMode p_mode) { } bool next_can_process = can_process(); + bool next_enabled = _is_enabled(); int pause_notification = 0; @@ -425,7 +428,16 @@ void Node::set_process_mode(ProcessMode p_mode) { pause_notification = NOTIFICATION_UNPAUSED; } - _propagate_process_owner(data.process_owner, pause_notification); + int enabled_notification = 0; + + if (prev_enabled && !next_enabled) { + enabled_notification = NOTIFICATION_DISABLED; + } else if (!prev_enabled && next_enabled) { + enabled_notification = NOTIFICATION_ENABLED; + } + + _propagate_process_owner(data.process_owner, pause_notification, enabled_notification); + #ifdef TOOLS_ENABLED // This is required for the editor to update the visibility of disabled nodes // It's very expensive during runtime to change, so editor-only @@ -454,17 +466,21 @@ Node::ProcessMode Node::get_process_mode() const { return data.process_mode; } -void Node::_propagate_process_owner(Node *p_owner, int p_notification) { +void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification) { data.process_owner = p_owner; - if (p_notification != 0) { - notification(p_notification); + if (p_pause_notification != 0) { + notification(p_pause_notification); + } + + if (p_enabled_notification != 0) { + notification(p_enabled_notification); } for (int i = 0; i < data.children.size(); i++) { Node *c = data.children[i]; if (c->data.process_mode == PROCESS_MODE_INHERIT) { - c->_propagate_process_owner(p_owner, p_notification); + c->_propagate_process_owner(p_owner, p_pause_notification, p_enabled_notification); } } } @@ -491,7 +507,7 @@ bool Node::is_network_master() const { /***** RPC CONFIG ********/ -uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, NetworkedMultiplayerPeer::TransferMode p_transfer_mode, int p_channel) { +uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) { for (int i = 0; i < data.rpc_methods.size(); i++) { if (data.rpc_methods[i].name == p_method) { MultiplayerAPI::RPCConfig &nd = data.rpc_methods.write[i]; @@ -668,6 +684,27 @@ bool Node::_can_process(bool p_paused) const { } } +bool Node::_is_enabled() const { + ProcessMode process_mode; + + if (data.process_mode == PROCESS_MODE_INHERIT) { + if (!data.process_owner) { + process_mode = PROCESS_MODE_PAUSABLE; + } else { + process_mode = data.process_owner->data.process_mode; + } + } else { + process_mode = data.process_mode; + } + + return (process_mode != PROCESS_MODE_DISABLED); +} + +bool Node::is_enabled() const { + ERR_FAIL_COND_V(!is_inside_tree(), false); + return _is_enabled(); +} + float Node::get_physics_process_delta_time() const { if (data.tree) { return data.tree->get_physics_process_time(); @@ -1040,7 +1077,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) { ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent #ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_child->is_a_parent_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); + ERR_FAIL_COND_MSG(p_child->is_ancestor_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); #endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); @@ -1283,7 +1320,7 @@ Node *Node::find_parent(const String &p_mask) const { return nullptr; } -bool Node::is_a_parent_of(const Node *p_node) const { +bool Node::is_ancestor_of(const Node *p_node) const { ERR_FAIL_NULL_V(p_node, false); Node *p = p_node->data.parent; while (p) { @@ -1686,6 +1723,13 @@ int Node::get_index() const { return data.pos; } +Ref<Tween> Node::create_tween() { + ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create Tween when not inside scene tree."); + Ref<Tween> tween = get_tree()->create_tween(); + tween->bind_node(this); + return tween; +} + void Node::remove_and_skip() { ERR_FAIL_COND(!data.parent); @@ -1741,7 +1785,7 @@ String Node::get_editor_description() const { void Node::set_editable_instance(Node *p_node, bool p_editable) { ERR_FAIL_NULL(p_node); - ERR_FAIL_COND(!is_a_parent_of(p_node)); + ERR_FAIL_COND(!is_ancestor_of(p_node)); if (!p_editable) { p_node->data.editable_instance = false; // Avoid this flag being needlessly saved; @@ -1756,13 +1800,13 @@ bool Node::is_editable_instance(const Node *p_node) const { if (!p_node) { return false; // Easier, null is never editable. :) } - ERR_FAIL_COND_V(!is_a_parent_of(p_node), false); + ERR_FAIL_COND_V(!is_ancestor_of(p_node), false); return p_node->data.editable_instance; } Node *Node::get_deepest_editable_node(Node *p_start_node) const { ERR_FAIL_NULL_V(p_start_node, nullptr); - ERR_FAIL_COND_V(!is_a_parent_of(p_start_node), p_start_node); + ERR_FAIL_COND_V(!is_ancestor_of(p_start_node), p_start_node); Node const *iterated_item = p_start_node; Node *node = p_start_node; @@ -1805,7 +1849,7 @@ bool Node::get_scene_instance_load_placeholder() const { Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const { Node *node = nullptr; - bool instanced = false; + bool instantiated = false; if (Object::cast_to<InstancePlaceholder>(this)) { const InstancePlaceholder *ip = Object::cast_to<const InstancePlaceholder>(this); @@ -1822,13 +1866,13 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const ges = PackedScene::GEN_EDIT_STATE_INSTANCE; } #endif - node = res->instance(ges); + node = res->instantiate(ges); ERR_FAIL_COND_V(!node, nullptr); - instanced = true; + instantiated = true; } else { - Object *obj = ClassDB::instance(get_class()); + Object *obj = ClassDB::instantiate(get_class()); ERR_FAIL_COND_V(!obj, nullptr); node = Object::cast_to<Node>(obj); if (!node) { @@ -1848,9 +1892,9 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const List<const Node *> node_tree; node_tree.push_front(this); - if (instanced) { - // Since nodes in the instanced hierarchy won't be duplicated explicitly, we need to make an inventory - // of all the nodes in the tree of the instanced scene in order to transfer the values of the properties + if (instantiated) { + // Since nodes in the instantiated hierarchy won't be duplicated explicitly, we need to make an inventory + // of all the nodes in the tree of the instantiated scene in order to transfer the values of the properties Vector<const Node *> instance_roots; instance_roots.push_back(this); @@ -1858,8 +1902,8 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { for (int i = 0; i < N->get()->get_child_count(); ++i) { Node *descendant = N->get()->get_child(i); - // Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later - // but remember non-instanced nodes that are hidden below instanced ones + // Skip nodes not really belonging to the instantiated hierarchy; they'll be processed normally later + // but remember non-instantiated nodes that are hidden below instantiated ones if (!instance_roots.has(descendant->get_owner())) { if (descendant->get_parent() && descendant->get_parent() != this && descendant->data.owner != descendant->get_parent()) { hidden_roots.push_back(descendant); @@ -1942,7 +1986,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const if (get_child(i)->data.parent_owned) { continue; } - if (instanced && get_child(i)->data.owner == this) { + if (instantiated && get_child(i)->data.owner == this) { continue; //part of instance } @@ -2066,7 +2110,7 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc // because re-targeting of connections from some descendant to another is not possible // if the emitter node comes later in tree order than the receiver void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { - if ((this != p_original) && !(p_original->is_a_parent_of(this))) { + if ((this != p_original) && !(p_original->is_ancestor_of(this))) { return; } @@ -2449,7 +2493,9 @@ String Node::get_configuration_warnings_as_string() const { if (i > 0) { all_warnings += "\n\n"; } - all_warnings += String(warnings[i]); + // Format as a bullet point list to make multiple warnings easier to distinguish + // from each other. + all_warnings += String::utf8("• ") + String(warnings[i]); } return all_warnings; } @@ -2459,7 +2505,7 @@ void Node::update_configuration_warnings() { if (!is_inside_tree()) { return; } - if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { get_tree()->emit_signal(SceneStringNames::get_singleton()->node_configuration_warning_changed, this); } #endif @@ -2506,7 +2552,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); ClassDB::bind_method(D_METHOD("is_inside_tree"), &Node::is_inside_tree); - ClassDB::bind_method(D_METHOD("is_a_parent_of", "node"), &Node::is_a_parent_of); + ClassDB::bind_method(D_METHOD("is_ancestor_of", "node"), &Node::is_ancestor_of); ClassDB::bind_method(D_METHOD("is_greater_than", "node"), &Node::is_greater_than); ClassDB::bind_method(D_METHOD("get_path"), &Node::get_path); ClassDB::bind_method(D_METHOD("get_path_to", "node"), &Node::get_path_to); @@ -2555,6 +2601,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal); ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); + ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false)); @@ -2576,7 +2623,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer); - ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description); ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description); @@ -2619,6 +2666,8 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS); BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); + BIND_CONSTANT(NOTIFICATION_DISABLED); + BIND_CONSTANT(NOTIFICATION_ENABLED); BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE); BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE); diff --git a/scene/main/node.h b/scene/main/node.h index e7b36f351b..c6727ce884 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -41,6 +41,9 @@ class Viewport; class SceneState; +class Tween; +class PropertyTweener; + class Node : public Object { GDCLASS(Node, Object); OBJ_CATEGORY("Nodes"); @@ -163,7 +166,7 @@ private: void _propagate_after_exit_tree(); void _propagate_validate_owner(); void _print_stray_nodes(); - void _propagate_process_owner(Node *p_owner, int p_notification); + void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); Array _get_node_and_resource(const NodePath &p_path); void _duplicate_signals(const Node *p_original, Node *p_copy) const; @@ -181,6 +184,7 @@ private: void _propagate_pause_notification(bool p_enable); _FORCE_INLINE_ bool _can_process(bool p_paused) const; + _FORCE_INLINE_ bool _is_enabled() const; protected: void _block() { data.blocked++; } @@ -224,6 +228,8 @@ public: NOTIFICATION_INTERNAL_PROCESS = 25, NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26, NOTIFICATION_POST_ENTER_TREE = 27, + NOTIFICATION_DISABLED = 28, + NOTIFICATION_ENABLED = 29, //keep these linked to node NOTIFICATION_WM_MOUSE_ENTER = 1002, @@ -279,7 +285,7 @@ public: _FORCE_INLINE_ bool is_inside_tree() const { return data.inside_tree; } - bool is_a_parent_of(const Node *p_node) const; + bool is_ancestor_of(const Node *p_node) const; bool is_greater_than(const Node *p_node) const; NodePath get_path() const; @@ -308,6 +314,8 @@ public: void remove_and_skip(); int get_index() const; + Ref<Tween> create_tween(); + void print_tree(); void print_tree_pretty(); @@ -380,6 +388,7 @@ public: ProcessMode get_process_mode() const; bool can_process() const; bool can_process_notification(int p_what) const; + bool is_enabled() const; void request_ready(); @@ -421,7 +430,7 @@ public: int get_network_master() const; bool is_network_master() const; - uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, NetworkedMultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC + uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC Vector<MultiplayerAPI::RPCConfig> get_node_rpc_methods() const; void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 001c857d4c..fefe4c9f0d 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -41,6 +41,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "node.h" +#include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" #include "scene/resources/font.h" #include "scene/resources/material.h" @@ -412,6 +413,9 @@ bool SceneTree::physics_process(float p_time) { _notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS); _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack + + process_tweens(p_time, true); + flush_transform_notifications(); root_lock--; @@ -476,6 +480,8 @@ bool SceneTree::process(float p_time) { E = N; } + process_tweens(p_time, false); + flush_transform_notifications(); //additional transforms after timers update _call_idle_callbacks(); @@ -510,6 +516,32 @@ bool SceneTree::process(float p_time) { return _quit; } +void SceneTree::process_tweens(float p_delta, bool p_physics) { + // This methods works similarly to how SceneTreeTimers are handled. + List<Ref<Tween>>::Element *L = tweens.back(); + + for (List<Ref<Tween>>::Element *E = tweens.front(); E;) { + List<Ref<Tween>>::Element *N = E->next(); + // Don't process if paused or process mode doesn't match. + if ((paused && E->get()->should_pause()) || (p_physics == (E->get()->get_process_mode() == Tween::TWEEN_PROCESS_IDLE))) { + if (E == L) { + break; + } + E = N; + continue; + } + + if (!E->get()->step(p_delta)) { + E->get()->set_valid(false); + tweens.erase(E); + } + if (E == L) { + break; + } + E = N; + } +} + void SceneTree::finalize() { _flush_delete_queue(); @@ -591,7 +623,7 @@ void SceneTree::set_quit_on_go_back(bool p_enable) { #ifdef TOOLS_ENABLED bool SceneTree::is_node_being_edited(const Node *p_node) const { - return Engine::get_singleton()->is_editor_hint() && edited_scene_root && (edited_scene_root->is_a_parent_of(p_node) || edited_scene_root == p_node); + return Engine::get_singleton()->is_editor_hint() && edited_scene_root && (edited_scene_root->is_ancestor_of(p_node) || edited_scene_root == p_node); } #endif @@ -1061,7 +1093,7 @@ Error SceneTree::change_scene(const String &p_path) { Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { Node *new_scene = nullptr; if (p_scene.is_valid()) { - new_scene = p_scene->instance(); + new_scene = p_scene->instantiate(); ERR_FAIL_COND_V(!new_scene, ERR_CANT_CREATE); } @@ -1082,31 +1114,32 @@ void SceneTree::add_current_scene(Node *p_current) { Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_always) { Ref<SceneTreeTimer> stt; - stt.instance(); + stt.instantiate(); stt->set_process_always(p_process_always); stt->set_time_left(p_delay_sec); timers.push_back(stt); return stt; } -void SceneTree::_network_peer_connected(int p_id) { - emit_signal("network_peer_connected", p_id); -} - -void SceneTree::_network_peer_disconnected(int p_id) { - emit_signal("network_peer_disconnected", p_id); +Ref<Tween> SceneTree::create_tween() { + Ref<Tween> tween; + tween.instantiate(); + tween->set_valid(true); + tweens.push_back(tween); + return tween; } -void SceneTree::_connected_to_server() { - emit_signal("connected_to_server"); -} +Array SceneTree::get_processed_tweens() { + Array ret; + ret.resize(tweens.size()); -void SceneTree::_connection_failed() { - emit_signal("connection_failed"); -} + int i = 0; + for (List<Ref<Tween>>::Element *E = tweens.front(); E; E = E->next()) { + ret[i] = E->get(); + i++; + } -void SceneTree::_server_disconnected() { - emit_signal("server_disconnected"); + return ret; } Ref<MultiplayerAPI> SceneTree::get_multiplayer() const { @@ -1124,58 +1157,8 @@ bool SceneTree::is_multiplayer_poll_enabled() const { void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { ERR_FAIL_COND(!p_multiplayer.is_valid()); - if (multiplayer.is_valid()) { - multiplayer->disconnect("network_peer_connected", callable_mp(this, &SceneTree::_network_peer_connected)); - multiplayer->disconnect("network_peer_disconnected", callable_mp(this, &SceneTree::_network_peer_disconnected)); - multiplayer->disconnect("connected_to_server", callable_mp(this, &SceneTree::_connected_to_server)); - multiplayer->disconnect("connection_failed", callable_mp(this, &SceneTree::_connection_failed)); - multiplayer->disconnect("server_disconnected", callable_mp(this, &SceneTree::_server_disconnected)); - } - multiplayer = p_multiplayer; multiplayer->set_root_node(root); - - multiplayer->connect("network_peer_connected", callable_mp(this, &SceneTree::_network_peer_connected)); - multiplayer->connect("network_peer_disconnected", callable_mp(this, &SceneTree::_network_peer_disconnected)); - multiplayer->connect("connected_to_server", callable_mp(this, &SceneTree::_connected_to_server)); - multiplayer->connect("connection_failed", callable_mp(this, &SceneTree::_connection_failed)); - multiplayer->connect("server_disconnected", callable_mp(this, &SceneTree::_server_disconnected)); -} - -void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) { - multiplayer->set_network_peer(p_network_peer); -} - -Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const { - return multiplayer->get_network_peer(); -} - -bool SceneTree::is_network_server() const { - return multiplayer->is_network_server(); -} - -bool SceneTree::has_network_peer() const { - return multiplayer->has_network_peer(); -} - -int SceneTree::get_network_unique_id() const { - return multiplayer->get_network_unique_id(); -} - -Vector<int> SceneTree::get_network_connected_peers() const { - return multiplayer->get_network_connected_peers(); -} - -int SceneTree::get_rpc_sender_id() const { - return multiplayer->get_rpc_sender_id(); -} - -void SceneTree::set_refuse_new_network_connections(bool p_refuse) { - multiplayer->set_refuse_new_network_connections(p_refuse); -} - -bool SceneTree::is_refusing_new_network_connections() const { - return multiplayer->is_refusing_new_network_connections(); } void SceneTree::_bind_methods() { @@ -1197,6 +1180,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused); ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "process_always"), &SceneTree::create_timer, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("create_tween"), &SceneTree::create_tween); + ClassDB::bind_method(D_METHOD("get_processed_tweens"), &SceneTree::get_processed_tweens); ClassDB::bind_method(D_METHOD("get_node_count"), &SceneTree::get_node_count); ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame); @@ -1242,24 +1227,12 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer); ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled); ClassDB::bind_method(D_METHOD("is_multiplayer_poll_enabled"), &SceneTree::is_multiplayer_poll_enabled); - ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer); - ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer); - ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server); - ClassDB::bind_method(D_METHOD("has_network_peer"), &SceneTree::has_network_peer); - ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &SceneTree::get_network_connected_peers); - ClassDB::bind_method(D_METHOD("get_network_unique_id"), &SceneTree::get_network_unique_id); - ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &SceneTree::get_rpc_sender_id); - ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &SceneTree::set_refuse_new_network_connections); - ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &SceneTree::is_refusing_new_network_connections); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_collisions_hint"), "set_debug_collisions_hint", "is_debugging_collisions_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_navigation_hint"), "set_debug_navigation_hint", "is_debugging_navigation_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); - ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_edited_scene_root", "get_edited_scene_root"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_current_scene", "get_current_scene"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_root"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_multiplayer", "get_multiplayer"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled"); @@ -1275,11 +1248,6 @@ void SceneTree::_bind_methods() { ADD_SIGNAL(MethodInfo("physics_frame")); ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen"))); - ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("connected_to_server")); - ADD_SIGNAL(MethodInfo("connection_failed")); - ADD_SIGNAL(MethodInfo("server_disconnected")); BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT); BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 78c4c14e97..0be0e185a5 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -47,6 +47,7 @@ class Window; class Material; class Mesh; class SceneDebugger; +class Tween; class SceneTreeTimer : public RefCounted { GDCLASS(SceneTreeTimer, RefCounted); @@ -151,19 +152,13 @@ private: //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2); List<Ref<SceneTreeTimer>> timers; + List<Ref<Tween>> tweens; ///network/// Ref<MultiplayerAPI> multiplayer; bool multiplayer_poll = true; - void _network_peer_connected(int p_id); - void _network_peer_disconnected(int p_id); - - void _connected_to_server(); - void _connection_failed(); - void _server_disconnected(); - static SceneTree *singleton; friend class Node; @@ -171,6 +166,7 @@ private: void node_added(Node *p_node); void node_removed(Node *p_node); void node_renamed(Node *p_node); + void process_tweens(float p_delta, bool p_physics_frame); Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); @@ -318,6 +314,8 @@ public: Error reload_current_scene(); Ref<SceneTreeTimer> create_timer(float p_delay_sec, bool p_process_always = true); + Ref<Tween> create_tween(); + Array get_processed_tweens(); //used by Main::start, don't use otherwise void add_current_scene(Node *p_current); @@ -332,16 +330,6 @@ public: void set_multiplayer_poll_enabled(bool p_enabled); bool is_multiplayer_poll_enabled() const; void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer); - void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer); - Ref<NetworkedMultiplayerPeer> get_network_peer() const; - bool is_network_server() const; - bool has_network_peer() const; - int get_network_unique_id() const; - Vector<int> get_network_connected_peers() const; - int get_rpc_sender_id() const; - - void set_refuse_new_network_connections(bool p_refuse); - bool is_refusing_new_network_connections() const; static void add_idle_callback(IdleCallback p_callback); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 3d65c12cb7..d22a6b2875 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -63,7 +63,12 @@ bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_valu if (o) { o->override = p_value; if (active) { - RS::get_singleton()->global_variable_set_override(*r, p_value); + if (o->override.get_type() == Variant::OBJECT) { + RID tex_rid = p_value; + RS::get_singleton()->global_variable_set_override(*r, tex_rid); + } else { + RS::get_singleton()->global_variable_set_override(*r, p_value); + } } o->in_use = p_value.get_type() != Variant::NIL; return true; @@ -228,7 +233,12 @@ void ShaderGlobalsOverride::_activate() { while ((K = overrides.next(K))) { Override *o = overrides.getptr(*K); if (o->in_use && o->override.get_type() != Variant::NIL) { - RS::get_singleton()->global_variable_set_override(*K, o->override); + if (o->override.get_type() == Variant::OBJECT) { + RID tex_rid = o->override; + RS::get_singleton()->global_variable_set_override(*K, tex_rid); + } else { + RS::get_singleton()->global_variable_set_override(*K, o->override); + } } } diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index a5ceec9c8b..f52237251c 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -37,7 +37,7 @@ void Timer::_notification(int p_what) { case NOTIFICATION_READY: { if (autostart) { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { break; } #endif @@ -207,7 +207,7 @@ void Timer::_bind_methods() { ADD_SIGNAL(MethodInfo("timeout")); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_timer_process_callback", "get_timer_process_callback"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_EXP_RANGE, "0.001,4096,0.001,or_greater"), "set_wait_time", "get_wait_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_RANGE, "0.001,4096,0.001,or_greater,exp"), "set_wait_time", "get_wait_time"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paused", "is_paused"); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 84a6dbe889..9a2e0c5230 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -594,7 +594,7 @@ void Viewport::_process_picking() { if (!has_mouse_event) { Ref<InputEventMouseMotion> mm; - mm.instance(); + mm.instantiate(); mm->set_device(InputEvent::DEVICE_ID_INTERNAL); mm->set_global_position(physics_last_mousepos); @@ -644,9 +644,9 @@ void Viewport::_process_picking() { physics_last_mouse_state.meta = mb->is_meta_pressed(); if (mb->is_pressed()) { - physics_last_mouse_state.mouse_mask |= (1 << (mb->get_button_index() - 1)); + physics_last_mouse_state.mouse_mask |= (MouseButton)(1 << (mb->get_button_index() - 1)); } else { - physics_last_mouse_state.mouse_mask &= ~(1 << (mb->get_button_index() - 1)); + physics_last_mouse_state.mouse_mask &= (MouseButton) ~(1 << (mb->get_button_index() - 1)); // If touch mouse raised, assume we don't know last mouse pos until new events come if (mb->get_device() == InputEvent::DEVICE_ID_TOUCH_MOUSE) { @@ -929,6 +929,15 @@ bool Viewport::is_audio_listener_2d() const { return audio_listener_2d; } +void Viewport::set_disable_3d(bool p_disable) { + disable_3d = p_disable; + RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d); +} + +bool Viewport::is_3d_disabled() const { + return disable_3d; +} + void Viewport::enable_canvas_transform_override(bool p_enable) { if (override_canvas_transform == p_enable) { return; @@ -1746,7 +1755,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ Control *c = Object::cast_to<Control>(p_node); - if (!c || !c->clips_input() || c->has_point(matrix.affine_inverse().xform(p_global))) { + if (!c || !c->is_clipping_contents() || c->has_point(matrix.affine_inverse().xform(p_global))) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i)); if (!ci || ci->is_set_as_top_level()) { @@ -1770,7 +1779,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ } Control *drag_preview = _gui_get_drag_preview(); - if (!drag_preview || (c != drag_preview && !drag_preview->is_a_parent_of(c))) { + if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) { r_inv_xform = matrix; return c; } @@ -2585,10 +2594,10 @@ void Viewport::_drop_mouse_focus() { for (int i = 0; i < 3; i++) { if (mask & (1 << i)) { Ref<InputEventMouseButton> mb; - mb.instance(); + mb.instantiate(); mb->set_position(c->get_local_mouse_position()); mb->set_global_position(c->get_local_mouse_position()); - mb->set_button_index(i + 1); + mb->set_button_index(MouseButton(i + 1)); mb->set_pressed(false); c->call(SceneStringNames::get_singleton()->_gui_input, mb); } @@ -2661,12 +2670,12 @@ void Viewport::_post_gui_grab_click_focus() { for (int i = 0; i < 3; i++) { if (mask & (1 << i)) { Ref<InputEventMouseButton> mb; - mb.instance(); + mb.instantiate(); //send unclick mb->set_position(click); - mb->set_button_index(i + 1); + mb->set_button_index(MouseButton(i + 1)); mb->set_pressed(false); gui.mouse_focus->call(SceneStringNames::get_singleton()->_gui_input, mb); } @@ -2679,12 +2688,12 @@ void Viewport::_post_gui_grab_click_focus() { for (int i = 0; i < 3; i++) { if (mask & (1 << i)) { Ref<InputEventMouseButton> mb; - mb.instance(); + mb.instantiate(); //send click mb->set_position(click); - mb->set_button_index(i + 1); + mb->set_button_index(MouseButton(i + 1)); mb->set_pressed(true); gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb); } @@ -3023,7 +3032,7 @@ void Viewport::input(const Ref<InputEvent> &p_event, bool p_local_coords) { return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { return; } @@ -3065,7 +3074,7 @@ void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coor return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { return; } @@ -3259,8 +3268,8 @@ Viewport::DebugDraw Viewport::get_debug_draw() const { return debug_draw; } -int Viewport::get_render_info(RenderInfo p_info) { - return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfo(p_info)); +int Viewport::get_render_info(RenderInfoType p_type, RenderInfo p_info) { + return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfoType(p_type), RS::ViewportRenderInfo(p_info)); } void Viewport::set_snap_controls_to_pixels(bool p_enable) { @@ -3490,7 +3499,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw); ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw); - ClassDB::bind_method(D_METHOD("get_render_info", "info"), &Viewport::get_render_info); + ClassDB::bind_method(D_METHOD("get_render_info", "type", "info"), &Viewport::get_render_info); ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr); ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr); @@ -3516,6 +3525,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d); ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d); + ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d); + ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled); + ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position); ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse); @@ -3582,6 +3594,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled"); ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled (Fastest),2x (Fast),4x (Average),8x (Slow),16x (Slower)"), "set_msaa", "get_msaa"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); @@ -3637,13 +3650,14 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX); BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_INFO_VERTICES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_INFO_MATERIAL_CHANGES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_INFO_SHADER_CHANGES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_INFO_SURFACE_CHANGES_IN_FRAME); + BIND_ENUM_CONSTANT(RENDER_INFO_PRIMITIVES_IN_FRAME); BIND_ENUM_CONSTANT(RENDER_INFO_DRAW_CALLS_IN_FRAME); BIND_ENUM_CONSTANT(RENDER_INFO_MAX); + BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_VISIBLE); + BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_SHADOW); + BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_MAX); + BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLED); BIND_ENUM_CONSTANT(DEBUG_DRAW_UNSHADED); BIND_ENUM_CONSTANT(DEBUG_DRAW_LIGHTING); @@ -3698,7 +3712,7 @@ Viewport::Viewport() { viewport = RenderingServer::get_singleton()->viewport_create(); texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport); - default_texture.instance(); + default_texture.instantiate(); default_texture->vp = const_cast<Viewport *>(this); viewport_textures.insert(default_texture.ptr()); default_texture->proxy = RS::get_singleton()->texture_proxy_create(texture_rid); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index d905e44a82..0f3bb8707d 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -115,14 +115,17 @@ public: enum RenderInfo { RENDER_INFO_OBJECTS_IN_FRAME, - RENDER_INFO_VERTICES_IN_FRAME, - RENDER_INFO_MATERIAL_CHANGES_IN_FRAME, - RENDER_INFO_SHADER_CHANGES_IN_FRAME, - RENDER_INFO_SURFACE_CHANGES_IN_FRAME, + RENDER_INFO_PRIMITIVES_IN_FRAME, RENDER_INFO_DRAW_CALLS_IN_FRAME, RENDER_INFO_MAX }; + enum RenderInfoType { + RENDER_INFO_TYPE_VISIBLE, + RENDER_INFO_TYPE_SHADOW, + RENDER_INFO_TYPE_MAX + }; + enum DebugDraw { DEBUG_DRAW_DISABLED, DEBUG_DRAW_UNSHADED, @@ -288,6 +291,8 @@ private: void _update_listener(); void _update_listener_2d(); + bool disable_3d = false; + void _propagate_enter_world(Node *p_node); void _propagate_exit_world(Node *p_node); void _propagate_viewport_notification(Node *p_node, int p_what); @@ -503,6 +508,9 @@ public: void set_as_audio_listener_2d(bool p_enable); bool is_audio_listener_2d() const; + void set_disable_3d(bool p_disable); + bool is_3d_disabled() const; + void update_canvas_items(); Rect2 get_visible_rect() const; @@ -591,7 +599,7 @@ public: void set_debug_draw(DebugDraw p_debug_draw); DebugDraw get_debug_draw() const; - int get_render_info(RenderInfo p_info); + int get_render_info(RenderInfoType p_type, RenderInfo p_info); void set_snap_controls_to_pixels(bool p_enable); bool is_snap_controls_to_pixels_enabled() const; @@ -694,6 +702,7 @@ VARIANT_ENUM_CAST(Viewport::SDFScale); VARIANT_ENUM_CAST(Viewport::SDFOversize); VARIANT_ENUM_CAST(SubViewport::ClearMode); VARIANT_ENUM_CAST(Viewport::RenderInfo); +VARIANT_ENUM_CAST(Viewport::RenderInfoType); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index d793be1869..9299f8d6be 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -227,7 +227,8 @@ void Window::_make_window() { } } - window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), f, Rect2i(position, size)); + DisplayServer::VSyncMode vsync_mode = DisplayServer::get_singleton()->window_get_vsync_mode(DisplayServer::MAIN_WINDOW_ID); + window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size)); ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); @@ -1393,6 +1394,8 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("is_embedded"), &Window::is_embedded); + ClassDB::bind_method(D_METHOD("get_contents_minimum_size"), &Window::get_contents_minimum_size); + ClassDB::bind_method(D_METHOD("set_content_scale_size", "size"), &Window::set_content_scale_size); ClassDB::bind_method(D_METHOD("get_content_scale_size"), &Window::get_content_scale_size); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 2a97ae3acf..ea0df685a1 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -31,6 +31,7 @@ #include "register_scene_types.h" #include "core/config/project_settings.h" +#include "core/extension/native_extension_manager.h" #include "core/object/class_db.h" #include "core/os/os.h" #include "scene/2d/animated_sprite_2d.h" @@ -146,6 +147,7 @@ #include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/height_map_shape_3d.h" +#include "scene/resources/immediate_mesh.h" #include "scene/resources/line_shape_2d.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -203,7 +205,6 @@ #include "scene/3d/decal.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/3d/gpu_particles_collision_3d.h" -#include "scene/3d/immediate_geometry_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" @@ -260,33 +261,33 @@ void register_scene_types() { Node::init_node_hrcr(); - resource_loader_font.instance(); + resource_loader_font.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_font); #ifndef DISABLE_DEPRECATED - resource_loader_compat_font.instance(); + resource_loader_compat_font.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_compat_font); #endif /* DISABLE_DEPRECATED */ - resource_loader_stream_texture.instance(); + resource_loader_stream_texture.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_stream_texture); - resource_loader_texture_layered.instance(); + resource_loader_texture_layered.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); - resource_loader_texture_3d.instance(); + resource_loader_texture_3d.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_texture_3d); - resource_saver_text.instance(); + resource_saver_text.instantiate(); ResourceSaver::add_resource_format_saver(resource_saver_text, true); - resource_loader_text.instance(); + resource_loader_text.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_text, true); - resource_saver_shader.instance(); + resource_saver_shader.instantiate(); ResourceSaver::add_resource_format_saver(resource_saver_shader, true); - resource_loader_shader.instance(); + resource_loader_shader.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_shader, true); OS::get_singleton()->yield(); //may take time to init @@ -407,6 +408,11 @@ void register_scene_types() { ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); + ClassDB::register_virtual_class<Tweener>(); + ClassDB::register_class<PropertyTweener>(); + ClassDB::register_class<IntervalTweener>(); + ClassDB::register_class<CallbackTweener>(); + ClassDB::register_class<MethodTweener>(); ClassDB::register_class<AnimationTree>(); ClassDB::register_class<AnimationNode>(); @@ -453,7 +459,6 @@ void register_scene_types() { ClassDB::register_class<MeshInstance3D>(); ClassDB::register_class<OccluderInstance3D>(); ClassDB::register_class<Occluder3D>(); - ClassDB::register_class<ImmediateGeometry3D>(); ClassDB::register_virtual_class<SpriteBase3D>(); ClassDB::register_class<Sprite3D>(); ClassDB::register_class<AnimatedSprite3D>(); @@ -581,6 +586,7 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeTransformDecompose>(); ClassDB::register_class<VisualShaderNodeTexture>(); ClassDB::register_class<VisualShaderNodeCurveTexture>(); + ClassDB::register_class<VisualShaderNodeCurve3Texture>(); ClassDB::register_virtual_class<VisualShaderNodeSample3D>(); ClassDB::register_class<VisualShaderNodeTexture2DArray>(); ClassDB::register_class<VisualShaderNodeTexture3D>(); @@ -712,6 +718,7 @@ void register_scene_types() { ClassDB::register_virtual_class<Mesh>(); ClassDB::register_class<ArrayMesh>(); + ClassDB::register_class<ImmediateMesh>(); ClassDB::register_class<MultiMesh>(); ClassDB::register_class<SurfaceTool>(); ClassDB::register_class<MeshDataTool>(); @@ -768,6 +775,7 @@ void register_scene_types() { ClassDB::register_class<AtlasTexture>(); ClassDB::register_class<MeshTexture>(); ClassDB::register_class<CurveTexture>(); + ClassDB::register_class<Curve3Texture>(); ClassDB::register_class<GradientTexture>(); ClassDB::register_class<ProxyTexture>(); ClassDB::register_class<AnimatedTexture>(); @@ -930,14 +938,12 @@ void register_scene_types() { ClassDB::add_compatibility_class("Physics2DServerSW", "PhysicsServer2DSW"); ClassDB::add_compatibility_class("Physics2DServer", "PhysicsServer2D"); ClassDB::add_compatibility_class("Physics2DShapeQueryParameters", "PhysicsShapeQueryParameters2D"); - ClassDB::add_compatibility_class("Physics2DShapeQueryResult", "PhysicsShapeQueryResult2D"); ClassDB::add_compatibility_class("Physics2DTestMotionResult", "PhysicsTestMotionResult2D"); ClassDB::add_compatibility_class("PhysicsBody", "PhysicsBody3D"); ClassDB::add_compatibility_class("PhysicsDirectBodyState", "PhysicsDirectBodyState3D"); ClassDB::add_compatibility_class("PhysicsDirectSpaceState", "PhysicsDirectSpaceState3D"); ClassDB::add_compatibility_class("PhysicsServer", "PhysicsServer3D"); ClassDB::add_compatibility_class("PhysicsShapeQueryParameters", "PhysicsShapeQueryParameters3D"); - ClassDB::add_compatibility_class("PhysicsShapeQueryResult", "PhysicsShapeQueryResult3D"); ClassDB::add_compatibility_class("PinJoint", "PinJoint3D"); ClassDB::add_compatibility_class("PlaneShape", "WorldMarginShape3D"); ClassDB::add_compatibility_class("ProceduralSky", "Sky"); @@ -1035,9 +1041,13 @@ void register_scene_types() { } } SceneDebugger::initialize(); + + NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); } void unregister_scene_types() { + NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); + SceneDebugger::deinitialize(); clear_default_theme(); diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 81062feb46..8ffd2df112 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -596,7 +596,7 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { Ref<AudioStreamPlaybackSample> sample; - sample.instance(); + sample.instantiate(); sample->base = Ref<AudioStreamSample>(this); return sample; } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 0ffeb8a5bf..de557494c3 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -487,7 +487,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo Point2i from; Ref<BitMap> fill; - fill.instance(); + fill.instantiate(); fill->create(get_size()); Vector<Vector<Vector2>> polygons; @@ -525,7 +525,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); Ref<BitMap> copy; - copy.instance(); + copy.instantiate(); copy->create(get_size()); copy->bitmask = bitmask; @@ -604,7 +604,7 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con void BitMap::resize(const Size2 &p_new_size) { Ref<BitMap> new_bitmap; - new_bitmap.instance(); + new_bitmap.instantiate(); new_bitmap->create(p_new_size); int lw = MIN(width, p_new_size.width); int lh = MIN(height, p_new_size.height); @@ -621,7 +621,7 @@ void BitMap::resize(const Size2 &p_new_size) { Ref<Image> BitMap::convert_to_image() const { Ref<Image> image; - image.instance(); + image.instantiate(); image->create(width, height, false, Image::FORMAT_L8); for (int i = 0; i < width; i++) { diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index 34c6bc05bc..b633196424 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -175,11 +175,11 @@ void CameraEffects::_bind_methods() { ADD_GROUP("DOF Blur", "dof_blur_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); // Override exposure diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index a4228d48b4..5464a46df4 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -559,7 +559,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // ScrollContainer Ref<StyleBoxEmpty> empty; - empty.instance(); + empty.instantiate(); theme->set_stylebox("bg", "ScrollContainer", empty); // WindowDialog @@ -979,7 +979,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<Theme> t; - t.instance(); + t.instantiate(); Ref<StyleBox> default_style; Ref<Texture2D> default_icon; @@ -993,10 +993,10 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { // The default DynamicFont is chosen to have a small file size since it's // embedded in both editor and export template binaries. Ref<Font> dynamic_font; - dynamic_font.instance(); + dynamic_font.instantiate(); Ref<FontData> dynamic_font_data; - dynamic_font_data.instance(); + dynamic_font_data.instantiate(); dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size); dynamic_font->add_data(dynamic_font_data); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index c04b271d81..8550af8bcb 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1329,7 +1329,7 @@ void Environment::_bind_methods() { 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_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + 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::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_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 6f87c524d8..032171847d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -813,7 +813,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size) const { if (cache.has(hash)) { buffer = cache.get(hash); } else { - buffer.instance(); + buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); @@ -838,7 +838,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { - lines_buffer.instance(); + lines_buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); @@ -870,7 +870,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t if (cache.has(hash)) { buffer = cache.get(hash); } else { - buffer.instance(); + buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); @@ -905,7 +905,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { - lines_buffer.instance(); + lines_buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); @@ -1041,7 +1041,7 @@ RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_origina } Ref<FontData> dfont; - dfont.instance(); + dfont.instantiate(); dfont->load_resource(p_path); if (r_error) { @@ -1096,11 +1096,11 @@ RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_o } Ref<FontData> dfont; - dfont.instance(); + dfont.instantiate(); dfont->load_resource(p_path); Ref<Font> font; - font.instance(); + font.instantiate(); font->add_data(dfont); if (r_error) { diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp new file mode 100644 index 0000000000..ebe65605f8 --- /dev/null +++ b/scene/resources/immediate_mesh.cpp @@ -0,0 +1,416 @@ +/*************************************************************************/ +/* immediate_mesh.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 "immediate_mesh.h" + +void ImmediateMesh::surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material) { + ERR_FAIL_COND_MSG(surface_active, "Already creating a new surface."); + active_surface_data.primitive = p_primitive; + active_surface_data.material = p_material; + surface_active = true; +} +void ImmediateMesh::surface_set_color(const Color &p_color) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + + if (!uses_colors) { + colors.resize(vertices.size()); + for (uint32_t i = 0; i < colors.size(); i++) { + colors[i] = p_color; + } + uses_colors = true; + } + + current_color = p_color; +} +void ImmediateMesh::surface_set_normal(const Vector3 &p_normal) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + + if (!uses_normals) { + normals.resize(vertices.size()); + for (uint32_t i = 0; i < normals.size(); i++) { + normals[i] = p_normal; + } + uses_normals = true; + } + + current_normal = p_normal; +} +void ImmediateMesh::surface_set_tangent(const Plane &p_tangent) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_tangents) { + tangents.resize(vertices.size()); + for (uint32_t i = 0; i < tangents.size(); i++) { + tangents[i] = p_tangent; + } + uses_tangents = true; + } + + current_tangent = p_tangent; +} +void ImmediateMesh::surface_set_uv(const Vector2 &p_uv) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_uvs) { + uvs.resize(vertices.size()); + for (uint32_t i = 0; i < uvs.size(); i++) { + uvs[i] = p_uv; + } + uses_uvs = true; + } + + current_uv = p_uv; +} +void ImmediateMesh::surface_set_uv2(const Vector2 &p_uv2) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_uv2s) { + uv2s.resize(vertices.size()); + for (uint32_t i = 0; i < uv2s.size(); i++) { + uv2s[i] = p_uv2; + } + uses_uv2s = true; + } + + current_uv2 = p_uv2; +} +void ImmediateMesh::surface_add_vertex(const Vector3 &p_vertex) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(vertices.size() && active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface."); + + if (uses_colors) { + colors.push_back(current_color); + } + if (uses_normals) { + normals.push_back(current_normal); + } + if (uses_tangents) { + tangents.push_back(current_tangent); + } + if (uses_uvs) { + uvs.push_back(current_uv); + } + if (uses_uv2s) { + uv2s.push_back(current_uv2); + } + vertices.push_back(p_vertex); +} + +void ImmediateMesh::surface_add_vertex_2d(const Vector2 &p_vertex) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(vertices.size() && !active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface."); + + if (uses_colors) { + colors.push_back(current_color); + } + if (uses_normals) { + normals.push_back(current_normal); + } + if (uses_tangents) { + tangents.push_back(current_tangent); + } + if (uses_uvs) { + uvs.push_back(current_uv); + } + if (uses_uv2s) { + uv2s.push_back(current_uv2); + } + Vector3 v(p_vertex.x, p_vertex.y, 0); + vertices.push_back(v); + + active_surface_data.vertex_2d = true; +} +void ImmediateMesh::surface_end() { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(!vertices.size(), "No vertices were added, surface cant be created."); + + uint32_t format = ARRAY_FORMAT_VERTEX; + + uint32_t vertex_stride = 0; + if (active_surface_data.vertex_2d) { + format |= ARRAY_FLAG_USE_2D_VERTICES; + vertex_stride = sizeof(float) * 2; + } else { + vertex_stride = sizeof(float) * 3; + } + + uint32_t normal_offset = 0; + if (uses_normals) { + format |= ARRAY_FORMAT_NORMAL; + normal_offset = vertex_stride; + vertex_stride += sizeof(uint32_t); + } + uint32_t tangent_offset = 0; + if (uses_tangents) { + format |= ARRAY_FORMAT_TANGENT; + tangent_offset += vertex_stride; + vertex_stride += sizeof(uint32_t); + } + + AABB aabb; + + { + surface_vertex_create_cache.resize(vertex_stride * vertices.size()); + uint8_t *surface_vertex_ptr = surface_vertex_create_cache.ptrw(); + for (uint32_t i = 0; i < vertices.size(); i++) { + { + float *vtx = (float *)&surface_vertex_ptr[i * vertex_stride]; + vtx[0] = vertices[i].x; + vtx[1] = vertices[i].y; + if (!active_surface_data.vertex_2d) { + vtx[2] = vertices[i].z; + } + if (i == 0) { + aabb.position = vertices[i]; + } else { + aabb.expand_to(vertices[i]); + } + } + if (uses_normals) { + uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset]; + + Vector3 n = normals[i] * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + *normal = value; + } + if (uses_tangents) { + uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset]; + Plane t = tangents[i]; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + + *tangent = value; + } + } + } + + if (uses_colors || uses_uvs || uses_uv2s) { + uint32_t attribute_stride = 0; + + if (uses_colors) { + format |= ARRAY_FORMAT_COLOR; + attribute_stride += sizeof(uint8_t) * 4; + } + uint32_t uv_offset = 0; + if (uses_uvs) { + format |= ARRAY_FORMAT_TEX_UV; + uv_offset = attribute_stride; + attribute_stride += sizeof(float) * 2; + } + uint32_t uv2_offset = 0; + if (uses_uv2s) { + format |= ARRAY_FORMAT_TEX_UV2; + uv2_offset = attribute_stride; + attribute_stride += sizeof(float) * 2; + } + + surface_attribute_create_cache.resize(vertices.size() * attribute_stride); + + uint8_t *surface_attribute_ptr = surface_attribute_create_cache.ptrw(); + + for (uint32_t i = 0; i < vertices.size(); i++) { + if (uses_colors) { + uint8_t *color8 = (uint8_t *)&surface_attribute_ptr[i * attribute_stride]; + + color8[0] = uint8_t(CLAMP(colors[i].r * 255.0, 0.0, 255.0)); + color8[1] = uint8_t(CLAMP(colors[i].g * 255.0, 0.0, 255.0)); + color8[2] = uint8_t(CLAMP(colors[i].b * 255.0, 0.0, 255.0)); + color8[3] = uint8_t(CLAMP(colors[i].a * 255.0, 0.0, 255.0)); + } + if (uses_uvs) { + float *uv = (float *)&surface_attribute_ptr[i * attribute_stride + uv_offset]; + + uv[0] = uvs[i].x; + uv[1] = uvs[i].y; + } + + if (uses_uv2s) { + float *uv2 = (float *)&surface_attribute_ptr[i * attribute_stride + uv2_offset]; + + uv2[0] = uv2s[i].x; + uv2[1] = uv2s[i].y; + } + } + } + + RS::SurfaceData sd; + + sd.primitive = RS::PrimitiveType(active_surface_data.primitive); + sd.format = format; + sd.vertex_data = surface_vertex_create_cache; + if (uses_colors || uses_uvs || uses_uv2s) { + sd.attribute_data = surface_attribute_create_cache; + } + sd.vertex_count = vertices.size(); + sd.aabb = aabb; + if (active_surface_data.material.is_valid()) { + sd.material = active_surface_data.material->get_rid(); + } + + RS::get_singleton()->mesh_add_surface(mesh, sd); + + active_surface_data.aabb = aabb; + + active_surface_data.format = format; + active_surface_data.array_len = vertices.size(); + + surfaces.push_back(active_surface_data); + + colors.clear(); + normals.clear(); + tangents.clear(); + uvs.clear(); + uv2s.clear(); + vertices.clear(); + + uses_colors = false; + uses_normals = false; + uses_tangents = false; + uses_uvs = false; + uses_uv2s = false; + + surface_active = false; +} + +void ImmediateMesh::clear_surfaces() { + RS::get_singleton()->mesh_clear(mesh); + surfaces.clear(); + surface_active = false; + + colors.clear(); + normals.clear(); + tangents.clear(); + uvs.clear(); + uv2s.clear(); + vertices.clear(); + + uses_colors = false; + uses_normals = false; + uses_tangents = false; + uses_uvs = false; + uses_uv2s = false; +} + +int ImmediateMesh::get_surface_count() const { + return surfaces.size(); +} +int ImmediateMesh::surface_get_array_len(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), -1); + return surfaces[p_idx].array_len; +} +int ImmediateMesh::surface_get_array_index_len(int p_idx) const { + return 0; +} +bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const { + return false; +} +Array ImmediateMesh::surface_get_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array()); + return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); +} +Array ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) const { + return Array(); +} +Dictionary ImmediateMesh::surface_get_lods(int p_surface) const { + return Dictionary(); +} +uint32_t ImmediateMesh::surface_get_format(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), 0); + return surfaces[p_idx].format; +} +Mesh::PrimitiveType ImmediateMesh::surface_get_primitive_type(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), PRIMITIVE_MAX); + return surfaces[p_idx].primitive; +} +void ImmediateMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_idx, int(surfaces.size())); + surfaces[p_idx].material = p_material; + RID mat; + if (p_material.is_valid()) { + mat = p_material->get_rid(); + } + RS::get_singleton()->mesh_surface_set_material(mesh, p_idx, mat); +} +Ref<Material> ImmediateMesh::surface_get_material(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), Ref<Material>()); + return surfaces[p_idx].material; +} +int ImmediateMesh::get_blend_shape_count() const { + return 0; +} +StringName ImmediateMesh::get_blend_shape_name(int p_index) const { + return StringName(); +} +void ImmediateMesh::set_blend_shape_name(int p_index, const StringName &p_name) { +} + +AABB ImmediateMesh::get_aabb() const { + AABB aabb; + for (uint32_t i = 0; i < surfaces.size(); i++) { + if (i == 0) { + aabb = surfaces[i].aabb; + } else { + aabb.merge(surfaces[i].aabb); + } + } + return aabb; +} + +void ImmediateMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("surface_begin", "primitive", "material"), &ImmediateMesh::surface_begin, DEFVAL(Ref<Material>())); + ClassDB::bind_method(D_METHOD("surface_set_color", "color"), &ImmediateMesh::surface_set_color); + ClassDB::bind_method(D_METHOD("surface_set_normal", "normal"), &ImmediateMesh::surface_set_normal); + ClassDB::bind_method(D_METHOD("surface_set_tangent", "tangent"), &ImmediateMesh::surface_set_tangent); + ClassDB::bind_method(D_METHOD("surface_set_uv", "uv"), &ImmediateMesh::surface_set_uv); + ClassDB::bind_method(D_METHOD("surface_set_uv2", "uv2"), &ImmediateMesh::surface_set_uv2); + ClassDB::bind_method(D_METHOD("surface_add_vertex", "vertex"), &ImmediateMesh::surface_add_vertex); + ClassDB::bind_method(D_METHOD("surface_add_vertex_2d", "vertex"), &ImmediateMesh::surface_add_vertex_2d); + ClassDB::bind_method(D_METHOD("surface_end"), &ImmediateMesh::surface_end); + + ClassDB::bind_method(D_METHOD("clear_surfaces"), &ImmediateMesh::clear_surfaces); +} + +RID ImmediateMesh::get_rid() const { + return mesh; +} + +ImmediateMesh::ImmediateMesh() { + mesh = RS::get_singleton()->mesh_create(); +} +ImmediateMesh::~ImmediateMesh() { + RS::get_singleton()->free(mesh); +} diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h new file mode 100644 index 0000000000..7010d40719 --- /dev/null +++ b/scene/resources/immediate_mesh.h @@ -0,0 +1,117 @@ +/*************************************************************************/ +/* immediate_mesh.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 IMMEDIATE_MESH_H +#define IMMEDIATE_MESH_H + +#include "core/templates/local_vector.h" +#include "scene/resources/mesh.h" + +class ImmediateMesh : public Mesh { + GDCLASS(ImmediateMesh, Mesh) + + RID mesh; + + bool uses_colors = false; + bool uses_normals = false; + bool uses_tangents = false; + bool uses_uvs = false; + bool uses_uv2s = false; + + Color current_color; + Vector3 current_normal; + Plane current_tangent; + Vector2 current_uv; + Vector2 current_uv2; + + LocalVector<Color> colors; + LocalVector<Vector3> normals; + LocalVector<Plane> tangents; + LocalVector<Vector2> uvs; + LocalVector<Vector2> uv2s; + LocalVector<Vector3> vertices; + + struct Surface { + PrimitiveType primitive; + Ref<Material> material; + bool vertex_2d = false; + int array_len = 0; + uint32_t format = 0; + AABB aabb; + }; + + LocalVector<Surface> surfaces; + + bool surface_active = false; + Surface active_surface_data; + + Vector<uint8_t> surface_vertex_create_cache; + Vector<uint8_t> surface_attribute_create_cache; + +protected: + static void _bind_methods(); + +public: + void surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material = Ref<Material>()); + void surface_set_color(const Color &p_color); + void surface_set_normal(const Vector3 &p_normal); + void surface_set_tangent(const Plane &p_tangent); + void surface_set_uv(const Vector2 &p_uv); + void surface_set_uv2(const Vector2 &p_uv2); + void surface_add_vertex(const Vector3 &p_vertex); + void surface_add_vertex_2d(const Vector2 &p_vertex); + void surface_end(); + + void clear_surfaces(); + + virtual int get_surface_count() const override; + virtual int surface_get_array_len(int p_idx) const override; + virtual int surface_get_array_index_len(int p_idx) const override; + virtual bool surface_is_softbody_friendly(int p_idx) const override; + virtual Array surface_get_arrays(int p_surface) const override; + virtual Array surface_get_blend_shape_arrays(int p_surface) const override; + virtual Dictionary surface_get_lods(int p_surface) const override; + virtual uint32_t surface_get_format(int p_idx) const override; + virtual PrimitiveType surface_get_primitive_type(int p_idx) const override; + 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; + virtual int get_blend_shape_count() const override; + virtual StringName get_blend_shape_name(int p_index) const override; + virtual void set_blend_shape_name(int p_index, const StringName &p_name) override; + + virtual AABB get_aabb() const override; + + virtual RID get_rid() const override; + + ImmediateMesh(); + ~ImmediateMesh(); +}; + +#endif // IMMEDIATEMESH_H diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 2c5634e6ef..a264c2d1eb 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -77,7 +77,7 @@ RID Material::get_rid() const { void Material::_validate_property(PropertyInfo &property) const { if (!_can_do_next_pass() && property.name == "next_pass") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -130,7 +130,7 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { } } if (pr) { - RenderingServer::get_singleton()->material_set_param(_get_material(), pr, p_value); + set_shader_param(pr, p_value); return true; } } @@ -152,7 +152,12 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { } if (pr) { - r_ret = RenderingServer::get_singleton()->material_get_param(_get_material(), pr); + const Map<StringName, Variant>::Element *E = param_cache.find(pr); + if (E) { + r_ret = E->get(); + } else { + r_ret = Variant(); + } return true; } } @@ -219,11 +224,31 @@ Ref<Shader> ShaderMaterial::get_shader() const { } void ShaderMaterial::set_shader_param(const StringName &p_param, const Variant &p_value) { - RS::get_singleton()->material_set_param(_get_material(), p_param, p_value); + if (p_value.get_type() == Variant::NIL) { + param_cache.erase(p_param); + RS::get_singleton()->material_set_param(_get_material(), p_param, Variant()); + } else { + param_cache[p_param] = p_value; + if (p_value.get_type() == Variant::OBJECT) { + RID tex_rid = p_value; + if (tex_rid == RID()) { + param_cache.erase(p_param); + RS::get_singleton()->material_set_param(_get_material(), p_param, Variant()); + } else { + RS::get_singleton()->material_set_param(_get_material(), p_param, tex_rid); + } + } else { + RS::get_singleton()->material_set_param(_get_material(), p_param, p_value); + } + } } Variant ShaderMaterial::get_shader_param(const StringName &p_param) const { - return RS::get_singleton()->material_get_param(_get_material(), p_param); + if (param_cache.has(p_param)) { + return param_cache[p_param]; + } else { + return Variant(); + } } void ShaderMaterial::_shader_changed() { @@ -345,7 +370,6 @@ void BaseMaterial3D::init_shaders() { shader_names->refraction_texture_channel = "refraction_texture_channel"; shader_names->transmittance_color = "transmittance_color"; - shader_names->transmittance_curve = "transmittance_curve"; shader_names->transmittance_depth = "transmittance_depth"; shader_names->transmittance_boost = "transmittance_boost"; @@ -692,7 +716,6 @@ void BaseMaterial3D::_update_shader() { code += "uniform vec4 transmittance_color : hint_color;\n"; code += "uniform float transmittance_depth;\n"; code += "uniform sampler2D texture_subsurface_transmittance : hint_white," + texfilter_str + ";\n"; - code += "uniform float transmittance_curve;\n"; code += "uniform float transmittance_boost;\n"; } @@ -822,9 +845,9 @@ void BaseMaterial3D::_update_shader() { code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n"; code += "\tTANGENT = normalize(TANGENT);\n"; - code += "\tBINORMAL = vec3(0.0,-1.0,0.0) * abs(NORMAL.x);\n"; - code += "\tBINORMAL+= vec3(0.0,0.0,1.0) * abs(NORMAL.y);\n"; - code += "\tBINORMAL+= vec3(0.0,-1.0,0.0) * abs(NORMAL.z);\n"; + code += "\tBINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);\n"; + code += "\tBINORMAL+= vec3(0.0,0.0,-1.0) * abs(NORMAL.y);\n"; + code += "\tBINORMAL+= vec3(0.0,1.0,0.0) * abs(NORMAL.z);\n"; code += "\tBINORMAL = normalize(BINORMAL);\n"; } @@ -878,6 +901,20 @@ void BaseMaterial3D::_update_shader() { code += "\tvec2 base_uv2 = UV2;\n"; } + if (features[FEATURE_HEIGHT_MAPPING] && flags[FLAG_UV1_USE_TRIPLANAR]) { + // Display both resource name and albedo texture name. + // Materials are often built-in to scenes, so displaying the resource name alone may not be meaningful. + // On the other hand, albedo textures are almost always external to the scene. + if (textures[TEXTURE_ALBEDO].is_valid()) { + WARN_PRINT(vformat("%s (albedo %s): Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path(), textures[TEXTURE_ALBEDO]->get_path())); + } else if (!get_path().is_empty()) { + WARN_PRINT(vformat("%s: Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path())); + } else { + // Resource wasn't saved yet. + WARN_PRINT("Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping."); + } + } + if (!RenderingServer::get_singleton()->is_low_end() && features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //heightmap not supported with triplanar code += "\t{\n"; code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-) @@ -987,9 +1024,9 @@ void BaseMaterial3D::_update_shader() { code += "\tSPECULAR = specular;\n"; } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n"; + code += "\tvec4 orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n"; } else { - code += "\tfloat orm_tex = texture(texture_orm,base_uv);\n"; + code += "\tvec4 orm_tex = texture(texture_orm,base_uv);\n"; } code += "\tROUGHNESS = orm_tex.g;\n"; @@ -1180,7 +1217,6 @@ void BaseMaterial3D::_update_shader() { code += "\tSSS_TRANSMITTANCE_COLOR=transmittance_color*trans_color_tex;\n"; code += "\tSSS_TRANSMITTANCE_DEPTH=transmittance_depth;\n"; - code += "\tSSS_TRANSMITTANCE_CURVE=transmittance_curve;\n"; code += "\tSSS_TRANSMITTANCE_BOOST=transmittance_boost;\n"; } @@ -1424,15 +1460,6 @@ float BaseMaterial3D::get_transmittance_depth() const { return transmittance_depth; } -void BaseMaterial3D::set_transmittance_curve(float p_curve) { - transmittance_curve = p_curve; - RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_curve, p_curve); -} - -float BaseMaterial3D::get_transmittance_curve() const { - return transmittance_curve; -} - void BaseMaterial3D::set_transmittance_boost(float p_boost) { transmittance_boost = p_boost; RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_boost, p_boost); @@ -1696,7 +1723,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { _validate_high_end("heightmap", property); if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) { @@ -1726,33 +1753,33 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { // alpha scissor slider isn't needed when alpha antialiasing is enabled if (property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } // alpha hash scale slider is only needed if transparency is alpha hash if (property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "alpha_antialiasing_mode" && !can_select_aa) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } // we can't choose an antialiasing mode if alpha isn't possible if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "blend_mode" && alpha_aa_enabled) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "heightmap_min_layers" || property.name == "heightmap_max_layers") && !deep_parallax) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } - if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture" || property.name == "subsurf_scatter_transmittance_curve")) { - property.usage = 0; + if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture")) { + property.usage = PROPERTY_USAGE_NONE; } if (orm) { @@ -1761,12 +1788,12 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { property.hint_string = "Unshaded,Per-Pixel"; } if (property.name.begins_with("roughness") || property.name.begins_with("metallic") || property.name.begins_with("ao_texture")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } else { if (property.name == "orm_texture") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1774,47 +1801,47 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { if (shading_mode != SHADING_MODE_PER_VERTEX) { //these may still work per vertex if (property.name.begins_with("ao")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("emission")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("metallic")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("rim")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("roughness")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("subsurf_scatter")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } //these definitely only need per pixel if (property.name.begins_with("anisotropy")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("clearcoat")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("normal")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("backlight")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("transmittance")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } } @@ -2063,7 +2090,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y) { +Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, RID *r_shader_rid) { int version = 0; if (p_shaded) { version = 1; @@ -2088,11 +2115,14 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b } if (materials_for_2d[version].is_valid()) { - return materials_for_2d[version]->get_rid(); + if (r_shader_rid) { + *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + } + return materials_for_2d[version]; } Ref<StandardMaterial3D> material; - material.instance(); + material.instantiate(); material->set_shading_mode(p_shaded ? SHADING_MODE_PER_PIXEL : SHADING_MODE_UNSHADED); material->set_transparency(p_transparent ? (p_opaque_prepass ? TRANSPARENCY_ALPHA_DEPTH_PRE_PASS : (p_cut_alpha ? TRANSPARENCY_ALPHA_SCISSOR : TRANSPARENCY_ALPHA)) : TRANSPARENCY_DISABLED); @@ -2106,7 +2136,11 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b materials_for_2d[version] = material; - return materials_for_2d[version]->get_rid(); + if (r_shader_rid) { + *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + } + + return materials_for_2d[version]; } void BaseMaterial3D::set_on_top_of_alpha() { @@ -2175,6 +2209,8 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const { } RID BaseMaterial3D::get_shader_rid() const { + MutexLock lock(material_mutex); + ((BaseMaterial3D *)this)->_update_shader(); ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); return shader_map[current_key].shader; } @@ -2246,9 +2282,6 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transmittance_depth", "depth"), &BaseMaterial3D::set_transmittance_depth); ClassDB::bind_method(D_METHOD("get_transmittance_depth"), &BaseMaterial3D::get_transmittance_depth); - ClassDB::bind_method(D_METHOD("set_transmittance_curve", "curve"), &BaseMaterial3D::set_transmittance_curve); - ClassDB::bind_method(D_METHOD("get_transmittance_curve"), &BaseMaterial3D::get_transmittance_curve); - ClassDB::bind_method(D_METHOD("set_transmittance_boost", "boost"), &BaseMaterial3D::set_transmittance_boost); ClassDB::bind_method(D_METHOD("get_transmittance_boost"), &BaseMaterial3D::get_transmittance_boost); @@ -2483,7 +2516,6 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "subsurf_scatter_transmittance_color"), "set_transmittance_color", "get_transmittance_color"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "subsurf_scatter_transmittance_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_SUBSURFACE_TRANSMITTANCE); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_depth", PROPERTY_HINT_RANGE, "0.001,8,0.001,or_greater"), "set_transmittance_depth", "get_transmittance_depth"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_curve", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_transmittance_curve", "get_transmittance_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_boost", PROPERTY_HINT_RANGE, "0.00,1.0,0.01"), "set_transmittance_boost", "get_transmittance_boost"); ADD_GROUP("Back Lighting", "backlight_"); @@ -2700,7 +2732,6 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_backlight(Color(0, 0, 0)); set_transmittance_color(Color(1, 1, 1, 1)); set_transmittance_depth(0.1); - set_transmittance_curve(1.0); set_transmittance_boost(0.0); set_refraction(0.05); set_point_size(1); diff --git a/scene/resources/material.h b/scene/resources/material.h index dc3ecdb5de..e2838e1399 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -79,6 +79,8 @@ class ShaderMaterial : public Material { GDCLASS(ShaderMaterial, Material); Ref<Shader> shader; + Map<StringName, Variant> param_cache; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -389,7 +391,6 @@ private: StringName heightmap_scale; StringName subsurface_scattering_strength; StringName transmittance_color; - StringName transmittance_curve; StringName transmittance_depth; StringName transmittance_boost; StringName backlight; @@ -458,7 +459,6 @@ private: float transmittance_amount; Color transmittance_color; float transmittance_depth; - float transmittance_curve; float transmittance_boost; Color backlight; @@ -602,9 +602,6 @@ public: void set_transmittance_depth(float p_depth); float get_transmittance_depth() const; - void set_transmittance_curve(float p_curve); - float get_transmittance_curve() const; - void set_transmittance_boost(float p_boost); float get_transmittance_boost() const; @@ -738,7 +735,7 @@ public: static void finish_shaders(); static void flush_changes(); - static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false); + static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 5e8e77c730..f44c0c3ee2 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -571,7 +571,7 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { } Ref<ConvexPolygonShape3D> shape; - shape.instance(); + shape.instantiate(); shape->set_points(convex_points); ret.push_back(shape); } @@ -590,157 +590,320 @@ Transform3D Mesh::get_builtin_bind_pose(int p_index) const { Mesh::Mesh() { } -#if 0 -static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_format, uint32_t p_elements) { - enum ArrayType { - OLD_ARRAY_VERTEX = 0, - OLD_ARRAY_NORMAL = 1, - OLD_ARRAY_TANGENT = 2, - OLD_ARRAY_COLOR = 3, - OLD_ARRAY_TEX_UV = 4, - OLD_ARRAY_TEX_UV2 = 5, - OLD_ARRAY_BONES = 6, - OLD_ARRAY_WEIGHTS = 7, - OLD_ARRAY_INDEX = 8, - OLD_ARRAY_MAX = 9 - }; - - enum ArrayFormat { - /* OLD_ARRAY FORMAT FLAGS */ - OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory - OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL, - OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT, - OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR, - OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV, - OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2, - OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES, - OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS, - OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX, - - OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1), - OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_DEFAULT = OLD_ARRAY_COMPRESS_NORMAL | OLD_ARRAY_COMPRESS_TANGENT | OLD_ARRAY_COMPRESS_COLOR | OLD_ARRAY_COMPRESS_TEX_UV | OLD_ARRAY_COMPRESS_TEX_UV2, - - OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1, - OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3, - }; - - bool vertex_16bit = p_format & ((1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE))); - bool has_bones = (p_format & OLD_ARRAY_FORMAT_BONES); - bool bone_8 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_INDEX << 2)); - bool weight_32 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_TEX_UV2 << 2)); - - print_line("convert vertex16: " + itos(vertex_16bit) + " convert bone 8 " + itos(bone_8) + " convert weight 32 " + itos(weight_32)); - - if (!vertex_16bit && !bone_8 && !weight_32) { - return p_src; - } +enum OldArrayType { + OLD_ARRAY_VERTEX, + OLD_ARRAY_NORMAL, + OLD_ARRAY_TANGENT, + OLD_ARRAY_COLOR, + OLD_ARRAY_TEX_UV, + OLD_ARRAY_TEX_UV2, + OLD_ARRAY_BONES, + OLD_ARRAY_WEIGHTS, + OLD_ARRAY_INDEX, + OLD_ARRAY_MAX, +}; - bool vertex_2d = (p_format & (OLD_ARRAY_COMPRESS_INDEX << 1)); +enum OldArrayFormat { + /* OLD_ARRAY FORMAT FLAGS */ + OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory + OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL, + OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT, + OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR, + OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV, + OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2, + OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES, + OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS, + OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX, + + OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1), + OLD_ARRAY_COMPRESS_VERTEX = 1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE), // mandatory + OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_BONES = 1 << (OLD_ARRAY_BONES + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_WEIGHTS = 1 << (OLD_ARRAY_WEIGHTS + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE), + + OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1, + OLD_ARRAY_FLAG_USE_16_BIT_BONES = OLD_ARRAY_COMPRESS_INDEX << 2, + OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3, - uint32_t src_stride = p_src.size() / p_elements; - uint32_t dst_stride = src_stride + (vertex_16bit ? 4 : 0) + (bone_8 ? 4 : 0) - (weight_32 ? 8 : 0); +}; - Vector<uint8_t> ret = p_src; +static Array _convert_old_array(const Array &p_old) { + Array new_array; + new_array.resize(Mesh::ARRAY_MAX); + new_array[Mesh::ARRAY_VERTEX] = p_old[OLD_ARRAY_VERTEX]; + new_array[Mesh::ARRAY_NORMAL] = p_old[OLD_ARRAY_NORMAL]; + new_array[Mesh::ARRAY_TANGENT] = p_old[OLD_ARRAY_TANGENT]; + new_array[Mesh::ARRAY_COLOR] = p_old[OLD_ARRAY_COLOR]; + new_array[Mesh::ARRAY_TEX_UV] = p_old[OLD_ARRAY_TEX_UV]; + new_array[Mesh::ARRAY_TEX_UV2] = p_old[OLD_ARRAY_TEX_UV2]; + new_array[Mesh::ARRAY_BONES] = p_old[OLD_ARRAY_BONES]; + new_array[Mesh::ARRAY_WEIGHTS] = p_old[OLD_ARRAY_WEIGHTS]; + new_array[Mesh::ARRAY_INDEX] = p_old[OLD_ARRAY_INDEX]; + return new_array; +} + +static Mesh::PrimitiveType _old_primitives[7] = { + Mesh::PRIMITIVE_POINTS, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_LINE_STRIP, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_TRIANGLES, + Mesh::PRIMITIVE_TRIANGLE_STRIP, + Mesh::PRIMITIVE_TRIANGLE_STRIP +}; - ret.resize(dst_stride * p_elements); - { - uint8_t *w = ret.ptrw(); - const uint8_t *r = p_src.ptr(); - - for (uint32_t i = 0; i < p_elements; i++) { - uint32_t remaining = src_stride; - const uint8_t *src = (const uint8_t *)(r + src_stride * i); - uint8_t *dst = (uint8_t *)(w + dst_stride * i); - - if (!vertex_2d) { //3D - if (vertex_16bit) { - float *dstw = (float *)dst; - const uint16_t *srcr = (const uint16_t *)src; - dstw[0] = Math::half_to_float(srcr[0]); - dstw[1] = Math::half_to_float(srcr[1]); - dstw[2] = Math::half_to_float(srcr[2]); - remaining -= 8; - src += 8; +void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) { + uint32_t dst_vertex_stride; + uint32_t dst_attribute_stride; + uint32_t dst_skin_stride; + uint32_t dst_offsets[Mesh::ARRAY_MAX]; + RenderingServer::get_singleton()->mesh_surface_make_offsets_from_format(p_new_format & (~RS::ARRAY_FORMAT_INDEX), p_elements, 0, dst_offsets, dst_vertex_stride, dst_attribute_stride, dst_skin_stride); + + vertex_data.resize(dst_vertex_stride * p_elements); + attribute_data.resize(dst_attribute_stride * p_elements); + skin_data.resize(dst_skin_stride * p_elements); + + uint8_t *dst_vertex_ptr = vertex_data.ptrw(); + uint8_t *dst_attribute_ptr = attribute_data.ptrw(); + uint8_t *dst_skin_ptr = skin_data.ptrw(); + + const uint8_t *src_vertex_ptr = p_src.ptr(); + uint32_t src_vertex_stride = p_src.size() / p_elements; + + uint32_t src_offset = 0; + for (uint32_t j = 0; j < OLD_ARRAY_INDEX; j++) { + if (!(p_old_format & (1 << j))) { + continue; + } + switch (j) { + case OLD_ARRAY_VERTEX: { + if (p_old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) { + if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } } else { - src += 12; - remaining -= 12; + if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + dst[2] = Math::half_to_float(src[2]); + } + src_offset += sizeof(uint16_t) * 4; //+pad + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + src_offset += sizeof(float) * 3; + } } - dst += 12; - } else { - if (vertex_16bit) { - float *dstw = (float *)dst; - const uint16_t *srcr = (const uint16_t *)src; - dstw[0] = Math::half_to_float(srcr[0]); - dstw[1] = Math::half_to_float(srcr[1]); - remaining -= 4; - src += 4; + } break; + case OLD_ARRAY_NORMAL: { + if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) { + const float multiplier = 1.f / 127.f * 1023.0f; + + for (uint32_t i = 0; i < p_elements; i++) { + const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); + *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; + } + src_offset += sizeof(uint32_t); } else { - src += 8; - remaining -= 8; + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); + *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; + } + src_offset += sizeof(float) * 3; } - dst += 8; - } - - if (has_bones) { - remaining -= bone_8 ? 4 : 8; - remaining -= weight_32 ? 16 : 8; - } - - for (uint32_t j = 0; j < remaining; j++) { - dst[j] = src[j]; - } - - if (has_bones) { - dst += remaining; - src += remaining; - if (bone_8) { - const uint8_t *src_bones = (const uint8_t *)src; - uint16_t *dst_bones = (uint16_t *)dst; + } break; + case OLD_ARRAY_TANGENT: { + if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { + const float multiplier = 1.f / 127.f * 1023.0f; + + for (uint32_t i = 0; i < p_elements; i++) { + const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); + *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; + if (src[3] > 0) { + *dst |= 3 << 30; + } + } + src_offset += sizeof(uint32_t); + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); + *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; + if (src[3] > 0) { + *dst |= 3 << 30; + } + } + src_offset += sizeof(float) * 4; + } - dst_bones[0] = src_bones[0]; - dst_bones[1] = src_bones[1]; - dst_bones[2] = src_bones[2]; - dst_bones[3] = src_bones[3]; + } break; + case OLD_ARRAY_COLOR: { + if (p_old_format & OLD_ARRAY_COMPRESS_COLOR) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint32_t *src = (const uint32_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]]; - src += 4; + *dst = *src; + } + src_offset += sizeof(uint32_t); } else { - for (uint32_t j = 0; j < 8; j++) { - dst[j] = src[j]; + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint8_t *dst = (uint8_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]]; + + dst[0] = uint8_t(CLAMP(src[0] * 255.0, 0.0, 255.0)); + dst[1] = uint8_t(CLAMP(src[1] * 255.0, 0.0, 255.0)); + dst[2] = uint8_t(CLAMP(src[2] * 255.0, 0.0, 255.0)); + dst[3] = uint8_t(CLAMP(src[3] * 255.0, 0.0, 255.0)); } - - src += 8; + src_offset += sizeof(float) * 4; } + } break; + case OLD_ARRAY_TEX_UV: { + if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]]; + + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]]; - dst += 8; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } - if (weight_32) { - const float *src_weights = (const float *)src; - uint16_t *dst_weights = (uint16_t *)dst; + } break; + case OLD_ARRAY_TEX_UV2: { + if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV2) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]]; - dst_weights[0] = CLAMP(src_weights[0] * 65535, 0, 65535); //16bits unorm - dst_weights[1] = CLAMP(src_weights[1] * 65535, 0, 65535); - dst_weights[2] = CLAMP(src_weights[2] * 65535, 0, 65535); - dst_weights[3] = CLAMP(src_weights[3] * 65535, 0, 65535); + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]]; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } + } break; + case OLD_ARRAY_BONES: { + if (p_old_format & OLD_ARRAY_FLAG_USE_16_BIT_BONES) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + src_offset += sizeof(uint16_t) * 4; } else { - for (uint32_t j = 0; j < 8; j++) { - dst[j] = src[j]; + for (uint32_t i = 0; i < p_elements; i++) { + const uint8_t *src = (const uint8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; } + src_offset += sizeof(uint8_t) * 4; } + } break; + case OLD_ARRAY_WEIGHTS: { + if (p_old_format & OLD_ARRAY_COMPRESS_WEIGHTS) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + src_offset += sizeof(uint16_t) * 4; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]]; + + dst[0] = uint16_t(CLAMP(src[0] * 65535.0, 0, 65535.0)); + dst[1] = uint16_t(CLAMP(src[1] * 65535.0, 0, 65535.0)); + dst[2] = uint16_t(CLAMP(src[2] * 65535.0, 0, 65535.0)); + dst[3] = uint16_t(CLAMP(src[3] * 65535.0, 0, 65535.0)); + } + src_offset += sizeof(float) * 4; + } + } break; + default: { } } } - - return ret; } -#endif bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -779,10 +942,13 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { if (d.has("arrays")) { //oldest format (2.x) ERR_FAIL_COND_V(!d.has("morph_arrays"), false); - add_surface_from_arrays(PrimitiveType(int(d["primitive"])), d["arrays"], d["morph_arrays"]); + Array morph_arrays = d["morph_arrays"]; + for (int i = 0; i < morph_arrays.size(); i++) { + morph_arrays[i] = _convert_old_array(morph_arrays[i]); + } + add_surface_from_arrays(_old_primitives[int(d["primitive"])], _convert_old_array(d["arrays"]), morph_arrays); } else if (d.has("array_data")) { -#if 0 //print_line("array data (old style"); //older format (3.x) Vector<uint8_t> array_data = d["array_data"]; @@ -792,48 +958,76 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } ERR_FAIL_COND_V(!d.has("format"), false); - uint32_t format = d["format"]; + uint32_t old_format = d["format"]; uint32_t primitive = d["primitive"]; - uint32_t primitive_remap[7] = { - PRIMITIVE_POINTS, - PRIMITIVE_LINES, - PRIMITIVE_LINE_STRIP, - PRIMITIVE_LINES, - PRIMITIVE_TRIANGLES, - PRIMITIVE_TRIANGLE_STRIP, - PRIMITIVE_TRIANGLE_STRIP - }; - - primitive = primitive_remap[primitive]; //compatibility + primitive = _old_primitives[primitive]; //compatibility ERR_FAIL_COND_V(!d.has("vertex_count"), false); int vertex_count = d["vertex_count"]; - array_data = _fix_array_compatibility(array_data, format, vertex_count); + uint32_t new_format = ARRAY_FORMAT_VERTEX; + + if (old_format & OLD_ARRAY_FORMAT_NORMAL) { + new_format |= ARRAY_FORMAT_NORMAL; + } + if (old_format & OLD_ARRAY_FORMAT_TANGENT) { + new_format |= ARRAY_FORMAT_TANGENT; + } + if (old_format & OLD_ARRAY_FORMAT_COLOR) { + new_format |= ARRAY_FORMAT_COLOR; + } + if (old_format & OLD_ARRAY_FORMAT_TEX_UV) { + new_format |= ARRAY_FORMAT_TEX_UV; + } + if (old_format & OLD_ARRAY_FORMAT_TEX_UV2) { + new_format |= ARRAY_FORMAT_TEX_UV2; + } + if (old_format & OLD_ARRAY_FORMAT_BONES) { + new_format |= ARRAY_FORMAT_BONES; + } + if (old_format & OLD_ARRAY_FORMAT_WEIGHTS) { + new_format |= ARRAY_FORMAT_WEIGHTS; + } + if (old_format & OLD_ARRAY_FORMAT_INDEX) { + new_format |= ARRAY_FORMAT_INDEX; + } + if (old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) { + new_format |= OLD_ARRAY_FLAG_USE_2D_VERTICES; + } + + Vector<uint8_t> vertex_array; + Vector<uint8_t> attribute_array; + Vector<uint8_t> skin_array; + + _fix_array_compatibility(array_data, old_format, new_format, vertex_count, vertex_array, attribute_array, skin_array); int index_count = 0; if (d.has("index_count")) { index_count = d["index_count"]; } - Vector<Vector<uint8_t>> blend_shapes; + Vector<uint8_t> blend_shapes; if (d.has("blend_shape_data")) { Array blend_shape_data = d["blend_shape_data"]; for (int i = 0; i < blend_shape_data.size(); i++) { + Vector<uint8_t> blend_vertex_array; + Vector<uint8_t> blend_attribute_array; + Vector<uint8_t> blend_skin_array; + Vector<uint8_t> shape = blend_shape_data[i]; - shape = _fix_array_compatibility(shape, format, vertex_count); + _fix_array_compatibility(shape, old_format, new_format, vertex_count, blend_vertex_array, blend_attribute_array, blend_skin_array); - blend_shapes.push_back(shape); + blend_shapes.append_array(blend_vertex_array); } } //clear unused flags - print_line("format pre: " + itos(format)); - format &= ~uint32_t((1 << (ARRAY_VERTEX + ARRAY_COMPRESS_BASE)) | (ARRAY_COMPRESS_INDEX << 2) | (ARRAY_COMPRESS_TEX_UV2 << 2)); - print_line("format post: " + itos(format)); + print_line("format pre: " + itos(old_format)); + + print_line("format post: " + itos(new_format)); ERR_FAIL_COND_V(!d.has("aabb"), false); AABB aabb = d["aabb"]; @@ -848,8 +1042,8 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } } - add_surface(format, PrimitiveType(primitive), array_data, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb); -#endif + add_surface(new_format, PrimitiveType(primitive), vertex_array, attribute_array, skin_array, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb); + } else { ERR_FAIL_V(false); } @@ -1120,9 +1314,9 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < surfaces.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } else { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } } } @@ -1335,9 +1529,21 @@ String ArrayMesh::surface_get_name(int p_idx) const { return surfaces[p_idx].name; } -void ArrayMesh::surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +void ArrayMesh::surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, p_surface, p_offset, p_data); + emit_changed(); +} + +void ArrayMesh::surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, p_surface, p_offset, p_data); + emit_changed(); +} + +void ArrayMesh::surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { ERR_FAIL_INDEX(p_surface, surfaces.size()); - RS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data); + RS::get_singleton()->mesh_surface_update_skin_region(mesh, p_surface, p_offset, p_data); emit_changed(); } @@ -1537,7 +1743,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, flo for (int i = 0; i < lightmap_surfaces.size(); i++) { Ref<SurfaceTool> st; - st.instance(); + st.instantiate(); st->begin(Mesh::PRIMITIVE_TRIANGLES); st->set_material(lightmap_surfaces[i].material); surfaces_tools.push_back(st); //stay there @@ -1635,7 +1841,9 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); - ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region); + ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region); + ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region); + ClassDB::bind_method(D_METHOD("surface_update_skin_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_skin_region); ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len); ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len); ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 2dfb46782b..02cab9a5e1 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -233,7 +233,9 @@ public: void set_blend_shape_mode(BlendShapeMode p_mode); BlendShapeMode get_blend_shape_mode() const; - void surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); int get_surface_count() const override; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 0a25bb2ed1..d2be2bdba1 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -501,14 +501,14 @@ void NavigationMesh::_bind_methods() { void NavigationMesh::_validate_property(PropertyInfo &property) const { if (property.name == "geometry/collision_mask") { if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } if (property.name == "geometry/source_group_name") { if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index ab8a4b7934..9bb2a4ddb8 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -41,11 +41,11 @@ #define PACKED_SCENE_VERSION 2 -bool SceneState::can_instance() const { +bool SceneState::can_instantiate() const { return nodes.size() > 0; } -Node *SceneState::instance(GenEditState p_edit_state) const { +Node *SceneState::instantiate(GenEditState p_edit_state) const { // nodes where instancing failed (because something is missing) List<Node *> stray_instances; @@ -109,7 +109,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { //scene inheritance on root node Ref<PackedScene> sdata = props[base_scene_idx]; ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state ERR_FAIL_COND_V(!node, nullptr); if (p_edit_state != GEN_EDIT_STATE_DISABLED) { node->set_scene_inherited_state(sdata->get_state()); @@ -122,7 +122,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (disable_placeholders) { Ref<PackedScene> sdata = ResourceLoader::load(path, "PackedScene"); ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); ERR_FAIL_COND_V(!node, nullptr); } else { InstancePlaceholder *ip = memnew(InstancePlaceholder); @@ -133,7 +133,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } else { Ref<PackedScene> sdata = props[n.instance & FLAG_MASK]; ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); ERR_FAIL_COND_V(!node, nullptr); } @@ -152,7 +152,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (ClassDB::is_class_enabled(snames[n.type])) { //node belongs to this scene and must be created - obj = ClassDB::instance(snames[n.type]); + obj = ClassDB::instantiate(snames[n.type]); } if (!Object::cast_to<Node>(obj)) { @@ -180,7 +180,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } if (node) { - // may not have found the node (part of instanced scene and removed) + // may not have found the node (part of instantiated scene and removed) // if found all is good, otherwise ignore //properties @@ -266,7 +266,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { parent->move_child(node, n.index); } } else { - //it may be possible that an instanced scene has changed + //it may be possible that an instantiated scene has changed //and the node has nowhere to go anymore stray_instances.push_back(node); //can't be added, go to stray list } @@ -368,7 +368,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { // this function handles all the work related to properly packing scenes, be it - // instanced or inherited. + // instantiated or inherited. // given the complexity of this process, an attempt will be made to properly // document it. if you fail to understand something, please ask! @@ -377,7 +377,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } - // save the child instanced scenes that are chosen as editable, so they can be restored + // save the child instantiated scenes that are chosen as editable, so they can be restored // upon load back if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) { editable_instances.push_back(p_owner->get_path_to(p_node)); @@ -386,7 +386,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map NodeData nd; nd.name = _nm_get_string(p_node->get_name(), name_map); - nd.instance = -1; //not instanced by default + nd.instance = -1; //not instantiated by default //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { @@ -396,18 +396,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map //This (hopefully) happens if the node is a scene root, so its index is irrelevant. nd.index = -1; } else { - //part of an inherited scene, or parent is from an instanced scene + //part of an inherited scene, or parent is from an instantiated scene nd.index = p_node->get_index(); } - // if this node is part of an instanced scene or sub-instanced scene + // if this node is part of an instantiated scene or sub-instantiated scene // we need to get the corresponding instance states. // with the instance states, we can query for identical properties/groups // and only save what has changed List<PackState> pack_state_stack; - bool instanced_by_owner = true; + bool instantiated_by_owner = true; { Node *n = p_node; @@ -423,11 +423,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map ps.node = node; ps.state = state; pack_state_stack.push_back(ps); - instanced_by_owner = false; + instantiated_by_owner = false; } } - if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instanced_by_owner) { + if (p_node->get_filename() != 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_filename(), variant_map); @@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map StringName type = p_node->get_class(); Ref<Script> script = p_node->get_script(); - if (script.is_valid()) { + 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(); } @@ -500,8 +502,8 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // } if (pack_state_stack.size()) { - // we are on part of an instanced subscene - // or part of instanced scene. + // 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 @@ -571,7 +573,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } /* if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name)) - continue; //group was instanced, don't add here + continue; //group was instantiated, don't add here */ bool skip = false; @@ -594,7 +596,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // save the right owner // for the saved scene root this is -1 // for nodes of the saved scene this is 0 - // for nodes of instanced scenes this is >0 + // for nodes of instantiated scenes this is >0 if (p_node == p_owner) { //saved scene root @@ -612,20 +614,20 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map //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 { - // this node is part of an instanced process, so do not save the type. - // instead, save that it was instanced + // this node is part of an instantiated process, so do not save the type. + // instead, save that it was instantiated nd.type = TYPE_INSTANCED; } // determine whether to save this node or not - // if this node is part of an instanced sub-scene, we can skip storing it if basically + // if this node is part of an instantiated sub-scene, we can skip storing it if basically // no properties changed and no groups were added to it. // below condition is true for all nodes of the scene being saved, and ones in subscenes // that hold changes 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 && instanced_by_owner); //part of scene and not instanced + save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated int idx = nodes.size(); int parent_node = NO_PARENT_SAVED; @@ -893,6 +895,13 @@ Error SceneState::pack(Node *p_scene) { node_paths.write[E->get()] = scene->get_path_to(E->key()); } + if (Engine::get_singleton()->is_editor_hint()) { + // Build node path cache + for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) { + node_path_cache[scene->get_path_to(E->key())] = E->get(); + } + } + return OK; } @@ -927,10 +936,12 @@ Ref<SceneState> SceneState::_get_base_scene_state() const { } 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 (idx >= 0) { + if (idx != -1) { int rkey = _find_base_scene_node_remap_key(idx); if (rkey == -1) { rkey = nodes.size() + base_scene_node_remap.size(); @@ -946,7 +957,7 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { 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 instanced scene, as a property may be missing + //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); if (idx != -1) { @@ -1614,16 +1625,16 @@ void PackedScene::clear() { state->clear(); } -bool PackedScene::can_instance() const { - return state->can_instance(); +bool PackedScene::can_instantiate() const { + return state->can_instantiate(); } -Node *PackedScene::instance(GenEditState p_edit_state) const { +Node *PackedScene::instantiate(GenEditState p_edit_state) const { #ifndef TOOLS_ENABLED ERR_FAIL_COND_V_MSG(p_edit_state != GEN_EDIT_STATE_DISABLED, nullptr, "Edit state is only for editors, does not work without tools compiled."); #endif - Node *s = state->instance((SceneState::GenEditState)p_edit_state); + Node *s = state->instantiate((SceneState::GenEditState)p_edit_state); if (!s) { return nullptr; } @@ -1671,8 +1682,8 @@ void PackedScene::reset_state() { } void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); - ClassDB::bind_method(D_METHOD("instance", "edit_state"), &PackedScene::instance, DEFVAL(GEN_EDIT_STATE_DISABLED)); - ClassDB::bind_method(D_METHOD("can_instance"), &PackedScene::can_instance); + ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); + ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index e85b933439..55708f7914 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -136,8 +136,8 @@ public: void clear(); - bool can_instance() const; - Node *instance(GenEditState p_edit_state) const; + bool can_instantiate() const; + Node *instantiate(GenEditState p_edit_state) const; //unbuild API @@ -213,8 +213,8 @@ public: void clear(); - bool can_instance() const; - Node *instance(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const; + bool can_instantiate() const; + Node *instantiate(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const; void recreate_state(); void replace_state(Ref<SceneState> p_by); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 60d5566f08..2bde98abe0 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -852,52 +852,54 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D tex_parameters[p_param] = p_texture; + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + switch (p_param) { case PARAM_INITIAL_LINEAR_VELOCITY: { //do none for this one } break; case PARAM_ANGULAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, tex_rid); _adjust_curve_range(p_texture, -360, 360); } break; case PARAM_ORBIT_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, tex_rid); _adjust_curve_range(p_texture, -500, 500); } break; case PARAM_LINEAR_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_RADIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_TANGENTIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_DAMPING: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, tex_rid); _adjust_curve_range(p_texture, 0, 100); } break; case PARAM_ANGLE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, tex_rid); _adjust_curve_range(p_texture, -360, 360); } break; case PARAM_SCALE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, tex_rid); _adjust_curve_range(p_texture, 0, 1); } break; case PARAM_HUE_VARIATION: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, tex_rid); _adjust_curve_range(p_texture, -1, 1); } break; case PARAM_ANIM_SPEED: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, tex_rid); _adjust_curve_range(p_texture, 0, 200); } break; case PARAM_ANIM_OFFSET: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, tex_rid); } break; case PARAM_MAX: break; // Can't happen, but silences warning @@ -923,7 +925,8 @@ Color ParticlesMaterial::get_color() const { void ParticlesMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) { color_ramp = p_texture; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, p_texture); + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid); _queue_shader_change(); notify_property_list_changed(); } @@ -965,17 +968,20 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { void ParticlesMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) { emission_point_texture = p_points; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points); + RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid); } void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) { emission_normal_texture = p_normals; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals); + RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid); } void ParticlesMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) { emission_color_texture = p_colors; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors); + RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid); _queue_shader_change(); } @@ -1041,39 +1047,39 @@ RID ParticlesMaterial::get_shader_rid() const { void ParticlesMaterial::_validate_property(PropertyInfo &property) const { if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1292,7 +1298,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_GROUP("Scale", ""); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index a745df522b..dfa45cc810 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -207,7 +207,7 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); } @@ -714,7 +714,7 @@ int BoxMesh::get_subdivide_depth() const { BoxMesh::BoxMesh() {} /** - CylinderMesh + CylinderMesh */ void CylinderMesh::_create_mesh_array(Array &p_arr) const { @@ -866,9 +866,9 @@ void CylinderMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings); ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_top_radius", "get_top_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings"); } @@ -955,7 +955,7 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { u /= (subdivide_w + 1.0); v /= (subdivide_d + 1.0); - points.push_back(Vector3(-x, 0.0, -z)); + points.push_back(Vector3(-x, 0.0, -z) + center_offset); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */ @@ -993,10 +993,13 @@ void PlaneMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width); ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth); ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth); + ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset); + ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } void PlaneMesh::set_size(const Size2 &p_size) { @@ -1026,6 +1029,15 @@ int PlaneMesh::get_subdivide_depth() const { return subdivide_d; } +void PlaneMesh::set_center_offset(const Vector3 p_offset) { + center_offset = p_offset; + _request_update(); +} + +Vector3 PlaneMesh::get_center_offset() const { + return center_offset; +} + PlaneMesh::PlaneMesh() {} /** @@ -1326,10 +1338,10 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); Vector3 quad_faces[4] = { - Vector3(-_size.x, -_size.y, 0), - Vector3(-_size.x, _size.y, 0), - Vector3(_size.x, _size.y, 0), - Vector3(_size.x, -_size.y, 0), + Vector3(-_size.x, -_size.y, 0) + center_offset, + Vector3(-_size.x, _size.y, 0) + center_offset, + Vector3(_size.x, _size.y, 0) + center_offset, + Vector3(_size.x, -_size.y, 0) + center_offset, }; static const int indices[6] = { @@ -1365,7 +1377,11 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { void QuadMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size); ClassDB::bind_method(D_METHOD("get_size"), &QuadMesh::get_size); + ClassDB::bind_method(D_METHOD("set_center_offset", "center_offset"), &QuadMesh::set_center_offset); + ClassDB::bind_method(D_METHOD("get_center_offset"), &QuadMesh::get_center_offset); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } QuadMesh::QuadMesh() { @@ -1381,6 +1397,15 @@ Size2 QuadMesh::get_size() const { return size; } +void QuadMesh::set_center_offset(Vector3 p_center_offset) { + center_offset = p_center_offset; + _request_update(); +} + +Vector3 QuadMesh::get_center_offset() const { + return center_offset; +} + /** SphereMesh */ diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index bd6f94921e..a3de34d3e3 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -211,6 +211,7 @@ private: Size2 size = Size2(2.0, 2.0); int subdivide_w = 0; int subdivide_d = 0; + Vector3 center_offset; protected: static void _bind_methods(); @@ -226,6 +227,9 @@ public: void set_subdivide_depth(const int p_divisions); int get_subdivide_depth() const; + void set_center_offset(const Vector3 p_offset); + Vector3 get_center_offset() const; + PlaneMesh(); }; @@ -274,6 +278,7 @@ class QuadMesh : public PrimitiveMesh { private: Size2 size = Size2(1.0, 1.0); + Vector3 center_offset; protected: static void _bind_methods(); @@ -284,6 +289,9 @@ public: void set_size(const Size2 &p_size); Size2 get_size() const; + + void set_center_offset(const Vector3 p_offset); + Vector3 get_center_offset() const; }; /** diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 27f0c50a79..ee61e64ed3 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -65,7 +65,7 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia if (!p_data->resource_map.has(index)) { Ref<DummyResource> dr; - dr.instance(); + dr.instantiate(); dr->set_subindex(index); p_data->resource_map[index] = dr; p_data->resource_set.insert(dr); @@ -183,7 +183,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) { Ref<PackedScene> packed_scene; - packed_scene.instance(); + packed_scene.instantiate(); while (true) { if (next_tag.name == "node") { @@ -208,7 +208,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("type")) { type = packed_scene->get_state()->add_name(next_tag.fields["type"]); } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced + type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated } if (next_tag.fields.has("instance")) { @@ -522,7 +522,7 @@ Error ResourceLoaderText::load() { } else { //create - Object *obj = ClassDB::instance(type); + Object *obj = ClassDB::instantiate(type); if (!obj) { error_text += "Can't create sub resource of type: " + type; _printerr(); @@ -604,7 +604,7 @@ Error ResourceLoaderText::load() { } if (!resource.is_valid()) { - Object *obj = ClassDB::instance(res_type); + Object *obj = ClassDB::instantiate(res_type); if (!obj) { error_text += "Can't create sub resource of type: " + res_type; _printerr(); @@ -1022,7 +1022,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) int lindex = dummy_read.external_resources.size(); Ref<DummyResource> dr; - dr.instance(); + dr.instantiate(); dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external dummy_read.external_resources[dr] = lindex; dummy_read.rev_external_resources[index] = dr; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index cbd44315b7..f19d08dbb1 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -167,7 +167,7 @@ RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_origi } Ref<Shader> shader; - shader.instance(); + shader.instantiate(); Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 9d79c22159..89b3336118 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -270,7 +270,8 @@ ProceduralSkyMaterial::~ProceduralSkyMaterial() { void PanoramaSkyMaterial::set_panorama(const Ref<Texture2D> &p_panorama) { panorama = p_panorama; - RS::get_singleton()->material_set_param(_get_material(), "source_panorama", panorama); + RID tex_rid = p_panorama.is_valid() ? p_panorama->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "source_panorama", tex_rid); } Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const { @@ -411,7 +412,8 @@ float PhysicalSkyMaterial::get_dither_strength() const { void PhysicalSkyMaterial::set_night_sky(const Ref<Texture2D> &p_night_sky) { night_sky = p_night_sky; - RS::get_singleton()->material_set_param(_get_material(), "night_sky", night_sky); + RID tex_rid = p_night_sky.is_valid() ? p_night_sky->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "night_sky", tex_rid); } Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index fee9f92ad7..9f8c35b668 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -607,7 +607,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_ if (p_existing.is_valid()) { mesh = p_existing; } else { - mesh.instance(); + mesh.instantiate(); } int varr_len = vertex_array.size(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index aa85c7116b..98997e482a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -88,7 +88,7 @@ void ImageTexture::reload_from_file() { } Ref<Image> img; - img.instance(); + img.instantiate(); if (ImageLoader::load_image(path, img) == OK) { create_from_image(img); @@ -138,7 +138,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) { } Ref<Image> img; - img.instance(); + img.instantiate(); Error err = ImageLoader::load_image(path, img); ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'."); @@ -173,7 +173,7 @@ Image::Format ImageTexture::get_format() const { return format; } -void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) { +void ImageTexture::update(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image"); ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized."); ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h, @@ -183,11 +183,7 @@ void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) { ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(), "The new image mipmaps configuration must match the texture's image mipmaps configuration"); - if (p_immediate) { - RenderingServer::get_singleton()->texture_2d_update_immediate(texture, p_image); - } else { - RenderingServer::get_singleton()->texture_2d_update(texture, p_image); - } + RenderingServer::get_singleton()->texture_2d_update(texture, p_image); notify_property_list_changed(); emit_changed(); @@ -258,7 +254,7 @@ bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { decom->decompress(); img = decom; } - alpha_cache.instance(); + alpha_cache.instantiate(); alpha_cache->create_from_image_alpha(img); } } @@ -305,7 +301,7 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image); ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format); - ClassDB::bind_method(D_METHOD("update", "image", "immediate"), &ImageTexture::update, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update); ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook); } @@ -390,7 +386,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit //print_line("mipmap read total: " + itos(mipmap_images.size())); Ref<Image> image; - image.instance(); + image.instantiate(); if (mipmap_images.size() == 1) { //only one image (which will most likely be the case anyway for this format) @@ -442,7 +438,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit } Ref<Image> image; - image.instance(); + image.instantiate(); image->create(tw, th, mipmaps - i ? true : false, format, data); @@ -553,7 +549,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_hei Error StreamTexture2D::load(const String &p_path) { int lw, lh; Ref<Image> image; - image.instance(); + image.instantiate(); bool request_3d; bool request_normal; @@ -679,7 +675,7 @@ bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const { img = decom; } - alpha_cache.instance(); + alpha_cache.instantiate(); alpha_cache->create_from_image_alpha(img); } } @@ -738,7 +734,7 @@ StreamTexture2D::~StreamTexture2D() { RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<StreamTexture2D> st; - st.instance(); + st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; @@ -1036,7 +1032,7 @@ StreamTexture3D::~StreamTexture3D() { RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<StreamTexture3D> st; - st.instance(); + st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; @@ -1411,14 +1407,26 @@ void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve); + ClassDB::bind_method(D_METHOD("set_texture_mode", "texture_mode"), &CurveTexture::set_texture_mode); + ClassDB::bind_method(D_METHOD("get_texture_mode"), &CurveTexture::get_texture_mode); + ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "RGB,Red"), "set_texture_mode", "get_texture_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + + BIND_ENUM_CONSTANT(TEXTURE_MODE_RGB); + BIND_ENUM_CONSTANT(TEXTURE_MODE_RED); } void CurveTexture::set_width(int p_width) { ERR_FAIL_COND(p_width < 32 || p_width > 4096); + + if (_width == p_width) { + return; + } + _width = p_width; _update(); } @@ -1454,7 +1462,7 @@ void CurveTexture::set_curve(Ref<Curve> p_curve) { void CurveTexture::_update() { Vector<uint8_t> data; - data.resize(_width * sizeof(float)); + data.resize(_width * sizeof(float) * (texture_mode == TEXTURE_MODE_RGB ? 3 : 1)); // The array is locked in that scope { @@ -1465,24 +1473,42 @@ void CurveTexture::_update() { Curve &curve = **_curve; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i] = curve.interpolate_baked(t); + if (texture_mode == TEXTURE_MODE_RGB) { + wd[i * 3 + 0] = curve.interpolate_baked(t); + wd[i * 3 + 1] = wd[i * 3 + 0]; + wd[i * 3 + 2] = wd[i * 3 + 0]; + } else { + wd[i] = curve.interpolate_baked(t); + } } } else { for (int i = 0; i < _width; ++i) { - wd[i] = 0; + if (texture_mode == TEXTURE_MODE_RGB) { + wd[i * 3 + 0] = 0; + wd[i * 3 + 1] = 0; + wd[i * 3 + 2] = 0; + } else { + wd[i] = 0; + } } } } - Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data)); + Ref<Image> image = memnew(Image(_width, 1, false, texture_mode == TEXTURE_MODE_RGB ? Image::FORMAT_RGBF : Image::FORMAT_RF, data)); if (_texture.is_valid()) { - RID new_texture = RS::get_singleton()->texture_2d_create(image); - RS::get_singleton()->texture_replace(_texture, new_texture); + if (_current_texture_mode != texture_mode || _current_width != _width) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(_texture, new_texture); + } else { + RS::get_singleton()->texture_2d_update(_texture, image); + } } else { _texture = RS::get_singleton()->texture_2d_create(image); } + _current_texture_mode = texture_mode; + _current_width = _width; emit_changed(); } @@ -1491,6 +1517,17 @@ Ref<Curve> CurveTexture::get_curve() const { return _curve; } +void CurveTexture::set_texture_mode(TextureMode p_mode) { + if (texture_mode == p_mode) { + return; + } + texture_mode = p_mode; + _update(); +} +CurveTexture::TextureMode CurveTexture::get_texture_mode() const { + return texture_mode; +} + RID CurveTexture::get_rid() const { if (!_texture.is_valid()) { _texture = RS::get_singleton()->texture_2d_placeholder_create(); @@ -1508,6 +1545,204 @@ CurveTexture::~CurveTexture() { ////////////////// +void Curve3Texture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &Curve3Texture::set_width); + + ClassDB::bind_method(D_METHOD("set_curve_x", "curve"), &Curve3Texture::set_curve_x); + ClassDB::bind_method(D_METHOD("get_curve_x"), &Curve3Texture::get_curve_x); + + ClassDB::bind_method(D_METHOD("set_curve_y", "curve"), &Curve3Texture::set_curve_y); + ClassDB::bind_method(D_METHOD("get_curve_y"), &Curve3Texture::get_curve_y); + + ClassDB::bind_method(D_METHOD("set_curve_z", "curve"), &Curve3Texture::set_curve_z); + ClassDB::bind_method(D_METHOD("get_curve_z"), &Curve3Texture::get_curve_z); + + ClassDB::bind_method(D_METHOD("_update"), &Curve3Texture::_update); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_x", "get_curve_x"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_y", "get_curve_y"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_z", "get_curve_z"); +} + +void Curve3Texture::set_width(int p_width) { + ERR_FAIL_COND(p_width < 32 || p_width > 4096); + + if (_width == p_width) { + return; + } + + _width = p_width; + _update(); +} + +int Curve3Texture::get_width() const { + return _width; +} + +void Curve3Texture::ensure_default_setup(float p_min, float p_max) { + if (_curve_x.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_x(curve); + } + + if (_curve_y.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_y(curve); + } + + if (_curve_z.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_z(curve); + } +} + +void Curve3Texture::set_curve_x(Ref<Curve> p_curve) { + if (_curve_x != p_curve) { + if (_curve_x.is_valid()) { + _curve_x->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update)); + } + _curve_x = p_curve; + if (_curve_x.is_valid()) { + _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void Curve3Texture::set_curve_y(Ref<Curve> p_curve) { + if (_curve_y != p_curve) { + if (_curve_y.is_valid()) { + _curve_y->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update)); + } + _curve_y = p_curve; + if (_curve_y.is_valid()) { + _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void Curve3Texture::set_curve_z(Ref<Curve> p_curve) { + if (_curve_z != p_curve) { + if (_curve_z.is_valid()) { + _curve_z->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update)); + } + _curve_z = p_curve; + if (_curve_z.is_valid()) { + _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Curve3Texture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void Curve3Texture::_update() { + Vector<uint8_t> data; + data.resize(_width * sizeof(float) * 3); + + // The array is locked in that scope + { + uint8_t *wd8 = data.ptrw(); + float *wd = (float *)wd8; + + if (_curve_x.is_valid()) { + Curve &curve_x = **_curve_x; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 0] = curve_x.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 0] = 0; + } + } + + if (_curve_y.is_valid()) { + Curve &curve_y = **_curve_y; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 1] = curve_y.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 1] = 0; + } + } + + if (_curve_z.is_valid()) { + Curve &curve_z = **_curve_z; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 2] = curve_z.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 2] = 0; + } + } + } + + Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RGBF, data)); + + if (_texture.is_valid()) { + if (_current_width != _width) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(_texture, new_texture); + } else { + RS::get_singleton()->texture_2d_update(_texture, image); + } + } else { + _texture = RS::get_singleton()->texture_2d_create(image); + } + _current_width = _width; + + emit_changed(); +} + +Ref<Curve> Curve3Texture::get_curve_x() const { + return _curve_x; +} + +Ref<Curve> Curve3Texture::get_curve_y() const { + return _curve_y; +} + +Ref<Curve> Curve3Texture::get_curve_z() const { + return _curve_z; +} + +RID Curve3Texture::get_rid() const { + if (!_texture.is_valid()) { + _texture = RS::get_singleton()->texture_2d_placeholder_create(); + } + return _texture; +} + +Curve3Texture::Curve3Texture() {} + +Curve3Texture::~Curve3Texture() { + if (_texture.is_valid()) { + RS::get_singleton()->free(_texture); + } +} + +////////////////// + GradientTexture::GradientTexture() { _queue_update(); } @@ -1527,7 +1762,7 @@ void GradientTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_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,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); } void GradientTexture::set_gradient(Ref<Gradient> p_gradient) { @@ -1595,6 +1830,7 @@ void GradientTexture::_update() { } void GradientTexture::set_width(int p_width) { + ERR_FAIL_COND(p_width <= 0); width = p_width; _queue_update(); } @@ -1876,7 +2112,7 @@ void AnimatedTexture::_validate_property(PropertyInfo &property) const { if (prop.begins_with("frame_")) { int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); if (frame >= frame_count) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } } @@ -2257,15 +2493,15 @@ RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const S Ref<StreamTextureLayered> st; if (p_path.get_extension().to_lower() == "stexarray") { Ref<StreamTexture2DArray> s; - s.instance(); + s.instantiate(); st = s; } else if (p_path.get_extension().to_lower() == "scube") { Ref<StreamCubemap> s; - s.instance(); + s.instantiate(); st = s; } else if (p_path.get_extension().to_lower() == "scubearray") { Ref<StreamCubemapArray> s; - s.instance(); + s.instantiate(); st = s; } else { if (r_error) { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 3b1815266d..73390039cb 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -107,7 +107,7 @@ public: Image::Format get_format() const; - void update(const Ref<Image> &p_image, bool p_immediate = false); + void update(const Ref<Image> &p_image); Ref<Image> get_image() const override; int get_width() const override; @@ -586,11 +586,19 @@ public: class CurveTexture : public Texture2D { GDCLASS(CurveTexture, Texture2D); RES_BASE_EXTENSION("curvetex") +public: + enum TextureMode { + TEXTURE_MODE_RGB, + TEXTURE_MODE_RED, + }; private: mutable RID _texture; Ref<Curve> _curve; int _width = 2048; + int _current_width = 0; + TextureMode texture_mode = TEXTURE_MODE_RGB; + TextureMode _current_texture_mode = TEXTURE_MODE_RGB; void _update(); @@ -601,6 +609,9 @@ public: void set_width(int p_width); int get_width() const override; + void set_texture_mode(TextureMode p_mode); + TextureMode get_texture_mode() const; + void ensure_default_setup(float p_min = 0, float p_max = 1); void set_curve(Ref<Curve> p_curve); @@ -614,18 +625,49 @@ public: CurveTexture(); ~CurveTexture(); }; -/* - enum CubeMapSide { - CUBEMAP_LEFT, - CUBEMAP_RIGHT, - CUBEMAP_BOTTOM, - CUBEMAP_TOP, - CUBEMAP_FRONT, - CUBEMAP_BACK, - }; -*/ -//VARIANT_ENUM_CAST( Texture::CubeMapSide ); +VARIANT_ENUM_CAST(CurveTexture::TextureMode) + +class Curve3Texture : public Texture2D { + GDCLASS(Curve3Texture, Texture2D); + RES_BASE_EXTENSION("curvetex") + +private: + mutable RID _texture; + Ref<Curve> _curve_x; + Ref<Curve> _curve_y; + Ref<Curve> _curve_z; + int _width = 2048; + int _current_width = 0; + + void _update(); + +protected: + static void _bind_methods(); + +public: + void set_width(int p_width); + int get_width() const override; + + void ensure_default_setup(float p_min = 0, float p_max = 1); + + void set_curve_x(Ref<Curve> p_curve); + Ref<Curve> get_curve_x() const; + + void set_curve_y(Ref<Curve> p_curve); + Ref<Curve> get_curve_y() const; + + void set_curve_z(Ref<Curve> p_curve); + Ref<Curve> get_curve_z() const; + + virtual RID get_rid() const override; + + virtual int get_height() const override { return 1; } + virtual bool has_alpha() const override { return false; } + + Curve3Texture(); + ~Curve3Texture(); +}; class GradientTexture : public Texture2D { GDCLASS(GradientTexture, Texture2D); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 0eeea29da4..deee22f05f 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,7 +30,10 @@ #include "tile_set.h" +#include "core/core_string_names.h" #include "core/math/geometry_2d.h" +#include "core/templates/local_vector.h" + #include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -38,6 +41,25 @@ /////////////////////////////// TileSet ////////////////////////////////////// +const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { + "right_side", + "right_corner", + "bottom_right_side", + "bottom_right_corner", + "bottom_side", + "bottom_corner", + "bottom_left_side", + "bottom_left_corner", + "left_side", + "left_corner", + "top_left_side", + "top_left_corner", + "top_side", + "top_corner", + "top_right_side", + "top_right_corner" +}; + // --- Plugins --- Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { return tile_set_plugins_vector; @@ -51,6 +73,8 @@ void TileSet::set_tile_shape(TileSet::TileShape p_shape) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -72,6 +96,8 @@ void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { @@ -81,20 +107,14 @@ TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { void TileSet::set_tile_size(Size2i p_size) { ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); tile_size = p_size; + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } Size2i TileSet::get_tile_size() const { return tile_size; } -void TileSet::set_tile_skew(Vector2 p_skew) { - emit_changed(); - tile_skew = p_skew; -} -Vector2 TileSet::get_tile_skew() const { - return tile_skew; -} - int TileSet::get_next_source_id() const { return next_source_id; } @@ -117,7 +137,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source p_tile_set_source->set_tile_set(this); _compute_next_source_id(); - sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); emit_changed(); @@ -127,7 +147,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source void TileSet::remove_source(int p_source_id) { ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); - sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); sources[p_source_id]->set_tile_set(nullptr); sources.erase(p_source_id); @@ -240,80 +260,6 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { - // TODO: optimize this with 2D meshes when they work again. - if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { - if (p_filled && p_texture.is_valid()) { - p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color); - } else { - p_canvas_item->draw_rect(p_region, p_color, p_filled); - } - } else { - float overlap = 0.0; - switch (get_tile_shape()) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - - Vector<Vector2> uvs; - uvs.append(Vector2(0.5, 0.0)); - uvs.append(Vector2(0.0, overlap)); - uvs.append(Vector2(0.0, 1.0 - overlap)); - uvs.append(Vector2(0.5, 1.0)); - uvs.append(Vector2(1.0, 1.0 - overlap)); - uvs.append(Vector2(1.0, overlap)); - uvs.append(Vector2(0.5, 0.0)); - if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); - } - } - - Vector<Vector2> points; - for (int i = 0; i < uvs.size(); i++) { - points.append(p_region.position + uvs[i] * p_region.size); - } - - if (p_filled) { - // This does hurt performances a lot. We should use a mesh if possible instead. - p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture); - - // Should improve performances, but does not work as draw_primitive does not work with textures :/ : - /*for (int i = 0; i < 6; i += 3) { - Vector<Vector2> quad; - quad.append(points[i]); - quad.append(points[(i + 1) % points.size()]); - quad.append(points[(i + 2) % points.size()]); - quad.append(points[(i + 3) % points.size()]); - - Vector<Vector2> uv_quad; - uv_quad.append(uvs[i]); - uv_quad.append(uvs[(i + 1) % uvs.size()]); - uv_quad.append(uvs[(i + 2) % uvs.size()]); - uv_quad.append(uvs[(i + 3) % uvs.size()]); - - p_control->draw_primitive(quad, Vector<Color>(), uv_quad, p_texture); - }*/ - - } else { - // This does hurt performances a lot. We should use a mesh if possible instead. - // tile_shape_grid->draw_polyline(points, p_color); - for (int i = 0; i < points.size() - 1; i++) { - p_canvas_item->draw_line(points[i], points[i + 1], p_color); - } - } - } -} - // Physics void TileSet::set_physics_layers_count(int p_physics_layers_count) { ERR_FAIL_COND(p_physics_layers_count < 0); @@ -459,14 +405,9 @@ Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const { return terrain_sets[p_terrain_set].terrains[p_terrain_index].color; } -bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { - if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { - return false; - } - - TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); +bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || @@ -474,7 +415,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || @@ -483,7 +424,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || @@ -491,7 +432,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || @@ -501,7 +442,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } else { if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -511,7 +452,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -522,7 +463,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -532,7 +473,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -547,6 +488,15 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return false; } +bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { + if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { + return false; + } + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit); +} + // Navigation void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { ERR_FAIL_COND(p_navigation_layers_count < 0); @@ -657,9 +607,926 @@ Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { return custom_data_layers[p_layer_id].type; } +Vector<Vector2> TileSet::get_tile_shape_polygon() { + Vector<Vector2> points; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + points.append(Vector2(0.0, 0.0)); + points.append(Vector2(1.0, 0.0)); + points.append(Vector2(1.0, 1.0)); + points.append(Vector2(0.0, 1.0)); + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + + points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, overlap)); + points.append(Vector2(0.0, 1.0 - overlap)); + points.append(Vector2(0.5, 1.0)); + points.append(Vector2(1.0, 1.0 - overlap)); + points.append(Vector2(1.0, overlap)); + points.append(Vector2(0.5, 0.0)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < points.size(); i++) { + points.write[i] = Vector2(points[i].y, points[i].x); + } + } + } + for (int i = 0; i < points.size(); i++) { + points.write[i] = points[i] * tile_size - tile_size / 2; + } + return points; +} + +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { + if (tile_meshes_dirty) { + Vector<Vector2> uvs = get_tile_shape_polygon(); + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + } + + Vector<Color> colors; + colors.resize(uvs.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + + // Filled mesh. + tile_filled_mesh->clear_surfaces(); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + // Lines mesh. + tile_lines_mesh->clear_surfaces(); + a.clear(); + a.resize(Mesh::ARRAY_MAX); + // Add the first point again when drawing lines. + uvs.push_back(uvs[0]); + colors.push_back(colors[0]); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + tile_meshes_dirty = false; + } + + Transform2D xform; + xform.scale(p_region.size); + xform.set_origin(p_region.get_position()); + if (p_filled) { + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + } else { + p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color); + } +} + +Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { + ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>()); + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_square_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_square_side_terrain_bit_polygon(tile_size, p_bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } + } +} + +#define TERRAIN_ALPHA 0.6 + +void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data) { + ERR_FAIL_COND(!p_tile_data); + + if (terrain_bits_meshes_dirty) { + // Recompute the meshes. + terrain_bits_meshes.clear(); + + for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) { + TerrainMode terrain_mode = TerrainMode(terrain_mode_index); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + + if (is_valid_peering_bit_for_mode(terrain_mode, bit)) { + Vector<Vector2> polygon; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_square_side_terrain_bit_polygon(tile_size, bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } + } + + Ref<ArrayMesh> mesh; + mesh.instantiate(); + Vector<Vector2> uvs; + uvs.resize(polygon.size()); + Vector<Color> colors; + colors.resize(polygon.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = polygon; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + terrain_bits_meshes[terrain_mode][bit] = mesh; + } + } + } + terrain_bits_meshes_dirty = false; + } + + int terrain_set = p_tile_data->get_terrain_set(); + if (terrain_set < 0) { + return; + } + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + 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_id = p_tile_data->get_peering_bit_terrain(bit); + if (terrain_id >= 0) { + Color color = get_terrain_color(terrain_set, terrain_id); + color.a = TERRAIN_ALPHA; + p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color); + } + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { + // Counts the number of matching terrain tiles and find the best matching icon. + struct Count { + int count = 0; + float probability = 0.0; + Ref<Texture2D> texture; + Rect2i region; + }; + Vector<Vector<Ref<Texture2D>>> output; + LocalVector<LocalVector<Count>> counts; + output.resize(get_terrain_sets_count()); + counts.resize(get_terrain_sets_count()); + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + output.write[terrain_set].resize(get_terrains_count(terrain_set)); + counts[terrain_set].resize(get_terrains_count(terrain_set)); + } + + for (int source_index = 0; source_index < get_source_count(); source_index++) { + int source_id = get_source_id(source_index); + Ref<TileSetSource> source = get_source(source_id); + + 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); + + 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) { + ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>()); + + LocalVector<int> bit_counts; + bit_counts.resize(get_terrains_count(terrain_set)); + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + bit_counts[terrain] = 0; + } + for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) { + TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit); + if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { + int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); + if (terrain >= 0) { + bit_counts[terrain] += 1; + } + } + } + + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + if ((bit_counts[terrain] > counts[terrain_set][terrain].count) || (bit_counts[terrain] == counts[terrain_set][terrain].count && tile_data->get_probability() > counts[terrain_set][terrain].probability)) { + counts[terrain_set][terrain].count = bit_counts[terrain]; + counts[terrain_set][terrain].probability = tile_data->get_probability(); + counts[terrain_set][terrain].texture = atlas_source->get_texture(); + counts[terrain_set][terrain].region = atlas_source->get_tile_texture_region(tile_id); + } + } + } + } + } + } + } + + // Generate the icons. + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + Ref<Image> image; + image.instantiate(); + if (counts[terrain_set][terrain].count > 0) { + // Get the best tile. + Ref<Texture2D> texture = counts[terrain_set][terrain].texture; + Rect2 region = counts[terrain_set][terrain].region; + image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->blit_rect(texture->get_image(), region, Point2()); + image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); + } else { + image->create(1, 1, false, Image::FORMAT_RGBA8); + image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); + } + Ref<ImageTexture> icon; + icon.instantiate(); + icon->create_from_image(image); + icon->set_size_override(p_size); + + output.write[terrain_set].write[terrain] = icon; + } + } + return output; +} + void TileSet::_source_changed() { emit_changed(); - notify_property_list_changed(); +} + +Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Rect2 bit_rect; + bit_rect.size = Vector2(p_size) / 3; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit_rect.position = Vector2(1, -1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit_rect.position = Vector2(1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit_rect.position = Vector2(-1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit_rect.position = Vector2(-3, 1); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit_rect.position = Vector2(-3, -1); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit_rect.position = Vector2(-3, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit_rect.position = Vector2(-1, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit_rect.position = Vector2(1, -3); + break; + default: + break; + } + bit_rect.position *= Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + polygon.push_back(bit_rect.position); + polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y)); + polygon.push_back(bit_rect.get_end()); + polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y)); + return polygon; +} + +Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(1, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(1, 2) * unit); + polygon.push_back(Vector2(2, 1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1, 2) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1, -2) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(1, -2) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, 0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; } void TileSet::reset_state() { @@ -747,12 +1614,18 @@ void TileSet::compatibility_conversion() { for (int k = 0; k < ctd->shapes.size(); k++) { CompatibilityShapeData csd = ctd->shapes[k]; if (csd.autotile_coords == coords) { - tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1); - int index = tile_data->get_collision_shapes_count(0) - 1; - tile_data->set_collision_shape_one_way(0, index, csd.one_way); - tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin); - tile_data->set_collision_shape_shape(0, index, csd.shape); - // Ignores transform for now. + Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor + if (convex_shape.is_valid()) { + Vector<Vector2> polygon = convex_shape->get_points(); + for (int point_index = 0; point_index < polygon.size(); point_index++) { + polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + } + tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); + int index = tile_data->get_collision_polygons_count(0) - 1; + tile_data->set_collision_polygon_one_way(0, index, csd.one_way); + tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_polygon_points(0, index, polygon); + } } } @@ -1086,7 +1959,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { return true; } } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { - // Create source only if it does not exist. + // Create source only if it does not exists. int source_id = components[1].to_int(); if (!has_source(source_id)) { @@ -1287,14 +2160,11 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); - ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew); - ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew"); // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); @@ -1375,22 +2245,22 @@ void TileSet::_bind_methods() { BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL); BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_CORNER); BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS); @@ -1398,10 +2268,13 @@ void TileSet::_bind_methods() { } TileSet::TileSet() { - // Instanciatie and list all plugins. + // Instantiate the tile meshes. + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); + + // Instanciate and list all plugins. tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasTerrain)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); } @@ -2237,16 +3110,14 @@ void TileSetScenesCollectionSource::_bind_methods() { void TileData::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; - if (tile_set) { - occluders.resize(tile_set->get_occlusion_layers_count()); - physics.resize(tile_set->get_physics_layers_count()); - navigation.resize(tile_set->get_navigation_layers_count()); - custom_data.resize(tile_set->get_custom_data_layers_count()); - } - notify_property_list_changed(); + notify_tile_data_properties_should_change(); } void TileData::notify_tile_data_properties_should_change() { + if (!tile_set) { + return; + } + occluders.resize(tile_set->get_occlusion_layers_count()); physics.resize(tile_set->get_physics_layers_count()); for (int bit_index = 0; bit_index < 16; bit_index++) { @@ -2373,76 +3244,112 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_shapes_count(int p_layer_id) const { +int TileData::get_collision_polygons_count(int p_layer_id) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].shapes.size(); + return physics[p_layer_id].polygons.size(); } -void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) { +void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_COND(p_shapes_count < 0); - physics.write[p_layer_id].shapes.resize(p_shapes_count); + ERR_FAIL_COND(p_polygons_count < 0); + physics.write[p_layer_id].polygons.resize(p_polygons_count); notify_property_list_changed(); emit_signal("changed"); } -void TileData::add_collision_shape(int p_layer_id) { +void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData()); + physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); emit_signal("changed"); } -void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) { +void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.remove(p_shape_index); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.remove(p_polygon_index); emit_signal("changed"); } -void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape) { +void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points."); + + if (p_polygon.is_empty()) { + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear(); + } else { + // Decompose into convex shapes. + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon); + ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes."); + + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size()); + for (int i = 0; i < decomp.size(); i++) { + Ref<ConvexPolygonShape2D> shape; + shape.instantiate(); + shape->set_points(decomp[i]); + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape; + } + } + physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon; emit_signal("changed"); } -Ref<Shape2D> TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<Shape2D>()); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref<Shape2D>()); - return physics[p_layer_id].shapes[p_shape_index].shape; +Vector<Vector2> TileData::get_collision_polygon_points(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector<Vector2>()); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Vector<Vector2>()); + return physics[p_layer_id].polygons[p_polygon_index].polygon; } -void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) { +void TileData::set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way = p_one_way; emit_signal("changed"); } -bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const { +bool TileData::is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false); - return physics[p_layer_id].shapes[p_shape_index].one_way; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), false); + return physics[p_layer_id].polygons[p_polygon_index].one_way; } -void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) { +void TileData::set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way_margin = p_one_way_margin; emit_signal("changed"); } -float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const { +float TileData::get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0); - return physics[p_layer_id].shapes[p_shape_index].one_way_margin; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0.0); + return physics[p_layer_id].polygons[p_polygon_index].one_way_margin; +} + +int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_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(), 0); + return physics[p_layer_id].polygons[p_polygon_index].shapes.size(); +} + +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]; } // Terrain void TileData::set_terrain_set(int p_terrain_set) { ERR_FAIL_COND(p_terrain_set < -1); + if (p_terrain_set == terrain_set) { + return; + } if (tile_set) { ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; + } } terrain_set = p_terrain_set; notify_property_list_changed(); @@ -2454,7 +3361,7 @@ int TileData::get_terrain_set() const { } void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { - ERR_FAIL_INDEX(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX); + ERR_FAIL_COND(terrain_set < 0); ERR_FAIL_COND(p_terrain_index < -1); if (tile_set) { ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); @@ -2465,7 +3372,7 @@ void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int } int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { - ERR_FAIL_INDEX_V(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX, -1); + ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1); return terrain_peering_bits[p_peering_bit]; } @@ -2489,7 +3396,7 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const { // Misc void TileData::set_probability(float p_probability) { - ERR_FAIL_COND(p_probability <= 0.0); + ERR_FAIL_COND(p_probability < 0.0); probability = p_probability; emit_signal("changed"); } @@ -2550,7 +3457,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "shapes_count") { + if (components.size() == 2 && components[1] == "polygons_count") { if (p_value.get_type() != Variant::INT) { return false; } @@ -2562,13 +3469,13 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_shapes_count(layer_index, p_value); + set_collision_polygons_count(layer_index, p_value); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); - if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") { + if (components[2] == "points" || components[2] == "one_way" || components[2] == "one_way_margin") { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -2577,19 +3484,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } - if (shape_index >= physics[layer_index].shapes.size()) { - physics.write[layer_index].shapes.resize(shape_index + 1); + if (polygon_index >= physics[layer_index].polygons.size()) { + physics.write[layer_index].polygons.resize(polygon_index + 1); } } - if (components[2] == "shape") { - Ref<Shape2D> shape = p_value; - set_collision_shape_shape(layer_index, shape_index, shape); + if (components[2] == "points") { + Vector<Vector2> polygon = p_value; + set_collision_polygon_points(layer_index, polygon_index, polygon); return true; } else if (components[2] == "one_way") { - set_collision_shape_one_way(layer_index, shape_index, p_value); + set_collision_polygon_one_way(layer_index, polygon_index, p_value); return true; } else if (components[2] == "one_way_margin") { - set_collision_shape_one_way_margin(layer_index, shape_index, p_value); + set_collision_polygon_one_way_margin(layer_index, polygon_index, p_value); return true; } } @@ -2615,42 +3522,14 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value); - } else if (components[1] == "right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value); - } else if (components[1] == "bottom_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value); - } else if (components[1] == "bottom_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value); - } else if (components[1] == "bottom_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value); - } else if (components[1] == "bottom_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value); - } else if (components[1] == "left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value); - } else if (components[1] == "left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value); - } else if (components[1] == "top_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value); - } else if (components[1] == "top_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value); - } else if (components[1] == "top_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value); - } else if (components[1] == "top_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value); - } else if (components[1] == "top_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value); - } else if (components[1] == "top_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value); - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + set_peering_bit_terrain(bit, p_value); + return true; + } } - return true; + return false; } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { // Custom data layers. int layer_index = components[0].trim_prefix("custom_data_").to_int(); @@ -2693,64 +3572,35 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "shapes_count") { - r_ret = get_collision_shapes_count(layer_index); + if (components.size() == 2 && components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); - if (shape_index >= physics[layer_index].shapes.size()) { + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (polygon_index >= physics[layer_index].polygons.size()) { return false; } - if (components[2] == "shape") { - r_ret = get_collision_shape_shape(layer_index, shape_index); + if (components[2] == "points") { + r_ret = get_collision_polygon_points(layer_index, polygon_index); return true; } else if (components[2] == "one_way") { - r_ret = is_collision_shape_one_way(layer_index, shape_index); + r_ret = is_collision_polygon_one_way(layer_index, polygon_index); return true; } else if (components[2] == "one_way_margin") { - r_ret = get_collision_shape_one_way_margin(layer_index, shape_index); + r_ret = get_collision_polygon_one_way_margin(layer_index, polygon_index); return true; } } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE]; - } else if (components[1] == "right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER]; - } else if (components[1] == "bottom_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE]; - } else if (components[1] == "bottom_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER]; - } else if (components[1] == "bottom_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE]; - } else if (components[1] == "bottom_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER]; - } else if (components[1] == "bottom_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE]; - } else if (components[1] == "bottom_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER]; - } else if (components[1] == "left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE]; - } else if (components[1] == "left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER]; - } else if (components[1] == "top_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE]; - } else if (components[1] == "top_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER]; - } else if (components[1] == "top_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE]; - } else if (components[1] == "top_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER]; - } else if (components[1] == "top_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE]; - } else if (components[1] == "top_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER]; - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + r_ret = terrain_peering_bits[i]; + return true; + } } - return true; + return false; } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); @@ -2795,26 +3645,26 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - for (int j = 0; j < physics[i].shapes.size(); j++) { - // physics_layer_%d/shapes_count - property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT); - if (!physics[i].shapes[j].shape.is_valid()) { + for (int j = 0; j < physics[i].polygons.size(); j++) { + // physics_layer_%d/points + property_info = PropertyInfo(Variant::ARRAY, vformat("physics_layer_%d/polygon_%d/points", i, j), PROPERTY_HINT_ARRAY_TYPE, "Vector2", PROPERTY_USAGE_DEFAULT); + if (physics[i].polygons[j].polygon.is_empty()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way - property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j)); - if (physics[i].shapes[j].one_way == false) { + // physics_layer_%d/polygon_%d/one_way + property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/polygon_%d/one_way", i, j)); + if (physics[i].polygons[j].one_way == false) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way_margin - property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j)); - if (physics[i].shapes[j].one_way_margin == 1.0) { + // physics_layer_%d/polygon_%d/one_way_margin + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/polygon_%d/one_way_margin", i, j)); + if (physics[i].polygons[j].one_way_margin == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -2824,117 +3674,15 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Terrain data if (terrain_set >= 0) { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (is_valid_peering_bit_terrain(bit)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); + if (get_peering_bit_terrain(bit) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); } - p_list->push_back(property_info); } } @@ -2986,16 +3734,16 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count); - ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count); - ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape); - ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape); - ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); + ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); + ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); + ClassDB::bind_method(D_METHOD("get_collision_polygon_points", "layer_id", "polygon_index"), &TileData::get_collision_polygon_points); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way", "layer_id", "polygon_index", "one_way"), &TileData::set_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("is_collision_polygon_one_way", "layer_id", "polygon_index"), &TileData::is_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way_margin", "layer_id", "polygon_index", "one_way_margin"), &TileData::set_collision_polygon_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygon_one_way_margin", "layer_id", "polygon_index"), &TileData::get_collision_polygon_one_way_margin); // Terrain ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); @@ -3034,805 +3782,6 @@ void TileData::_bind_methods() { ADD_SIGNAL(MethodInfo("changed")); } - -/////////////////////////////// TileSetPluginAtlasTerrain ////////////////////////////////////// - -// --- PLUGINS --- -void TileSetPluginAtlasTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - Rect2 bit_rect; - bit_rect.size = Vector2(p_size) / 3; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit_rect.position = Vector2(1, -1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit_rect.position = Vector2(1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit_rect.position = Vector2(-1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit_rect.position = Vector2(-3, 1); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit_rect.position = Vector2(-3, -1); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit_rect.position = Vector2(-3, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit_rect.position = Vector2(-1, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit_rect.position = Vector2(1, -3); - break; - default: - break; - } - bit_rect.position *= Vector2(p_size) / 6.0; - p_canvas_item->draw_rect(bit_rect, p_color); -} - -void TileSetPluginAtlasTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(1, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(1, 2) * unit); - polygon.push_back(Vector2(2, 1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1, 2) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1, -2) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(1, -2) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, 0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -#define TERRAIN_ALPHA 0.8 - -#define DRAW_TERRAIN_BIT(f, bit) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit)); \ - } \ - } - -#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \ - } \ - } - -void TileSetPluginAtlasTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) { - ERR_FAIL_COND(!p_tile_set); - ERR_FAIL_COND(!p_tile_data); - - int terrain_set = p_tile_data->get_terrain_set(); - if (terrain_set < 0) { - return; - } - TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set); - - TileSet::TileShape shape = p_tile_set->get_tile_shape(); - Vector2i size = p_tile_set->get_tile_size(); - - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } - } else { - TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis(); - float overlap = 0.0; - switch (p_tile_set->get_tile_shape()) { - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } - } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); -} - /////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { @@ -4218,15 +4167,17 @@ void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self for (int body_index = 0; body_index < q.bodies.size(); body_index++) { // Add the shapes again. - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index); - float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index); - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { Transform2D xform = Transform2D(); xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); @@ -4343,11 +4294,13 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - // Draw the debug shape. - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { - shape->draw(p_quadrant->debug_canvas_item, debug_collision_color); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector<Color> color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); } } } @@ -4587,7 +4540,7 @@ void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, if (scenes_collection_source) { Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); if (packed_scene.is_valid()) { - Node *scene = packed_scene->instance(); + Node *scene = packed_scene->instantiate(); p_tile_map->add_child(scene); Control *scene_as_control = Object::cast_to<Control>(scene); Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 6cf4198f30..dbf6dbabe6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -33,19 +33,18 @@ #include "core/io/resource.h" #include "core/object/object.h" +#include "core/templates/local_vector.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" #ifndef DISABLE_DEPRECATED -#include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/resources/shader.h" -#include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" #endif @@ -60,7 +59,6 @@ class TileSetPlugin; class TileSetPluginAtlasRendering; class TileSetPluginAtlasPhysics; class TileSetPluginAtlasNavigation; -class TileSetPluginAtlasTerrain; class TileSet : public Resource { GDCLASS(TileSet, Resource); @@ -138,6 +136,8 @@ public: CELL_NEIGHBOR_MAX, }; + static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[]; + enum TerrainMode { TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0, TERRAIN_MODE_MATCH_CORNERS, @@ -194,6 +194,10 @@ private: }; Vector<OcclusionLayer> occlusion_layers; + Ref<ArrayMesh> tile_lines_mesh; + Ref<ArrayMesh> tile_filled_mesh; + bool tile_meshes_dirty = true; + // Physics struct PhysicsLayer { uint32_t collision_layer = 1; @@ -213,6 +217,9 @@ private: }; Vector<TerrainSet> terrain_sets; + Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; + bool terrain_bits_meshes_dirty = true; + // Navigation struct Navigationlayer { uint32_t layers = 1; @@ -239,6 +246,19 @@ private: void _compute_next_source_id(); void _source_changed(); + // Helpers + Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + protected: static void _bind_methods(); @@ -257,8 +277,6 @@ public: TileOffsetAxis get_tile_offset_axis() const; void set_tile_size(Size2i p_size); Size2i get_tile_size() const; - void set_tile_skew(Vector2 p_skew); - Vector2 get_tile_skew() const; // -- Sources management -- int get_next_source_id() const; @@ -305,6 +323,7 @@ public: String get_terrain_name(int p_terrain_set, int p_terrain_index) const; void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); Color get_terrain_color(int p_terrain_set, int p_terrain_index) const; + bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const; bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation @@ -323,8 +342,14 @@ public: Variant::Type get_custom_data_type(int p_layer_id) const; // Helpers + Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); + Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit); + void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data); + Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size); + + // Resource management virtual void reset_state() override; TileSet(); @@ -509,13 +534,14 @@ private: // Physics struct PhysicsLayerTileData { - struct ShapeTileData { - Ref<Shape2D> shape = Ref<Shape2D>(); + struct PolygonShapeTileData { + LocalVector<Vector2> polygon; + LocalVector<Ref<ConvexPolygonShape2D>> shapes; bool one_way = false; float one_way_margin = 1.0; }; - Vector<ShapeTileData> shapes; + Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; // TODO add support for areas. @@ -570,16 +596,18 @@ public: Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; // Physics - int get_collision_shapes_count(int p_layer_id) const; - void set_collision_shapes_count(int p_layer_id, int p_shapes_count); - void add_collision_shape(int p_layer_id); - void remove_collision_shape(int p_layer_id, int p_shape_index); - void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape); - Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way); - bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin); - float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const; + int get_collision_polygons_count(int p_layer_id) const; + void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + void add_collision_polygon(int p_layer_id); + void remove_collision_polygon(int p_layer_id, int p_polygon_index); + void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon); + Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way); + bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin); + float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const; + int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const; + Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const; // Terrain void set_terrain_set(int p_terrain_id); @@ -637,26 +665,6 @@ public: 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, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); }; -class TileSetPluginAtlasTerrain : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasTerrain, TileSetPlugin); - -private: - static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - -public: - static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data); -}; - class TileSetPluginAtlasPhysics : public TileSetPlugin { GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 5759948fe6..54bc7382db 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -617,7 +617,7 @@ void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_c if (g->nodes[p_id].node->get_class_name() == p_new_class) { return; } - VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(p_new_class)); + VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(p_new_class)); vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update)); g->nodes[p_id].node = Ref<VisualShaderNode>(vsn); @@ -2020,13 +2020,13 @@ VisualShader::VisualShader() { for (int i = 0; i < TYPE_MAX; i++) { if (i > (int)TYPE_LIGHT && i < (int)TYPE_SKY) { Ref<VisualShaderNodeParticleOutput> output; - output.instance(); + output.instantiate(); output->shader_type = Type(i); output->shader_mode = shader_mode; graph[i].nodes[NODE_ID_OUTPUT].node = output; } else { Ref<VisualShaderNodeOutput> output; - output.instance(); + output.instantiate(); output->shader_type = Type(i); output->shader_mode = shader_mode; graph[i].nodes[NODE_ID_OUTPUT].node = output; @@ -2533,6 +2533,14 @@ Vector<StringName> VisualShaderNodeInput::get_editable_properties() const { return props; } +void VisualShaderNodeInput::set_shader_type(VisualShader::Type p_shader_type) { + shader_type = p_shader_type; +} + +void VisualShaderNodeInput::set_shader_mode(Shader::Mode p_shader_mode) { + shader_mode = p_shader_mode; +} + void VisualShaderNodeInput::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name); ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 53b165fe0f..454012b7ed 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -350,6 +350,10 @@ class VisualShaderNodeInput : public VisualShaderNode { String input_name = "[None]"; +public: + void set_shader_type(VisualShader::Type p_shader_type); + void set_shader_mode(Shader::Mode p_shader_mode); + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const override; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index d3b094de31..4e73b8db44 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -805,7 +805,7 @@ void VisualShaderNodeTexture::_bind_methods() { VisualShaderNodeTexture::VisualShaderNodeTexture() { } -////////////// Curve +////////////// CurveTexture String VisualShaderNodeCurveTexture::get_caption() const { return "CurveTexture"; @@ -889,6 +889,90 @@ VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() { allow_v_resize = false; } +////////////// Curve3Texture + +String VisualShaderNodeCurve3Texture::get_caption() const { + return "Curve3Texture"; +} + +int VisualShaderNodeCurve3Texture::get_input_port_count() const { + return 1; +} + +VisualShaderNodeCurve3Texture::PortType VisualShaderNodeCurve3Texture::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeCurve3Texture::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeCurve3Texture::get_output_port_count() const { + return 1; +} + +VisualShaderNodeCurve3Texture::PortType VisualShaderNodeCurve3Texture::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeCurve3Texture::get_output_port_name(int p_port) const { + return String(); +} + +void VisualShaderNodeCurve3Texture::set_texture(Ref<Curve3Texture> p_texture) { + texture = p_texture; + emit_changed(); +} + +Ref<Curve3Texture> VisualShaderNodeCurve3Texture::get_texture() const { + return texture; +} + +Vector<StringName> VisualShaderNodeCurve3Texture::get_editable_properties() const { + Vector<StringName> props; + props.push_back("texture"); + return props; +} + +String VisualShaderNodeCurve3Texture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve3d") + ";\n"; +} + +String VisualShaderNodeCurve3Texture::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 { + if (p_input_vars[0] == String()) { + return "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + } + String id = make_unique_id(p_type, p_id, "curve3d"); + String code; + code += "\t" + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).rgb;\n"; + return code; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurve3Texture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "curve3d"); + dtp.param = texture; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +void VisualShaderNodeCurve3Texture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &VisualShaderNodeCurve3Texture::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeCurve3Texture::get_texture); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Curve3Texture"), "set_texture", "get_texture"); +} + +bool VisualShaderNodeCurve3Texture::is_use_prop_slots() const { + return true; +} + +VisualShaderNodeCurve3Texture::VisualShaderNodeCurve3Texture() { + simple_decl = true; + allow_v_resize = false; +} + ////////////// Sample3D int VisualShaderNodeSample3D::get_input_port_count() const { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 5b44e9f776..3ae79723e9 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -338,6 +338,39 @@ public: /////////////////////////////////////// +class VisualShaderNodeCurve3Texture : public VisualShaderNodeResizableBase { + GDCLASS(VisualShaderNodeCurve3Texture, VisualShaderNodeResizableBase); + Ref<Curve3Texture> texture; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + 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 Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; + virtual String 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 = false) const override; + + void set_texture(Ref<Curve3Texture> p_value); + Ref<Curve3Texture> get_texture() const; + + virtual Vector<StringName> get_editable_properties() const override; + virtual bool is_use_prop_slots() const override; + + VisualShaderNodeCurve3Texture(); +}; + +/////////////////////////////////////// + class VisualShaderNodeSample3D : public VisualShaderNode { GDCLASS(VisualShaderNodeSample3D, VisualShaderNode); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 8acab79c9e..35f8a506fc 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -103,7 +103,6 @@ SceneStringNames::SceneStringNames() { _update_scroll = StaticCString::create("_update_scroll"); _update_xform = StaticCString::create("_update_xform"); - _clips_input = StaticCString::create("_clips_input"); _structured_text_parser = StaticCString::create("_structured_text_parser"); _proxgroup_add = StaticCString::create("_proxgroup_add"); @@ -144,7 +143,7 @@ SceneStringNames::SceneStringNames() { v_offset = StaticCString::create("v_offset"); transform_pos = StaticCString::create("position"); - transform_rot = StaticCString::create("rotation_degrees"); + transform_rot = StaticCString::create("rotation"); transform_scale = StaticCString::create("scale"); _update_remote = StaticCString::create("_update_remote"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 0c528a45f7..01865b0d2f 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -129,7 +129,6 @@ public: StringName _update_scroll; StringName _update_xform; - StringName _clips_input; StringName _structured_text_parser; StringName _proxgroup_add; |