summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/canvas_modulate.cpp2
-rw-r--r--scene/2d/gpu_particles_2d.cpp2
-rw-r--r--scene/2d/navigation_region_2d.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp61
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp2
-rw-r--r--scene/2d/touch_screen_button.cpp4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/camera_3d.cpp2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp2
-rw-r--r--scene/3d/light_3d.cpp2
-rw-r--r--scene/3d/lightmap_gi.cpp10
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/3d/occluder_instance_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.cpp67
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/3d/ray_cast_3d.cpp2
-rw-r--r--scene/3d/reflection_probe.h2
-rw-r--r--scene/3d/remote_transform_3d.cpp2
-rw-r--r--scene/3d/skeleton_3d.cpp4
-rw-r--r--scene/3d/soft_body_3d.cpp2
-rw-r--r--scene/3d/voxel_gi.cpp6
-rw-r--r--scene/3d/voxelizer.cpp6
-rw-r--r--scene/3d/world_environment.cpp2
-rw-r--r--scene/3d/xr_nodes.cpp6
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/animation_node_state_machine.cpp4
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/animation/tween.cpp2113
-rw-r--r--scene/animation/tween.h307
-rw-r--r--scene/debugger/scene_debugger.cpp22
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/button.cpp139
-rw-r--r--scene/gui/button.h4
-rw-r--r--scene/gui/code_edit.cpp512
-rw-r--r--scene/gui/code_edit.h34
-rw-r--r--scene/gui/color_picker.cpp8
-rw-r--r--scene/gui/control.cpp27
-rw-r--r--scene/gui/control.h3
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/gui/graph_edit.h1
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/item_list.cpp4
-rw-r--r--scene/gui/label.cpp1
-rw-r--r--scene/gui/link_button.cpp2
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/rich_text_label.cpp136
-rw-r--r--scene/gui/rich_text_label.h22
-rw-r--r--scene/gui/scroll_container.cpp8
-rw-r--r--scene/gui/scroll_container.h2
-rw-r--r--scene/gui/shortcut.cpp2
-rw-r--r--scene/gui/spin_box.cpp4
-rw-r--r--scene/gui/tab_container.cpp2
-rw-r--r--scene/gui/tabs.cpp2
-rw-r--r--scene/gui/text_edit.cpp551
-rw-r--r--scene/gui/text_edit.h39
-rw-r--r--scene/gui/texture_progress_bar.cpp140
-rw-r--r--scene/gui/texture_progress_bar.h3
-rw-r--r--scene/gui/tree.cpp343
-rw-r--r--scene/gui/tree.h29
-rw-r--r--scene/main/http_request.cpp5
-rw-r--r--scene/main/instance_placeholder.cpp2
-rw-r--r--scene/main/node.cpp45
-rw-r--r--scene/main/node.h7
-rw-r--r--scene/main/scene_tree.cpp61
-rw-r--r--scene/main/scene_tree.h5
-rw-r--r--scene/main/timer.cpp2
-rw-r--r--scene/main/viewport.cpp41
-rw-r--r--scene/main/viewport.h5
-rw-r--r--scene/register_scene_types.cpp28
-rw-r--r--scene/resources/audio_stream_sample.cpp2
-rw-r--r--scene/resources/bit_map.cpp8
-rw-r--r--scene/resources/default_theme/default_theme.cpp8
-rw-r--r--scene/resources/font.cpp14
-rw-r--r--scene/resources/line_shape_2d.h3
-rw-r--r--scene/resources/material.cpp22
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/packed_scene.cpp77
-rw-r--r--scene/resources/packed_scene.h8
-rw-r--r--scene/resources/primitive_meshes.cpp6
-rw-r--r--scene/resources/resource_format_text.cpp12
-rw-r--r--scene/resources/shader.cpp2
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/texture.cpp25
-rw-r--r--scene/resources/tile_set.cpp2321
-rw-r--r--scene/resources/tile_set.h86
-rw-r--r--scene/resources/visual_shader.cpp14
-rw-r--r--scene/resources/visual_shader.h4
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
91 files changed, 3711 insertions, 3796 deletions
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/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index d7404ff479..bf7bb12dbe 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
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/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index ce63b25ce0..2f552af3d9 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -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);
@@ -942,15 +975,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 +1000,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 +1013,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 +1028,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 +1157,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;
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index f084a247aa..b1d48a2cc9 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();
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 1eec2a3833..860e95b51e 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;
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/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 9640043031..e05f37c73c 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -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..32006d5e7f 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -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/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(&params);
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/light_3d.cpp b/scene/3d/light_3d.cpp
index d45749d36b..37881c3332 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -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() {
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index a3f681e53c..74b4269169 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);
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index c495f68890..08dec232ab 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -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/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 429e1d4b98..b0b9668fd2 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;
}
@@ -303,7 +303,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);
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 28a0c72fe3..7c9245cd3e 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -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;
@@ -978,15 +1011,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 +1036,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 +1049,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 +1064,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 +1205,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;
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 7d7adf1624..bc4556494d 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);
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.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..9592fe5849 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -465,7 +465,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));
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index e00be9204c..5558930df8 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());
@@ -467,7 +467,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/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_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 79a1dc1ac0..ad6115fa16 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -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..799c81c2ab 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -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/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] = &current_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..049de4c8c5 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -88,7 +88,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 +107,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;"
@@ -1213,9 +1213,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/control.cpp b/scene/gui/control.cpp
index 64a3f1ca77..1bfdff1134 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();
@@ -2784,7 +2768,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);
@@ -2940,5 +2923,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..87fad96571 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;
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/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/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..370fdd8b88 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;
}
@@ -4540,6 +4208,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 +4326,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 +4341,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 +4888,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 +4900,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 +5086,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 +5690,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 +5749,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 +5808,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 +5935,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..146de50275 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);
@@ -259,9 +260,6 @@ private:
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 +294,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 +417,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 +428,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 +513,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 +628,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 +643,6 @@ public:
brace_matching_enabled = p_enabled;
update();
}
- void set_auto_indent(bool p_auto_indent);
void center_viewport_to_cursor();
@@ -699,6 +688,9 @@ public:
void clear();
+ void delete_selection();
+
+ virtual void backspace();
void cut();
void copy();
void paste();
@@ -730,10 +722,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 +734,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..aac15cd9a5 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,53 @@ 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.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;
@@ -1846,78 +1893,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;
+
+ 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;
+ 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) {
@@ -2092,7 +2137,7 @@ void Tree::_range_click_timeout() {
click_handled = false;
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
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++;
@@ -3161,6 +3206,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
} break;
+ default:
+ break;
}
}
@@ -3276,7 +3323,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 +3347,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;
}
}
@@ -3445,7 +3504,7 @@ void Tree::_notification(int p_what) {
draw_ofs.y += tbh;
draw_size.y -= tbh;
- if (root) {
+ if (root && get_size().x > 0 && get_size().y > 0) {
draw_item(Point2(), draw_ofs, draw_size, root);
}
@@ -3513,7 +3572,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 +3610,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 +3744,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();
}
@@ -3748,44 +3817,82 @@ 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;
- }
+ if (columns[p_column].custom_min_width != 0) {
+ return columns[p_column].custom_min_width;
+ } else {
+ int depth = 0;
+ int min_width = 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;
+ }
+ }
+ }
- int expand_area = get_size().width;
+ // 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);
+ }
+ 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);
- }
+ if (columns[p_column].expand) {
+ int expand_area = get_size().width;
- if (v_scroll->is_visible_in_tree()) {
- expand_area -= v_scroll->get_combined_minimum_size().width;
- }
+ Ref<StyleBox> bg = cache.bg;
- int expanding_columns = 0;
- int expanding_total = 0;
+ if (bg.is_valid()) {
+ expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT);
+ }
- 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 (v_scroll->is_visible_in_tree()) {
+ expand_area -= v_scroll->get_combined_minimum_size().width;
}
- }
- if (expand_area < expanding_total) {
- return columns[p_column].min_width;
- }
+ int expanding_columns = 0;
+ int expanding_total = 0;
- ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen
+ for (int i = 0; i < columns.size(); i++) {
+ if (!columns[i].expand) {
+ expand_area -= get_column_minimum_width(i);
+ } else {
+ expanding_total += get_column_minimum_width(i);
+ expanding_columns++;
+ }
+ }
- return expand_area * columns[p_column].min_width / expanding_total;
+ if (expand_area < expanding_total) {
+ return get_column_minimum_width(p_column);
+ }
+
+ ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen
+ if (expanding_total == 0) {
+ return 0;
+ } else {
+ return expand_area * get_column_minimum_width(p_column) / expanding_total;
+ }
+ } else {
+ return get_column_minimum_width(p_column);
+ }
}
void Tree::propagate_set_columns(TreeItem *p_item) {
@@ -4047,6 +4154,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,7 +4547,7 @@ 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("get_column_width", "column"), &Tree::get_column_width);
@@ -4468,6 +4593,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 +4618,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..fd5fcd7838 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,7 +410,7 @@ private:
int drop_mode_flags = 0;
struct ColumnInfo {
- int min_width = 1;
+ int custom_min_width = 0;
bool expand = true;
String title;
Ref<TextLine> text_buf;
@@ -416,7 +418,7 @@ private:
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
ColumnInfo() {
- text_buf.instance();
+ text_buf.instantiate();
}
};
@@ -545,6 +547,8 @@ private:
void _scroll_moved(float p_value);
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();
@@ -623,11 +627,12 @@ 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);
+ int get_column_minimum_width(int p_column) const;
int get_column_width(int p_column) const;
void set_hide_root(bool p_enabled);
@@ -679,6 +684,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/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..9479b1339d 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"
@@ -1040,7 +1041,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 +1284,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 +1687,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 +1749,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 +1764,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 +1813,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 +1830,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 +1856,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 +1866,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 +1950,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 +2074,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;
}
@@ -2459,7 +2467,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 +2514,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 +2563,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));
diff --git a/scene/main/node.h b/scene/main/node.h
index e7b36f351b..f685eac86a 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");
@@ -279,7 +282,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 +311,8 @@ public:
void remove_and_skip();
int get_index() const;
+ Ref<Tween> create_tween();
+
void print_tree();
void print_tree_pretty();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 001c857d4c..4b52c4e99f 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,13 +1114,34 @@ 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;
}
+Ref<Tween> SceneTree::create_tween() {
+ Ref<Tween> tween;
+ tween.instantiate();
+ tween->set_valid(true);
+ tweens.push_back(tween);
+ return tween;
+}
+
+Array SceneTree::get_processed_tweens() {
+ Array ret;
+ ret.resize(tweens.size());
+
+ int i = 0;
+ for (List<Ref<Tween>>::Element *E = tweens.front(); E; E = E->next()) {
+ ret[i] = E->get();
+ i++;
+ }
+
+ return ret;
+}
+
void SceneTree::_network_peer_connected(int p_id) {
emit_signal("network_peer_connected", p_id);
}
@@ -1197,6 +1250,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);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 78c4c14e97..0e9ffb0f5f 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,6 +152,7 @@ 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///
@@ -171,6 +173,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 +321,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);
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index a5ceec9c8b..ea9f51aa5d 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
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 84a6dbe889..319e589036 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;
}
@@ -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");
@@ -3698,7 +3711,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..29491a1178 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -288,6 +288,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 +505,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;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2a97ae3acf..8d0283d536 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"
@@ -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>();
@@ -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/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/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/line_shape_2d.h b/scene/resources/line_shape_2d.h
index 9f0405ad29..210a1aa9e6 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/line_shape_2d.h
@@ -36,7 +36,8 @@
class LineShape2D : public Shape2D {
GDCLASS(LineShape2D, Shape2D);
- Vector2 normal = Vector2(0, 1);
+ // LineShape2D is often used for one-way platforms, where the normal pointing up makes sense.
+ Vector2 normal = Vector2(0, -1);
real_t distance = 0.0;
void _update_shape();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 2c5634e6ef..cb66d5724d 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -822,9 +822,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 +878,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 ;-)
@@ -2092,7 +2106,7 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b
}
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);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 5e8e77c730..cf59c6fa22 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);
}
@@ -1537,7 +1537,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
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/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index a745df522b..2b2ebb5c16 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -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");
}
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/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..acc85cf7df 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 + "'.");
@@ -258,7 +258,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);
}
}
@@ -390,7 +390,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 +442,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 +553,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 +679,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 +738,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 +1036,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;
@@ -1595,6 +1595,7 @@ void GradientTexture::_update() {
}
void GradientTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width <= 0);
width = p_width;
_queue_update();
}
@@ -2257,15 +2258,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/tile_set.cpp b/scene/resources/tile_set.cpp
index 0eeea29da4..4f854ff229 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);
@@ -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/scene_string_names.cpp b/scene/scene_string_names.cpp
index 8acab79c9e..2e0af89c1a 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");
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;