summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/audio_stream_player_2d.cpp326
-rw-r--r--scene/2d/audio_stream_player_2d.h26
-rw-r--r--scene/2d/physics_body_2d.cpp390
-rw-r--r--scene/2d/physics_body_2d.h75
-rw-r--r--scene/2d/tile_map.cpp60
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp661
-rw-r--r--scene/3d/audio_stream_player_3d.h42
-rw-r--r--scene/3d/physics_body_3d.cpp444
-rw-r--r--scene/3d/physics_body_3d.h76
-rw-r--r--scene/3d/soft_body_3d.cpp5
-rw-r--r--scene/3d/sprite_3d.cpp2
-rw-r--r--scene/3d/vehicle_body_3d.cpp33
-rw-r--r--scene/3d/vehicle_body_3d.h3
-rw-r--r--scene/audio/audio_stream_player.cpp237
-rw-r--r--scene/audio/audio_stream_player.h13
-rw-r--r--scene/gui/box_container.cpp4
-rw-r--r--scene/gui/check_box.cpp18
-rw-r--r--scene/gui/code_edit.cpp26
-rw-r--r--scene/gui/color_picker.cpp16
-rw-r--r--scene/gui/control.cpp29
-rw-r--r--scene/gui/control.h3
-rw-r--r--scene/gui/dialogs.cpp6
-rw-r--r--scene/gui/file_dialog.cpp11
-rw-r--r--scene/gui/gradient_edit.cpp2
-rw-r--r--scene/gui/graph_edit.cpp5
-rw-r--r--scene/gui/item_list.cpp2
-rw-r--r--scene/gui/label.cpp23
-rw-r--r--scene/gui/line_edit.cpp11
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup.cpp2
-rw-r--r--scene/gui/popup_menu.cpp9
-rw-r--r--scene/gui/rich_text_effect.h6
-rw-r--r--scene/gui/rich_text_label.cpp10
-rw-r--r--scene/gui/scroll_container.cpp7
-rw-r--r--scene/gui/spin_box.cpp4
-rw-r--r--scene/gui/tab_container.cpp20
-rw-r--r--scene/gui/text_edit.cpp65
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/gui/texture_progress_bar.cpp57
-rw-r--r--scene/gui/texture_progress_bar.h4
-rw-r--r--scene/gui/tree.cpp68
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/gui/video_player.cpp5
-rw-r--r--scene/main/canvas_item.cpp7
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/node.cpp202
-rw-r--r--scene/main/node.h36
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/main/viewport.cpp16
-rw-r--r--scene/main/window.cpp6
-rw-r--r--scene/register_scene_types.cpp30
-rw-r--r--scene/resources/audio_stream_sample.cpp7
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/default_theme/default_theme.cpp3
-rw-r--r--scene/resources/font.cpp1580
-rw-r--r--scene/resources/font.h286
-rw-r--r--scene/resources/packed_scene.cpp9
-rw-r--r--scene/resources/resource_format_text.cpp4
-rw-r--r--scene/resources/separation_ray_shape_2d.cpp119
-rw-r--r--scene/resources/separation_ray_shape_2d.h61
-rw-r--r--scene/resources/separation_ray_shape_3d.cpp91
-rw-r--r--scene/resources/separation_ray_shape_3d.h56
-rw-r--r--scene/resources/text_line.cpp8
-rw-r--r--scene/resources/text_paragraph.cpp8
-rw-r--r--scene/resources/tile_set.cpp689
-rw-r--r--scene/resources/tile_set.h88
68 files changed, 3696 insertions, 2435 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 8a4d42fd1f..ea491e8b0e 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -33,125 +33,17 @@
#include "scene/2d/area_2d.h"
#include "scene/main/window.h"
-void AudioStreamPlayer2D::_mix_audio() {
- if (!stream_playback.is_valid() || !active.is_set() ||
- (stream_paused && !stream_paused_fade_out)) {
- return;
- }
-
- if (setseek.get() >= 0.0) {
- stream_playback->start(setseek.get());
- setseek.set(-1.0); //reset seek
- }
-
- //get data
- AudioFrame *buffer = mix_buffer.ptrw();
- int buffer_size = mix_buffer.size();
-
- if (stream_paused_fade_out) {
- // Short fadeout ramp
- buffer_size = MIN(buffer_size, 128);
- }
-
- stream_playback->mix(buffer, pitch_scale, buffer_size);
-
- //write all outputs
- int oc = output_count.get();
- for (int i = 0; i < oc; i++) {
- Output current = outputs[i];
-
- //see if current output exists, to keep volume ramp
- bool found = false;
- for (int j = i; j < prev_output_count; j++) {
- if (prev_outputs[j].viewport == current.viewport) {
- if (j != i) {
- SWAP(prev_outputs[j], prev_outputs[i]);
- }
- found = true;
- break;
- }
- }
-
- if (!found) {
- //create new if was not used before
- if (prev_output_count < MAX_OUTPUTS) {
- prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport
- prev_output_count++;
- }
- prev_outputs[i] = current;
- }
-
- //mix!
- AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
- AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
- AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = vol_prev;
-
- int cc = AudioServer::get_singleton()->get_channel_count();
-
- if (cc == 1) {
- if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, 0)) {
- continue; //may have been removed
- }
-
- AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 0);
-
- for (int j = 0; j < buffer_size; j++) {
- target[j] += buffer[j] * vol;
- vol += vol_inc;
- }
-
- } else {
- AudioFrame *targets[4];
- bool valid = true;
-
- for (int k = 0; k < cc; k++) {
- if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) {
- valid = false; //may have been removed
- break;
- }
-
- targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
- }
-
- if (!valid) {
- continue;
- }
-
- for (int j = 0; j < buffer_size; j++) {
- AudioFrame frame = buffer[j] * vol;
- for (int k = 0; k < cc; k++) {
- targets[k][j] += frame;
- }
- vol += vol_inc;
- }
- }
-
- prev_outputs[i] = current;
- }
-
- prev_output_count = oc;
-
- //stream is no longer active, disable this.
- if (!stream_playback->is_playing()) {
- active.clear();
- }
-
- output_ready.clear();
- stream_paused_fade_in = false;
- stream_paused_fade_out = false;
-}
-
void AudioStreamPlayer2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- AudioServer::get_singleton()->add_callback(_mix_audios, this);
+ AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
}
}
if (p_what == NOTIFICATION_EXIT_TREE) {
- AudioServer::get_singleton()->remove_callback(_mix_audios, this);
+ stop();
+ AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
}
if (p_what == NOTIFICATION_PAUSED) {
@@ -168,109 +60,120 @@ void AudioStreamPlayer2D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
- if (!output_ready.is_set()) {
- Ref<World2D> world_2d = get_world_2d();
- ERR_FAIL_COND(world_2d.is_null());
+ if (!stream_playback.is_valid()) {
+ return;
+ }
+ if (setplay.get() >= 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ _update_panning();
+ if (setplay.get() >= 0) {
+ active.set();
+ AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
+ setplay.set(-1);
+ }
+ }
+
+ // Stop playing if no longer active.
+ if (active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
+ active.clear();
+ set_physics_process_internal(false);
+ emit_signal(SNAME("finished"));
+ }
+ }
+}
- int new_output_count = 0;
+StringName AudioStreamPlayer2D::_get_actual_bus() {
+ if (!stream_playback.is_valid()) {
+ return SNAME("Master");
+ }
- Vector2 global_pos = get_global_position();
+ Vector2 global_pos = get_global_position();
- int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
+ //check if any area is diverting sound into a bus
+ Ref<World2D> world_2d = get_world_2d();
+ ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master"));
- //check if any area is diverting sound into a bus
+ PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
+ PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS];
- PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
+ int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
- PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS];
+ for (int i = 0; i < areas; i++) {
+ Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider);
+ if (!area2d) {
+ continue;
+ }
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
+ if (!area2d->is_overriding_audio_bus()) {
+ continue;
+ }
- for (int i = 0; i < areas; i++) {
- Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider);
- if (!area2d) {
- continue;
- }
+ return area2d->get_audio_bus_name();
+ }
+ return default_bus;
+}
- if (!area2d->is_overriding_audio_bus()) {
- continue;
- }
+void AudioStreamPlayer2D::_update_panning() {
+ if (!stream_playback.is_valid()) {
+ return;
+ }
- StringName bus_name = area2d->get_audio_bus_name();
- bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name);
- break;
- }
+ last_mix_count = AudioServer::get_singleton()->get_mix_count();
- const Set<Viewport *> viewports = world_2d->get_viewports();
+ Ref<World2D> world_2d = get_world_2d();
+ ERR_FAIL_COND(world_2d.is_null());
- for (Set<Viewport *>::Element *E = viewports.front(); E; E = E->next()) {
- Viewport *vp = E->get();
- if (vp->is_audio_listener_2d()) {
- //compute matrix to convert to screen
- Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform();
- Vector2 screen_size = vp->get_visible_rect().size;
+ Vector2 global_pos = get_global_position();
- //screen in global is used for attenuation
- Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5);
+ Set<Viewport *> viewports = world_2d->get_viewports();
+ viewports.insert(get_viewport()); // TODO: This is a mediocre workaround for #50958. Remove when that bug is fixed!
- float dist = global_pos.distance_to(screen_in_global); //distance to screen center
+ volume_vector.resize(4);
+ volume_vector.write[0] = AudioFrame(0, 0);
+ volume_vector.write[1] = AudioFrame(0, 0);
+ volume_vector.write[2] = AudioFrame(0, 0);
+ volume_vector.write[3] = AudioFrame(0, 0);
- if (dist > max_distance) {
- continue; //can't hear this sound in this viewport
- }
+ for (Viewport *vp : viewports) {
+ if (!vp->is_audio_listener_2d()) {
+ continue;
+ }
+ //compute matrix to convert to screen
+ Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform();
+ Vector2 screen_size = vp->get_visible_rect().size;
- float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
- multiplier *= Math::db2linear(volume_db); //also apply player volume!
+ //screen in global is used for attenuation
+ Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5);
- //point in screen is used for panning
- Vector2 point_in_screen = to_screen.xform(global_pos);
+ float dist = global_pos.distance_to(screen_in_global); //distance to screen center
- float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0);
+ if (dist > max_distance) {
+ continue; //can't hear this sound in this viewport
+ }
- float l = 1.0 - pan;
- float r = pan;
+ float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
+ multiplier *= Math::db2linear(volume_db); //also apply player volume!
- outputs[new_output_count].vol = AudioFrame(l, r) * multiplier;
- outputs[new_output_count].bus_index = bus_index;
- outputs[new_output_count].viewport = vp; //keep pointer only for reference
- new_output_count++;
- if (new_output_count == MAX_OUTPUTS) {
- break;
- }
- }
- }
+ //point in screen is used for panning
+ Vector2 point_in_screen = to_screen.xform(global_pos);
- output_count.set(new_output_count);
- output_ready.set();
- }
+ float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0);
- //start playing if requested
- if (setplay.get() >= 0.0) {
- setseek.set(setplay.get());
- active.set();
- setplay.set(-1);
- }
+ float l = 1.0 - pan;
+ float r = pan;
- //stop playing if no longer active
- if (!active.is_set()) {
- set_physics_process_internal(false);
- emit_signal(SNAME("finished"));
- }
+ volume_vector.write[0] = AudioFrame(l, r) * multiplier;
}
+
+ AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, _get_actual_bus(), volume_vector);
}
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
- AudioServer::get_singleton()->lock();
-
- mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
-
if (stream_playback.is_valid()) {
- stream_playback.unref();
- stream.unref();
- active.clear();
- setseek.set(-1);
+ stop();
}
+ stream_playback.unref();
+ stream.unref();
if (p_stream.is_valid()) {
stream_playback = p_stream->instance_playback();
if (stream_playback.is_valid()) {
@@ -280,8 +183,6 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
}
}
- AudioServer::get_singleton()->unlock();
-
if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
}
@@ -302,6 +203,9 @@ float AudioStreamPlayer2D::get_volume_db() const {
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(p_pitch_scale <= 0.0);
pitch_scale = p_pitch_scale;
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, p_pitch_scale);
+ }
}
float AudioStreamPlayer2D::get_pitch_scale() const {
@@ -309,27 +213,26 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
}
void AudioStreamPlayer2D::play(float p_from_pos) {
- if (!is_playing()) {
- // Reset the prev_output_count if the stream is stopped
- prev_output_count = 0;
+ stop();
+ if (stream.is_valid()) {
+ stream_playback = stream->instance_playback();
}
-
if (stream_playback.is_valid()) {
setplay.set(p_from_pos);
- output_ready.clear();
set_physics_process_internal(true);
}
}
void AudioStreamPlayer2D::seek(float p_seconds) {
- if (stream_playback.is_valid()) {
- setseek.set(p_seconds);
+ if (stream_playback.is_valid() && active.is_set()) {
+ play(p_seconds);
}
}
void AudioStreamPlayer2D::stop() {
if (stream_playback.is_valid()) {
active.clear();
+ AudioServer::get_singleton()->stop_playback_stream(stream_playback);
set_physics_process_internal(false);
setplay.set(-1);
}
@@ -337,7 +240,7 @@ void AudioStreamPlayer2D::stop() {
bool AudioStreamPlayer2D::is_playing() const {
if (stream_playback.is_valid()) {
- return active.is_set() || setplay.get() >= 0;
+ return AudioServer::get_singleton()->is_playback_active(stream_playback);
}
return false;
@@ -345,30 +248,23 @@ bool AudioStreamPlayer2D::is_playing() const {
float AudioStreamPlayer2D::get_playback_position() {
if (stream_playback.is_valid()) {
- float ss = setseek.get();
- if (ss >= 0.0) {
- return ss;
- }
- return stream_playback->get_playback_position();
+ return AudioServer::get_singleton()->get_playback_position(stream_playback);
}
return 0;
}
void AudioStreamPlayer2D::set_bus(const StringName &p_bus) {
- //if audio is active, must lock this
- AudioServer::get_singleton()->lock();
- bus = p_bus;
- AudioServer::get_singleton()->unlock();
+ default_bus = p_bus; // This will be pushed to the audio server during the next physics timestep, which is fast enough.
}
StringName AudioStreamPlayer2D::get_bus() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
- if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
- return bus;
+ if (AudioServer::get_singleton()->get_bus_name(i) == default_bus) {
+ return default_bus;
}
}
- return "Master";
+ return SNAME("Master");
}
void AudioStreamPlayer2D::set_autoplay(bool p_enable) {
@@ -388,7 +284,11 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
}
bool AudioStreamPlayer2D::_is_active() const {
- return active.is_set();
+ if (stream_playback.is_valid()) {
+ // TODO make sure this doesn't change any behavior w.r.t. pauses. Is a paused stream active?
+ return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ }
+ return false;
}
void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
@@ -436,15 +336,17 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
}
void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
- if (p_pause != stream_paused) {
- stream_paused = p_pause;
- stream_paused_fade_in = !p_pause;
- stream_paused_fade_out = p_pause;
+ // TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
}
}
bool AudioStreamPlayer2D::get_stream_paused() const {
- return stream_paused;
+ if (stream_playback.is_valid()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ }
+ return false;
}
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index cf05a49b00..6428fbe017 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -51,38 +51,30 @@ private:
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
};
- Output outputs[MAX_OUTPUTS];
- SafeNumeric<int> output_count;
- SafeFlag output_ready;
-
- //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks)
- Output prev_outputs[MAX_OUTPUTS];
- int prev_output_count = 0;
-
Ref<AudioStreamPlayback> stream_playback;
Ref<AudioStream> stream;
- Vector<AudioFrame> mix_buffer;
- SafeNumeric<float> setseek{ -1.0 };
SafeFlag active;
SafeNumeric<float> setplay{ -1.0 };
+ Vector<AudioFrame> volume_vector;
+
+ uint64_t last_mix_count = -1;
+
float volume_db = 0.0;
float pitch_scale = 1.0;
bool autoplay = false;
- bool stream_paused = false;
- bool stream_paused_fade_in = false;
- bool stream_paused_fade_out = false;
- StringName bus;
-
- void _mix_audio();
- static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_mix_audio(); }
+ StringName default_bus = "Master";
void _set_playing(bool p_enable);
bool _is_active() const;
+ StringName _get_actual_bus();
+ void _update_panning();
void _bus_layout_changed();
+ static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_update_panning(); }
+
uint32_t area_mask = 1;
float max_distance = 2000.0;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index a9d4877cbb..30f012c7aa 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -70,12 +70,12 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
return Ref<KinematicCollision2D>();
}
-bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
+bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
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_margin, &r_result, p_exclude);
+ bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
@@ -165,21 +165,13 @@ void PhysicsBody2D::remove_collision_exception_with(Node *p_node) {
void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) {
constant_linear_velocity = p_vel;
- if (kinematic_motion) {
- _update_kinematic_motion();
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
}
void StaticBody2D::set_constant_angular_velocity(real_t p_vel) {
constant_angular_velocity = p_vel;
- if (kinematic_motion) {
- _update_kinematic_motion();
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
}
Vector2 StaticBody2D::get_constant_linear_velocity() const {
@@ -209,81 +201,83 @@ Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const {
return physics_material_override;
}
-void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) {
- if (p_enabled == kinematic_motion) {
- return;
- }
-
- kinematic_motion = p_enabled;
+void StaticBody2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity);
- if (kinematic_motion) {
- set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
- } else {
- set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
- }
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- return;
- }
-#endif
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
+}
- _update_kinematic_motion();
+StaticBody2D::StaticBody2D(PhysicsServer2D::BodyMode p_mode) :
+ PhysicsBody2D(p_mode) {
}
-bool StaticBody2D::is_kinematic_motion_enabled() const {
- return kinematic_motion;
+void StaticBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
+ }
}
-void StaticBody2D::set_sync_to_physics(bool p_enable) {
+void AnimatableBody2D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
sync_to_physics = p_enable;
+ _update_kinematic_motion();
+}
+
+bool AnimatableBody2D::is_sync_to_physics_enabled() const {
+ return sync_to_physics;
+}
+
+void AnimatableBody2D::_update_kinematic_motion() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
return;
}
#endif
- if (kinematic_motion) {
- _update_kinematic_motion();
+ if (sync_to_physics) {
+ PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
+ set_only_update_transform_changes(true);
+ set_notify_local_transform(true);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr);
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
}
}
-bool StaticBody2D::is_sync_to_physics_enabled() const {
- return sync_to_physics;
+void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
+ AnimatableBody2D *body = (AnimatableBody2D *)p_instance;
+ body->_body_state_changed(p_state);
}
-void StaticBody2D::_direct_state_changed(Object *p_state) {
+void AnimatableBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
if (!sync_to_physics) {
return;
}
- PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument");
-
- last_valid_transform = state->get_transform();
+ last_valid_transform = p_state->get_transform();
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
}
-TypedArray<String> StaticBody2D::get_configuration_warnings() const {
- TypedArray<String> warnings = PhysicsBody2D::get_configuration_warnings();
-
- if (sync_to_physics && !kinematic_motion) {
- warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled."));
- }
-
- return warnings;
-}
-
-void StaticBody2D::_notification(int p_what) {
+void AnimatableBody2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
@@ -293,10 +287,6 @@ void StaticBody2D::_notification(int p_what) {
// Used by sync to physics, send the new transform to the physics...
Transform2D new_transform = get_global_transform();
- real_t delta_time = get_physics_process_delta_time();
- new_transform.translate(constant_linear_velocity * delta_time);
- new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
-
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// ... but then revert changes.
@@ -304,98 +294,19 @@ void StaticBody2D::_notification(int p_what) {
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
} break;
-
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- ERR_FAIL_COND(!kinematic_motion);
-
- Transform2D new_transform = get_global_transform();
-
- real_t delta_time = get_physics_process_delta_time();
- new_transform.translate(constant_linear_velocity * delta_time);
- new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
-
- if (sync_to_physics) {
- // Propagate transform change to node.
- set_global_transform(new_transform);
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
-
- // Propagate transform change to node.
- set_block_transform_notify(true);
- set_global_transform(new_transform);
- set_block_transform_notify(false);
- }
- } break;
}
}
-void StaticBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity);
+void AnimatableBody2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &AnimatableBody2D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &AnimatableBody2D::is_sync_to_physics_enabled);
- ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody2D::set_kinematic_motion_enabled);
- ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody2D::is_kinematic_motion_enabled);
-
- ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
- ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
-
- ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody2D::set_sync_to_physics);
- ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody2D::is_sync_to_physics_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}
-StaticBody2D::StaticBody2D() :
- PhysicsBody2D(PhysicsServer2D::BODY_MODE_STATIC) {
-}
-
-void StaticBody2D::_reload_physics_characteristics() {
- if (physics_material_override.is_null()) {
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1);
- } else {
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
- }
-}
-
-void StaticBody2D::_update_kinematic_motion() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- if (kinematic_motion && sync_to_physics) {
- PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody2D::_direct_state_changed));
- set_only_update_transform_changes(true);
- set_notify_local_transform(true);
- } else {
- PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
- set_only_update_transform_changes(false);
- set_notify_local_transform(false);
- }
-
- bool needs_physics_process = false;
- if (kinematic_motion) {
- if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) {
- needs_physics_process = true;
- }
- }
-
- set_physics_process_internal(needs_physics_process);
+AnimatableBody2D::AnimatableBody2D() :
+ StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
+ _update_kinematic_motion();
}
void RigidBody2D::_body_enter_tree(ObjectID p_id) {
@@ -511,26 +422,26 @@ struct _RigidBody2DInOut {
int local_shape = 0;
};
-void RigidBody2D::_direct_state_changed(Object *p_state) {
-#ifdef DEBUG_ENABLED
- state = Object::cast_to<PhysicsDirectBodyState2D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument");
-#else
- state = (PhysicsDirectBodyState2D *)p_state; //trust it
-#endif
+void RigidBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
+ RigidBody2D *body = (RigidBody2D *)p_instance;
+ body->_body_state_changed(p_state);
+}
+void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
set_block_transform_notify(true); // don't want notify (would feedback loop)
if (mode != MODE_KINEMATIC) {
- set_global_transform(state->get_transform());
+ set_global_transform(p_state->get_transform());
}
- linear_velocity = state->get_linear_velocity();
- angular_velocity = state->get_angular_velocity();
- if (sleeping != state->is_sleeping()) {
- sleeping = state->is_sleeping();
+
+ linear_velocity = p_state->get_linear_velocity();
+ angular_velocity = p_state->get_angular_velocity();
+
+ if (sleeping != p_state->is_sleeping()) {
+ sleeping = p_state->is_sleeping();
emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
}
- GDVIRTUAL_CALL(_integrate_forces, state);
+ GDVIRTUAL_CALL(_integrate_forces, p_state);
set_block_transform_notify(false); // want it back
@@ -546,20 +457,18 @@ void RigidBody2D::_direct_state_changed(Object *p_state) {
}
}
- _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(state->get_contact_count() * sizeof(_RigidBody2DInOut));
+ _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut));
int toadd_count = 0; //state->get_contact_count();
RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction));
int toremove_count = 0;
//put the ones to add
- for (int i = 0; i < state->get_contact_count(); i++) {
- RID rid = state->get_contact_collider(i);
- ObjectID obj = state->get_contact_collider_id(i);
- int local_shape = state->get_contact_local_shape(i);
- int shape = state->get_contact_collider_shape(i);
-
- //bool found=false;
+ for (int i = 0; i < p_state->get_contact_count(); i++) {
+ RID rid = p_state->get_contact_collider(i);
+ ObjectID obj = p_state->get_contact_collider_id(i);
+ int local_shape = p_state->get_contact_local_shape(i);
+ int shape = p_state->get_contact_collider_shape(i);
Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj);
if (!E) {
@@ -612,8 +521,6 @@ void RigidBody2D::_direct_state_changed(Object *p_state) {
contact_monitor->locked = false;
}
-
- state = nullptr;
}
void RigidBody2D::set_mode(Mode p_mode) {
@@ -653,11 +560,53 @@ real_t RigidBody2D::get_mass() const {
void RigidBody2D::set_inertia(real_t p_inertia) {
ERR_FAIL_COND(p_inertia < 0);
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, p_inertia);
+ inertia = p_inertia;
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia);
}
real_t RigidBody2D::get_inertia() const {
- return PhysicsServer2D::get_singleton()->body_get_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA);
+ return inertia;
+}
+
+void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) {
+ if (center_of_mass_mode == p_mode) {
+ return;
+ }
+
+ center_of_mass_mode = p_mode;
+
+ switch (center_of_mass_mode) {
+ case CENTER_OF_MASS_MODE_AUTO: {
+ center_of_mass = Vector2();
+ PhysicsServer2D::get_singleton()->body_reset_mass_properties(get_rid());
+ if (inertia != 0.0) {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia);
+ }
+ } break;
+
+ case CENTER_OF_MASS_MODE_CUSTOM: {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+ } break;
+ }
+}
+
+RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const {
+ return center_of_mass_mode;
+}
+
+void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) {
+ if (center_of_mass == p_center_of_mass) {
+ return;
+ }
+
+ ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM);
+ center_of_mass = p_center_of_mass;
+
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+}
+
+const Vector2 &RigidBody2D::get_center_of_mass() const {
+ return center_of_mass;
}
void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
@@ -709,25 +658,15 @@ real_t RigidBody2D::get_angular_damp() const {
}
void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) {
- Vector2 v = state ? state->get_linear_velocity() : linear_velocity;
Vector2 axis = p_axis.normalized();
- v -= axis * axis.dot(v);
- v += p_axis;
- if (state) {
- set_linear_velocity(v);
- } else {
- PhysicsServer2D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis);
- linear_velocity = v;
- }
+ linear_velocity -= axis * axis.dot(linear_velocity);
+ linear_velocity += p_axis;
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
}
void RigidBody2D::set_linear_velocity(const Vector2 &p_velocity) {
linear_velocity = p_velocity;
- if (state) {
- state->set_linear_velocity(linear_velocity);
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
}
Vector2 RigidBody2D::get_linear_velocity() const {
@@ -736,11 +675,7 @@ Vector2 RigidBody2D::get_linear_velocity() const {
void RigidBody2D::set_angular_velocity(real_t p_velocity) {
angular_velocity = p_velocity;
- if (state) {
- state->set_angular_velocity(angular_velocity);
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity);
}
real_t RigidBody2D::get_angular_velocity() const {
@@ -925,6 +860,12 @@ void RigidBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody2D::get_inertia);
ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody2D::set_inertia);
+ ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody2D::set_center_of_mass_mode);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody2D::get_center_of_mass_mode);
+
+ ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody2D::set_center_of_mass);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody2D::get_center_of_mass);
+
ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override);
ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override);
@@ -981,8 +922,11 @@ void RigidBody2D::_bind_methods() {
GDVIRTUAL_BIND(_integrate_forces, "state");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp", PROPERTY_USAGE_NONE), "set_inertia", "get_inertia");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass");
+ ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
@@ -1012,14 +956,25 @@ void RigidBody2D::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
BIND_ENUM_CONSTANT(MODE_KINEMATIC);
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
+
BIND_ENUM_CONSTANT(CCD_MODE_DISABLED);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE);
}
+void RigidBody2D::_validate_property(PropertyInfo &property) const {
+ if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) {
+ if (property.name == "center_of_mass") {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+}
+
RigidBody2D::RigidBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) {
- PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody2D::_direct_state_changed));
+ PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
}
RigidBody2D::~RigidBody2D() {
@@ -1045,14 +1000,19 @@ void RigidBody2D::_reload_physics_characteristics() {
bool CharacterBody2D::move_and_slide() {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
- float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
+ double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector2 current_platform_velocity = platform_velocity;
if ((on_floor || on_wall) && platform_rid.is_valid()) {
- bool excluded = (moving_platform_ignore_layers & platform_layer) != 0;
+ bool excluded = false;
+ if (on_floor) {
+ excluded = (moving_platform_floor_layers & platform_layer) == 0;
+ } else if (on_wall) {
+ excluded = (moving_platform_wall_layers & platform_layer) == 0;
+ }
if (!excluded) {
- // This approach makes sure there is less delay between the actual body velocity and the one we saved.
+ //this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
Transform2D gt = get_global_transform();
@@ -1075,7 +1035,7 @@ bool CharacterBody2D::move_and_slide() {
PhysicsServer2D::MotionResult floor_result;
Set<RID> exclude;
exclude.insert(platform_rid);
- if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, exclude)) {
+ if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
@@ -1095,7 +1055,7 @@ bool CharacterBody2D::move_and_slide() {
return motion_results.size() > 0;
}
-void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) {
+void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) {
Vector2 motion = linear_velocity * p_delta;
Vector2 motion_slide_up = motion.slide(up_direction);
@@ -1239,7 +1199,7 @@ void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_flo
}
}
-void CharacterBody2D::_move_and_slide_free(real_t p_delta) {
+void CharacterBody2D::_move_and_slide_free(double p_delta) {
Vector2 motion = linear_velocity * p_delta;
platform_rid = RID();
@@ -1285,12 +1245,11 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
Transform2D gt = get_global_transform();
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
+ if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
bool apply = true;
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
- platform_velocity = result.collider_velocity;
_set_platform_data(result);
if (floor_stop_on_slope) {
@@ -1319,7 +1278,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
}
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
+ if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
@@ -1332,19 +1291,21 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_result.collision_normal;
- platform_velocity = p_result.collider_velocity;
_set_platform_data(p_result);
} else if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
- platform_velocity = p_result.collider_velocity;
- _set_platform_data(p_result);
+ // Don't apply wall velocity when the collider is a CharacterBody2D.
+ if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) {
+ _set_platform_data(p_result);
+ }
}
}
void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) {
platform_rid = p_result.collider;
+ platform_velocity = p_result.collider_velocity;
platform_layer = 0;
CollisionObject2D *collision_object = Object::cast_to<CollisionObject2D>(ObjectDB::get_instance(p_result.collider_id));
if (collision_object) {
@@ -1468,12 +1429,20 @@ void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) {
slide_on_ceiling = p_enabled;
}
-uint32_t CharacterBody2D::get_moving_platform_ignore_layers() const {
- return moving_platform_ignore_layers;
+uint32_t CharacterBody2D::get_moving_platform_floor_layers() const {
+ return moving_platform_floor_layers;
+}
+
+void CharacterBody2D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) {
+ moving_platform_floor_layers = p_exclude_layers;
+}
+
+uint32_t CharacterBody2D::get_moving_platform_wall_layers() const {
+ return moving_platform_wall_layers;
}
-void CharacterBody2D::set_moving_platform_ignore_layers(uint32_t p_exclude_layers) {
- moving_platform_ignore_layers = p_exclude_layers;
+void CharacterBody2D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) {
+ moving_platform_wall_layers = p_exclude_layers;
}
void CharacterBody2D::set_motion_mode(MotionMode p_mode) {
@@ -1558,8 +1527,10 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled);
ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled);
- ClassDB::bind_method(D_METHOD("set_moving_platform_ignore_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_ignore_layers);
- ClassDB::bind_method(D_METHOD("get_moving_platform_ignore_layers"), &CharacterBody2D::get_moving_platform_ignore_layers);
+ ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_floor_layers);
+ ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody2D::get_moving_platform_floor_layers);
+ ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_wall_layers);
+ ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody2D::get_moving_platform_wall_layers);
ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides);
ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides);
@@ -1601,7 +1572,8 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length");
ADD_GROUP("Moving platform", "moving_platform");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_ignore_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_ignore_layers", "get_moving_platform_ignore_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index a999317953..1d6437a3ad 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_test_only = false, real_t p_margin = 0.08);
public:
- bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>());
+ bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
@@ -63,20 +63,13 @@ public:
class StaticBody2D : public PhysicsBody2D {
GDCLASS(StaticBody2D, PhysicsBody2D);
+private:
Vector2 constant_linear_velocity;
real_t constant_angular_velocity = 0.0;
Ref<PhysicsMaterial> physics_material_override;
- bool kinematic_motion = false;
- bool sync_to_physics = false;
-
- Transform2D last_valid_transform;
-
- void _direct_state_changed(Object *p_state);
-
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
@@ -89,17 +82,32 @@ public:
Vector2 get_constant_linear_velocity() const;
real_t get_constant_angular_velocity() const;
- virtual TypedArray<String> get_configuration_warnings() const override;
-
- StaticBody2D();
+ StaticBody2D(PhysicsServer2D::BodyMode p_mode = PhysicsServer2D::BODY_MODE_STATIC);
private:
void _reload_physics_characteristics();
+};
- void _update_kinematic_motion();
+class AnimatableBody2D : public StaticBody2D {
+ GDCLASS(AnimatableBody2D, StaticBody2D);
+
+private:
+ bool sync_to_physics = false;
+
+ Transform2D last_valid_transform;
+
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state);
+ void _body_state_changed(PhysicsDirectBodyState2D *p_state);
- void set_kinematic_motion_enabled(bool p_enabled);
- bool is_kinematic_motion_enabled() const;
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ AnimatableBody2D();
+
+private:
+ void _update_kinematic_motion();
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
@@ -116,6 +124,11 @@ public:
MODE_KINEMATIC,
};
+ enum CenterOfMassMode {
+ CENTER_OF_MASS_MODE_AUTO,
+ CENTER_OF_MASS_MODE_CUSTOM,
+ };
+
enum CCDMode {
CCD_MODE_DISABLED,
CCD_MODE_CAST_RAY,
@@ -124,10 +137,13 @@ public:
private:
bool can_sleep = true;
- PhysicsDirectBodyState2D *state = nullptr;
Mode mode = MODE_DYNAMIC;
real_t mass = 1.0;
+ real_t inertia = 0.0;
+ CenterOfMassMode center_of_mass_mode = CENTER_OF_MASS_MODE_AUTO;
+ Vector2 center_of_mass;
+
Ref<PhysicsMaterial> physics_material_override;
real_t gravity_scale = 1.0;
real_t linear_damp = -1.0;
@@ -183,12 +199,16 @@ private:
void _body_exit_tree(ObjectID p_id);
void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape);
- void _direct_state_changed(Object *p_state);
+
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state);
+ void _body_state_changed(PhysicsDirectBodyState2D *p_state);
protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
+
GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *)
public:
@@ -201,6 +221,12 @@ public:
void set_inertia(real_t p_inertia);
real_t get_inertia() const;
+ void set_center_of_mass_mode(CenterOfMassMode p_mode);
+ CenterOfMassMode get_center_of_mass_mode() const;
+
+ void set_center_of_mass(const Vector2 &p_center_of_mass);
+ const Vector2 &get_center_of_mass() const;
+
void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
Ref<PhysicsMaterial> get_physics_material_override() const;
@@ -265,6 +291,7 @@ private:
};
VARIANT_ENUM_CAST(RigidBody2D::Mode);
+VARIANT_ENUM_CAST(RigidBody2D::CenterOfMassMode);
VARIANT_ENUM_CAST(RigidBody2D::CCDMode);
class CharacterBody2D : public PhysicsBody2D {
@@ -310,7 +337,8 @@ private:
float floor_snap_length = 0;
real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
- uint32_t moving_platform_ignore_layers = 0;
+ uint32_t moving_platform_floor_layers = UINT32_MAX;
+ uint32_t moving_platform_wall_layers = 0;
Vector2 linear_velocity;
Vector2 floor_normal;
@@ -350,14 +378,17 @@ private:
real_t get_free_mode_min_slide_angle() const;
void set_free_mode_min_slide_angle(real_t p_radians);
- uint32_t get_moving_platform_ignore_layers() const;
- void set_moving_platform_ignore_layers(const uint32_t p_exclude_layer);
+ uint32_t get_moving_platform_floor_layers() const;
+ void set_moving_platform_floor_layers(const uint32_t p_exclude_layer);
+
+ uint32_t get_moving_platform_wall_layers() const;
+ void set_moving_platform_wall_layers(const uint32_t p_exclude_layer);
void set_motion_mode(MotionMode p_mode);
MotionMode get_motion_mode() const;
- void _move_and_slide_free(real_t p_delta);
- void _move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity);
+ void _move_and_slide_free(double p_delta);
+ void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
Ref<KinematicCollision2D> _get_last_slide_collision();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 13f1d258a8..e1552b3b60 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -314,16 +314,38 @@ int TileMap::get_quadrant_size() const {
return quadrant_size;
}
-void TileMap::set_layers_count(int p_layers_count) {
- ERR_FAIL_COND(p_layers_count < 0);
- _clear_internals();
+int TileMap::get_layers_count() const {
+ return layers.size();
+}
- layers.resize(p_layers_count);
+void TileMap::add_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = layers.size();
+ }
+
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ layers.insert(p_to_pos, TileMapLayer());
_recreate_internals();
notify_property_list_changed();
- if (selected_layer >= p_layers_count) {
- selected_layer = -1;
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+void TileMap::move_layer(int p_layer, int p_to_pos) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ TileMapLayer tl = layers[p_layer];
+ layers.insert(p_to_pos, tl);
+ layers.remove(p_to_pos < p_layer ? p_layer + 1 : p_layer);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer == p_layer) {
+ selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
}
emit_signal(SNAME("changed"));
@@ -331,8 +353,20 @@ void TileMap::set_layers_count(int p_layers_count) {
update_configuration_warnings();
}
-int TileMap::get_layers_count() const {
- return layers.size();
+void TileMap::remove_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ layers.remove(p_layer);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer >= p_layer) {
+ selected_layer -= 1;
+ }
+
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
}
void TileMap::set_layer_name(int p_layer, String p_name) {
@@ -1147,7 +1181,7 @@ void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) {
PhysicsServer2D::get_singleton()->body_set_space(body, space);
Transform2D xform;
- xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer)));
+ xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)));
xform = global_transform * xform;
PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
}
@@ -2896,8 +2930,10 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count);
ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count);
+ ClassDB::bind_method(D_METHOD("add_layer"), &TileMap::add_layer);
+ ClassDB::bind_method(D_METHOD("move_layer"), &TileMap::move_layer);
+ ClassDB::bind_method(D_METHOD("remove_layer"), &TileMap::remove_layer);
ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name);
ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name);
ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled);
@@ -2942,9 +2978,7 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
- ADD_GROUP("Layers", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count");
- ADD_PROPERTY_DEFAULT("layers_count", 1);
+ ADD_ARRAY("layers", "layer_");
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 4e2d76a7b7..3ac50fc7cc 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -308,8 +308,10 @@ 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));
// Layers management.
- void set_layers_count(int p_layers_count);
int get_layers_count() const;
+ void add_layer(int p_to_pos);
+ void move_layer(int p_layer, int p_to_pos);
+ void remove_layer(int p_layer);
void set_layer_name(int p_layer, String p_name);
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 3b44580264..16d5b9da3e 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -95,7 +95,7 @@ static const Vector3 speaker_directions[7] = {
Vector3(1.0, 0.0, 0.0).normalized(), // side-right
};
-void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, AudioStreamPlayer3D::Output &output) {
+void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output) {
unsigned int speaker_count = 0; // only main speakers (no LFE)
switch (AudioServer::get_singleton()->get_speaker_mode()) {
case AudioServer::SPEAKER_MODE_STEREO:
@@ -118,182 +118,94 @@ void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tig
switch (AudioServer::get_singleton()->get_speaker_mode()) {
case AudioServer::SPEAKER_SURROUND_71:
- output.vol[3].l = volumes[5]; // side-left
- output.vol[3].r = volumes[6]; // side-right
+ output.write[3].l = volumes[5]; // side-left
+ output.write[3].r = volumes[6]; // side-right
[[fallthrough]];
case AudioServer::SPEAKER_SURROUND_51:
- output.vol[2].l = volumes[3]; // rear-left
- output.vol[2].r = volumes[4]; // rear-right
+ output.write[2].l = volumes[3]; // rear-left
+ output.write[2].r = volumes[4]; // rear-right
[[fallthrough]];
case AudioServer::SPEAKER_SURROUND_31:
- output.vol[1].r = 1.0; // LFE - always full power
- output.vol[1].l = volumes[2]; // center
+ output.write[1].r = 1.0; // LFE - always full power
+ output.write[1].l = volumes[2]; // center
[[fallthrough]];
case AudioServer::SPEAKER_MODE_STEREO:
- output.vol[0].r = volumes[1]; // front-right
- output.vol[0].l = volumes[0]; // front-left
+ output.write[0].r = volumes[1]; // front-right
+ output.write[0].l = volumes[0]; // front-left
break;
}
}
-void AudioStreamPlayer3D::_mix_audio() {
- if (!stream_playback.is_valid() || !active.is_set() ||
- (stream_paused && !stream_paused_fade_out)) {
- return;
- }
+void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol) {
+ reverb_vol.resize(4);
+ reverb_vol.write[0] = AudioFrame(0, 0);
+ reverb_vol.write[1] = AudioFrame(0, 0);
+ reverb_vol.write[2] = AudioFrame(0, 0);
+ reverb_vol.write[3] = AudioFrame(0, 0);
- bool started = false;
- if (setseek.get() >= 0.0) {
- stream_playback->start(setseek.get());
- setseek.set(-1.0); //reset seek
- started = true;
- }
+ float uniformity = area->get_reverb_uniformity();
+ float area_send = area->get_reverb_amount();
- //get data
- AudioFrame *buffer = mix_buffer.ptrw();
- int buffer_size = mix_buffer.size();
+ if (uniformity > 0.0) {
+ float distance = listener_area_pos.length();
+ float attenuation = Math::db2linear(_get_attenuation_db(distance));
- if (stream_paused_fade_out) {
- // Short fadeout ramp
- buffer_size = MIN(buffer_size, 128);
- }
+ // Determine the fraction of sound that would come from each speaker if they were all driven uniformly.
+ float center_val[3] = { 0.5f, 0.25f, 0.16666f };
+ int channel_count = AudioServer::get_singleton()->get_channel_count();
+ AudioFrame center_frame(center_val[channel_count - 1], center_val[channel_count - 1]);
- // Mix if we're not paused or we're fading out
- if ((output_count.get() > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) {
- float output_pitch_scale = 0.0;
- if (output_count.get()) {
- //used for doppler, not realistic but good enough
- for (int i = 0; i < output_count.get(); i++) {
- output_pitch_scale += outputs[i].pitch_scale;
- }
- output_pitch_scale /= float(output_count.get());
- } else {
- output_pitch_scale = 1.0;
- }
+ if (attenuation < 1.0) {
+ //pan the uniform sound
+ Vector3 rev_pos = listener_area_pos;
+ rev_pos.y = 0;
+ rev_pos.normalize();
- stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size);
- }
-
- //write all outputs
- for (int i = 0; i < output_count.get(); i++) {
- Output current = outputs[i];
-
- //see if current output exists, to keep volume ramp
- bool found = false;
- for (int j = i; j < prev_output_count; j++) {
- if (prev_outputs[j].viewport == current.viewport) {
- if (j != i) {
- SWAP(prev_outputs[j], prev_outputs[i]);
- }
- found = true;
- break;
+ if (channel_count >= 1) {
+ // Stereo pair
+ float c = rev_pos.x * 0.5 + 0.5;
+ reverb_vol.write[0].l = 1.0 - c;
+ reverb_vol.write[0].r = c;
}
- }
- bool interpolate_filter = !started;
+ if (channel_count >= 3) {
+ // Center pair + Side pair
+ float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5;
+ float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5;
- if (!found) {
- //create new if was not used before
- if (prev_output_count < MAX_OUTPUTS) {
- prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport
- prev_output_count++;
+ reverb_vol.write[1].l = xl;
+ reverb_vol.write[1].r = xr;
+ reverb_vol.write[2].l = 1.0 - xr;
+ reverb_vol.write[2].r = 1.0 - xl;
}
- prev_outputs[i] = current;
- interpolate_filter = false;
- }
-
- //mix!
-
- int buffers = AudioServer::get_singleton()->get_channel_count();
-
- for (int k = 0; k < buffers; k++) {
- AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
- AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
- AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = vol_prev;
- if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) {
- continue; //may have been deleted, will be updated on process
+ if (channel_count >= 4) {
+ // Rear pair
+ // FIXME: Not sure what math should be done here
+ float c = rev_pos.x * 0.5 + 0.5;
+ reverb_vol.write[3].l = 1.0 - c;
+ reverb_vol.write[3].r = c;
}
- AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
- current.filter.set_mode(AudioFilterSW::HIGHSHELF);
- current.filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate());
- current.filter.set_cutoff(attenuation_filter_cutoff_hz);
- current.filter.set_resonance(1);
- current.filter.set_stages(1);
- current.filter.set_gain(current.filter_gain);
-
- if (interpolate_filter) {
- current.filter_process[k * 2 + 0] = prev_outputs[i].filter_process[k * 2 + 0];
- current.filter_process[k * 2 + 1] = prev_outputs[i].filter_process[k * 2 + 1];
-
- current.filter_process[k * 2 + 0].set_filter(&current.filter, false);
- current.filter_process[k * 2 + 1].set_filter(&current.filter, false);
-
- current.filter_process[k * 2 + 0].update_coeffs(buffer_size);
- current.filter_process[k * 2 + 1].update_coeffs(buffer_size);
- for (int j = 0; j < buffer_size; j++) {
- AudioFrame f = buffer[j] * vol;
- current.filter_process[k * 2 + 0].process_one_interp(f.l);
- current.filter_process[k * 2 + 1].process_one_interp(f.r);
-
- target[j] += f;
- vol += vol_inc;
- }
- } else {
- current.filter_process[k * 2 + 0].set_filter(&current.filter);
- current.filter_process[k * 2 + 1].set_filter(&current.filter);
-
- current.filter_process[k * 2 + 0].update_coeffs();
- current.filter_process[k * 2 + 1].update_coeffs();
- for (int j = 0; j < buffer_size; j++) {
- AudioFrame f = buffer[j] * vol;
- current.filter_process[k * 2 + 0].process_one(f.l);
- current.filter_process[k * 2 + 1].process_one(f.r);
-
- target[j] += f;
- vol += vol_inc;
- }
+ for (int i = 0; i < channel_count; i++) {
+ reverb_vol.write[i] = reverb_vol[i].lerp(center_frame, attenuation);
}
-
- if (current.reverb_bus_index >= 0) {
- if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.reverb_bus_index, k)) {
- continue; //may have been deleted, will be updated on process
- }
-
- AudioFrame *rtarget = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.reverb_bus_index, k);
-
- if (current.reverb_bus_index == prev_outputs[i].reverb_bus_index) {
- AudioFrame rvol_inc = (current.reverb_vol[k] - prev_outputs[i].reverb_vol[k]) / float(buffer_size);
- AudioFrame rvol = prev_outputs[i].reverb_vol[k];
-
- for (int j = 0; j < buffer_size; j++) {
- rtarget[j] += buffer[j] * rvol;
- rvol += rvol_inc;
- }
- } else {
- AudioFrame rvol = current.reverb_vol[k];
- for (int j = 0; j < buffer_size; j++) {
- rtarget[j] += buffer[j] * rvol;
- }
- }
+ } else {
+ for (int i = 0; i < channel_count; i++) {
+ reverb_vol.write[i] = center_frame;
}
}
- prev_outputs[i] = current;
- }
-
- prev_output_count = output_count.get();
+ for (int i = 0; i < channel_count; i++) {
+ reverb_vol.write[i] = direct_path_vol[i].lerp(reverb_vol[i] * attenuation, uniformity);
+ reverb_vol.write[i] *= area_send;
+ }
- //stream is no longer active, disable this.
- if (!stream_playback->is_playing()) {
- active.clear();
+ } else {
+ for (int i = 0; i < 4; i++) {
+ reverb_vol.write[i] = direct_path_vol[i] * area_send;
+ }
}
-
- output_ready.clear();
- stream_paused_fade_in = false;
- stream_paused_fade_out = false;
}
float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
@@ -329,14 +241,15 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
void AudioStreamPlayer3D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
velocity_tracker->reset(get_global_transform().origin);
- AudioServer::get_singleton()->add_callback(_mix_audios, this);
+ AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
}
}
if (p_what == NOTIFICATION_EXIT_TREE) {
- AudioServer::get_singleton()->remove_callback(_mix_audios, this);
+ stop();
+ AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
}
if (p_what == NOTIFICATION_PAUSED) {
@@ -359,268 +272,216 @@ void AudioStreamPlayer3D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
- if (!output_ready.is_set()) {
- Vector3 linear_velocity;
+ if (!stream_playback.is_valid()) {
+ return;
+ }
+ //start playing if requested
- //compute linear velocity for doppler
- if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
- linear_velocity = velocity_tracker->get_tracked_linear_velocity();
- }
+ if (setplay.get() >= 0) {
+ Vector<AudioFrame> volume_vector = _update_panning();
+ AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
+ active.set();
+ setplay.set(-1);
+ }
+
+ if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) {
+ _update_panning();
+ last_mix_count = AudioServer::get_singleton()->get_mix_count();
+ }
+
+ // Stop playing if no longer active.
+ if (!active.is_set()) {
+ set_physics_process_internal(false);
+ emit_signal(SNAME("finished"));
+ }
+ }
+}
+
+Area3D *AudioStreamPlayer3D::_get_overriding_area() {
+ //check if any area is diverting sound into a bus
+ Ref<World3D> world_3d = get_world_3d();
+ ERR_FAIL_COND_V(world_3d.is_null(), nullptr);
+
+ Vector3 global_pos = get_global_transform().origin;
- Ref<World3D> world_3d = get_world_3d();
- ERR_FAIL_COND(world_3d.is_null());
+ PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
- int new_output_count = 0;
+ PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS];
- Vector3 global_pos = get_global_transform().origin;
+ int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
- int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
+ for (int i = 0; i < areas; i++) {
+ if (!sr[i].collider) {
+ continue;
+ }
+
+ Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider);
+ if (!tarea) {
+ continue;
+ }
+
+ if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) {
+ continue;
+ }
+
+ return tarea;
+ }
+ return nullptr;
+}
- //check if any area is diverting sound into a bus
+StringName AudioStreamPlayer3D::_get_actual_bus() {
+ if (!stream_playback.is_valid()) {
+ return SNAME("Master");
+ }
+ Area3D *overriding_area = _get_overriding_area();
+ if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) {
+ return overriding_area->get_audio_bus_name();
+ }
+ return bus;
+}
- PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
+Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
+ Vector<AudioFrame> output_volume_vector;
+ output_volume_vector.resize(4);
+ for (AudioFrame &frame : output_volume_vector) {
+ frame = AudioFrame(0, 0);
+ }
- PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS];
+ ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector);
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
- Area3D *area = nullptr;
+ Vector3 linear_velocity;
- for (int i = 0; i < areas; i++) {
- if (!sr[i].collider) {
- continue;
- }
+ //compute linear velocity for doppler
+ if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
+ linear_velocity = velocity_tracker->get_tracked_linear_velocity();
+ }
- Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider);
- if (!tarea) {
- continue;
- }
+ Vector3 global_pos = get_global_transform().origin;
- if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) {
- continue;
- }
+ Ref<World3D> world_3d = get_world_3d();
+ ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector);
- area = tarea;
- break;
+ Set<Camera3D *> cameras = world_3d->get_cameras();
+ cameras.insert(get_viewport()->get_camera_3d());
+
+ PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
+
+ for (Camera3D *camera : cameras) {
+ Viewport *vp = camera->get_viewport();
+ if (!vp->is_audio_listener_3d()) {
+ continue;
+ }
+
+ bool listener_is_camera = true;
+ Node3D *listener_node = camera;
+
+ Listener3D *listener = vp->get_listener_3d();
+ if (listener) {
+ listener_node = listener;
+ listener_is_camera = false;
+ }
+
+ Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos);
+
+ float dist = local_pos.length();
+
+ Vector3 area_sound_pos;
+ Vector3 listener_area_pos;
+
+ Area3D *area = _get_overriding_area();
+
+ if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
+ area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin);
+ listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos);
+ }
+
+ if (max_distance > 0) {
+ float total_max = max_distance;
+
+ if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
+ total_max = MAX(total_max, listener_area_pos.length());
}
+ if (total_max > max_distance) {
+ continue; //can't hear this sound in this listener
+ }
+ }
+
+ float multiplier = Math::db2linear(_get_attenuation_db(dist));
+ if (max_distance > 0) {
+ multiplier *= MAX(0, 1.0 - (dist / max_distance));
+ }
- for (const Set<Camera3D *>::Element *E = world_3d->get_cameras().front(); E; E = E->next()) {
- Camera3D *camera = E->get();
- Viewport *vp = camera->get_viewport();
- if (!vp->is_audio_listener_3d()) {
- continue;
- }
-
- bool listener_is_camera = true;
- Node3D *listener_node = camera;
-
- Listener3D *listener = vp->get_listener_3d();
- if (listener) {
- listener_node = listener;
- listener_is_camera = false;
- }
-
- Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos);
-
- float dist = local_pos.length();
-
- Vector3 area_sound_pos;
- Vector3 listener_area_pos;
-
- if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
- area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin);
- listener_area_pos = listener_node->to_local(area_sound_pos);
- }
-
- if (max_distance > 0) {
- float total_max = max_distance;
-
- if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
- total_max = MAX(total_max, listener_area_pos.length());
- }
- if (total_max > max_distance) {
- continue; //can't hear this sound in this listener
- }
- }
-
- float multiplier = Math::db2linear(_get_attenuation_db(dist));
- if (max_distance > 0) {
- multiplier *= MAX(0, 1.0 - (dist / max_distance));
- }
-
- Output output;
- output.bus_index = bus_index;
- output.reverb_bus_index = -1; //no reverb by default
- output.viewport = vp;
-
- float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db;
-
- if (emission_angle_enabled) {
- Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin;
- float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative
- float angle = Math::rad2deg(Math::acos(c));
- if (angle > emission_angle) {
- db_att -= -emission_angle_filter_attenuation_db;
- }
- }
-
- output.filter_gain = Math::db2linear(db_att);
-
- //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
- // speakers not facing the source) - this could be made distance dependent.
- _calc_output_vol(local_pos.normalized(), 4.0, output);
-
- unsigned int cc = AudioServer::get_singleton()->get_channel_count();
- for (unsigned int k = 0; k < cc; k++) {
- output.vol[k] *= multiplier;
- }
-
- bool filled_reverb = false;
- int vol_index_max = AudioServer::get_singleton()->get_speaker_mode() + 1;
-
- if (area) {
- if (area->is_overriding_audio_bus()) {
- //override audio bus
- StringName bus_name = area->get_audio_bus_name();
- output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name);
- }
-
- if (area->is_using_reverb_bus()) {
- filled_reverb = true;
- StringName bus_name = area->get_reverb_bus();
- output.reverb_bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name);
-
- float uniformity = area->get_reverb_uniformity();
- float area_send = area->get_reverb_amount();
-
- if (uniformity > 0.0) {
- float distance = listener_area_pos.length();
- float attenuation = Math::db2linear(_get_attenuation_db(distance));
-
- //float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life
-
- float center_val[3] = { 0.5f, 0.25f, 0.16666f };
- AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]);
-
- if (attenuation < 1.0) {
- //pan the uniform sound
- Vector3 rev_pos = listener_area_pos;
- rev_pos.y = 0;
- rev_pos.normalize();
-
- if (cc >= 1) {
- // Stereo pair
- float c = rev_pos.x * 0.5 + 0.5;
- output.reverb_vol[0].l = 1.0 - c;
- output.reverb_vol[0].r = c;
- }
-
- if (cc >= 3) {
- // Center pair + Side pair
- float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5;
- float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5;
-
- output.reverb_vol[1].l = xl;
- output.reverb_vol[1].r = xr;
- output.reverb_vol[2].l = 1.0 - xr;
- output.reverb_vol[2].r = 1.0 - xl;
- }
-
- if (cc >= 4) {
- // Rear pair
- // FIXME: Not sure what math should be done here
- float c = rev_pos.x * 0.5 + 0.5;
- output.reverb_vol[3].l = 1.0 - c;
- output.reverb_vol[3].r = c;
- }
-
- for (int i = 0; i < vol_index_max; i++) {
- output.reverb_vol[i] = output.reverb_vol[i].lerp(center_frame, attenuation);
- }
- } else {
- for (int i = 0; i < vol_index_max; i++) {
- output.reverb_vol[i] = center_frame;
- }
- }
-
- for (int i = 0; i < vol_index_max; i++) {
- output.reverb_vol[i] = output.vol[i].lerp(output.reverb_vol[i] * attenuation, uniformity);
- output.reverb_vol[i] *= area_send;
- }
-
- } else {
- for (int i = 0; i < vol_index_max; i++) {
- output.reverb_vol[i] = output.vol[i] * area_send;
- }
- }
- }
- }
-
- if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
- Vector3 listener_velocity;
-
- if (listener_is_camera) {
- listener_velocity = camera->get_doppler_tracked_velocity();
- }
-
- Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
-
- if (local_velocity == Vector3()) {
- output.pitch_scale = 1.0;
- } else {
- float approaching = local_pos.normalized().dot(local_velocity.normalized());
- float velocity = local_velocity.length();
- float speed_of_sound = 343.0;
-
- output.pitch_scale = speed_of_sound / (speed_of_sound + velocity * approaching);
- output.pitch_scale = CLAMP(output.pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
- }
-
- } else {
- output.pitch_scale = 1.0;
- }
-
- if (!filled_reverb) {
- for (int i = 0; i < vol_index_max; i++) {
- output.reverb_vol[i] = AudioFrame(0, 0);
- }
- }
-
- outputs[new_output_count] = output;
- new_output_count++;
- if (new_output_count == MAX_OUTPUTS) {
- break;
- }
+ float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db;
+
+ if (emission_angle_enabled) {
+ Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin;
+ float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative
+ float angle = Math::rad2deg(Math::acos(c));
+ if (angle > emission_angle) {
+ db_att -= -emission_angle_filter_attenuation_db;
}
+ }
+
+ AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz);
+ //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
+ // speakers not facing the source) - this could be made distance dependent.
+ _calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector);
- output_count.set(new_output_count);
- output_ready.set();
+ for (unsigned int k = 0; k < 4; k++) {
+ output_volume_vector.write[k] = multiplier * output_volume_vector[k];
}
- //start playing if requested
- if (setplay.get() >= 0.0) {
- setseek.set(setplay.get());
- active.set();
- setplay.set(-1);
+ Map<StringName, Vector<AudioFrame>> bus_volumes;
+ if (area) {
+ if (area->is_overriding_audio_bus()) {
+ //override audio bus
+ bus_volumes[area->get_audio_bus_name()] = output_volume_vector;
+ }
+
+ if (area->is_using_reverb_bus()) {
+ StringName reverb_bus_name = area->get_reverb_bus();
+ Vector<AudioFrame> reverb_vol;
+ _calc_reverb_vol(area, listener_area_pos, output_volume_vector, reverb_vol);
+ bus_volumes[reverb_bus_name] = reverb_vol;
+ }
+ } else {
+ bus_volumes[bus] = output_volume_vector;
}
+ AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes);
- //stop playing if no longer active
- if (!active.is_set()) {
- set_physics_process_internal(false);
- emit_signal(SNAME("finished"));
+ if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
+ Vector3 listener_velocity;
+
+ if (listener_is_camera) {
+ listener_velocity = camera->get_doppler_tracked_velocity();
+ }
+
+ Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
+
+ if (local_velocity == Vector3()) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
+ } else {
+ float approaching = local_pos.normalized().dot(local_velocity.normalized());
+ float velocity = local_velocity.length();
+ float speed_of_sound = 343.0;
+
+ float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
+ doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
+
+ AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale);
+ }
+ } else {
+ AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
}
}
+ return output_volume_vector;
}
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
- AudioServer::get_singleton()->lock();
-
- mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
-
if (stream_playback.is_valid()) {
+ stop();
stream_playback.unref();
stream.unref();
- active.clear();
- setseek.set(-1);
}
if (p_stream.is_valid()) {
@@ -632,8 +493,6 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
}
}
- AudioServer::get_singleton()->unlock();
-
if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
}
@@ -677,27 +536,22 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
}
void AudioStreamPlayer3D::play(float p_from_pos) {
- if (!is_playing()) {
- // Reset the prev_output_count if the stream is stopped
- prev_output_count = 0;
- }
-
if (stream_playback.is_valid()) {
setplay.set(p_from_pos);
- output_ready.clear();
set_physics_process_internal(true);
}
}
void AudioStreamPlayer3D::seek(float p_seconds) {
- if (stream_playback.is_valid()) {
- setseek.set(p_seconds);
+ if (stream_playback.is_valid() && active.is_set()) {
+ play(p_seconds);
}
}
void AudioStreamPlayer3D::stop() {
if (stream_playback.is_valid()) {
active.clear();
+ AudioServer::get_singleton()->stop_playback_stream(stream_playback);
set_physics_process_internal(false);
setplay.set(-1);
}
@@ -713,11 +567,7 @@ bool AudioStreamPlayer3D::is_playing() const {
float AudioStreamPlayer3D::get_playback_position() {
if (stream_playback.is_valid()) {
- float ss = setseek.get();
- if (ss >= 0.0) {
- return ss;
- }
- return stream_playback->get_playback_position();
+ return AudioServer::get_singleton()->get_playback_position(stream_playback);
}
return 0;
@@ -736,7 +586,7 @@ StringName AudioStreamPlayer3D::get_bus() const {
return bus;
}
}
- return "Master";
+ return SNAME("Master");
}
void AudioStreamPlayer3D::set_autoplay(bool p_enable) {
@@ -879,15 +729,16 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
}
void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
- if (p_pause != stream_paused) {
- stream_paused = p_pause;
- stream_paused_fade_in = !stream_paused;
- stream_paused_fade_out = stream_paused;
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
}
}
bool AudioStreamPlayer3D::get_stream_paused() const {
- return stream_paused;
+ if (stream_playback.is_valid()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ }
+ return false;
}
Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index f86e19403c..f915abad2b 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -31,10 +31,12 @@
#ifndef AUDIO_STREAM_PLAYER_3D_H
#define AUDIO_STREAM_PLAYER_3D_H
+#include "scene/3d/area_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/velocity_tracker_3d.h"
#include "servers/audio/audio_filter_sw.h"
#include "servers/audio/audio_stream.h"
+#include "servers/audio_server.h"
class Camera3D;
class AudioStreamPlayer3D : public Node3D {
@@ -66,31 +68,9 @@ private:
};
- struct Output {
- AudioFilterSW filter;
- AudioFilterSW::Processor filter_process[8];
- AudioFrame vol[4];
- float filter_gain = 0.0;
- float pitch_scale = 0.0;
- int bus_index = -1;
- int reverb_bus_index = -1;
- AudioFrame reverb_vol[4];
- Viewport *viewport = nullptr; //pointer only used for reference to previous mix
- };
-
- Output outputs[MAX_OUTPUTS];
- SafeNumeric<int> output_count;
- SafeFlag output_ready;
-
- //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks)
- Output prev_outputs[MAX_OUTPUTS];
- int prev_output_count = 0;
-
Ref<AudioStreamPlayback> stream_playback;
Ref<AudioStream> stream;
- Vector<AudioFrame> mix_buffer;
- SafeNumeric<float> setseek{ -1.0 };
SafeFlag active;
SafeNumeric<float> setplay{ -1.0 };
@@ -100,17 +80,21 @@ private:
float max_db = 3.0;
float pitch_scale = 1.0;
bool autoplay = false;
- bool stream_paused = false;
- bool stream_paused_fade_in = false;
- bool stream_paused_fade_out = false;
- StringName bus;
+ StringName bus = "Master";
+
+ uint64_t last_mix_count = -1;
+
+ static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output);
+
+ void _calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol);
- static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Output &output);
- void _mix_audio();
- static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_mix_audio(); }
+ static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_update_panning(); }
void _set_playing(bool p_enable);
bool _is_active() const;
+ StringName _get_actual_bus();
+ Area3D *_get_overriding_area();
+ Vector<AudioFrame> _update_panning();
void _bus_layout_changed();
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index d34331e1d8..00c6664e65 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -111,9 +111,9 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
return Ref<KinematicCollision3D>();
}
-bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
+bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
Transform3D gt = get_global_transform();
- bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_exclude);
+ bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
@@ -223,121 +223,113 @@ Ref<PhysicsMaterial> StaticBody3D::get_physics_material_override() const {
return physics_material_override;
}
-void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) {
- if (p_enabled == kinematic_motion) {
- return;
- }
+void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) {
+ constant_linear_velocity = p_vel;
- kinematic_motion = p_enabled;
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
+}
- if (kinematic_motion) {
- set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
- } else {
- set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
- }
+void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) {
+ constant_angular_velocity = p_vel;
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- return;
- }
-#endif
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
+}
- _update_kinematic_motion();
+Vector3 StaticBody3D::get_constant_linear_velocity() const {
+ return constant_linear_velocity;
}
-bool StaticBody3D::is_kinematic_motion_enabled() const {
- return kinematic_motion;
+Vector3 StaticBody3D::get_constant_angular_velocity() const {
+ return constant_angular_velocity;
}
-void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) {
- constant_linear_velocity = p_vel;
+void StaticBody3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody3D::get_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody3D::get_constant_angular_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
+}
+
+StaticBody3D::StaticBody3D(PhysicsServer3D::BodyMode p_mode) :
+ PhysicsBody3D(p_mode) {
+}
- if (kinematic_motion) {
- _update_kinematic_motion();
+void StaticBody3D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, 1);
} else {
- PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
}
}
-void StaticBody3D::set_sync_to_physics(bool p_enable) {
+Vector3 AnimatableBody3D::get_linear_velocity() const {
+ return linear_velocity;
+}
+
+Vector3 AnimatableBody3D::get_angular_velocity() const {
+ return angular_velocity;
+}
+
+void AnimatableBody3D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
sync_to_physics = p_enable;
+ _update_kinematic_motion();
+}
+
+bool AnimatableBody3D::is_sync_to_physics_enabled() const {
+ return sync_to_physics;
+}
+
+void AnimatableBody3D::_update_kinematic_motion() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
return;
}
#endif
- if (kinematic_motion) {
- _update_kinematic_motion();
+ if (sync_to_physics) {
+ set_only_update_transform_changes(true);
+ set_notify_local_transform(true);
+ } else {
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
}
}
-bool StaticBody3D::is_sync_to_physics_enabled() const {
- return sync_to_physics;
+void AnimatableBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) {
+ AnimatableBody3D *body = (AnimatableBody3D *)p_instance;
+ body->_body_state_changed(p_state);
}
-void StaticBody3D::_direct_state_changed(Object *p_state) {
- PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
-
- linear_velocity = state->get_linear_velocity();
- angular_velocity = state->get_angular_velocity();
+void AnimatableBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
+ linear_velocity = p_state->get_linear_velocity();
+ angular_velocity = p_state->get_angular_velocity();
if (!sync_to_physics) {
return;
}
- last_valid_transform = state->get_transform();
+ last_valid_transform = p_state->get_transform();
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
_on_transform_changed();
}
-TypedArray<String> StaticBody3D::get_configuration_warnings() const {
- TypedArray<String> warnings = PhysicsBody3D::get_configuration_warnings();
-
- if (sync_to_physics && !kinematic_motion) {
- warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled."));
- }
-
- return warnings;
-}
-
-void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) {
- constant_angular_velocity = p_vel;
-
- if (kinematic_motion) {
- _update_kinematic_motion();
- } else {
- PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
- }
-}
-
-Vector3 StaticBody3D::get_constant_linear_velocity() const {
- return constant_linear_velocity;
-}
-
-Vector3 StaticBody3D::get_constant_angular_velocity() const {
- return constant_angular_velocity;
-}
-
-Vector3 StaticBody3D::get_linear_velocity() const {
- return linear_velocity;
-}
-
-Vector3 StaticBody3D::get_angular_velocity() const {
- return angular_velocity;
-}
-
-void StaticBody3D::_notification(int p_what) {
+void AnimatableBody3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
@@ -347,17 +339,6 @@ void StaticBody3D::_notification(int p_what) {
// Used by sync to physics, send the new transform to the physics...
Transform3D new_transform = get_global_transform();
- real_t delta_time = get_physics_process_delta_time();
- new_transform.origin += constant_linear_velocity * delta_time;
-
- real_t ang_vel = constant_angular_velocity.length();
- if (!Math::is_zero_approx(ang_vel)) {
- Vector3 ang_vel_axis = constant_angular_velocity / ang_vel;
- Basis rot(ang_vel_axis, ang_vel * delta_time);
- new_transform.basis = rot * new_transform.basis;
- new_transform.orthonormalize();
- }
-
PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform);
// ... but then revert changes.
@@ -366,108 +347,21 @@ void StaticBody3D::_notification(int p_what) {
set_notify_local_transform(true);
_on_transform_changed();
} break;
-
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- ERR_FAIL_COND(!kinematic_motion);
-
- Transform3D new_transform = get_global_transform();
-
- real_t delta_time = get_physics_process_delta_time();
- new_transform.origin += constant_linear_velocity * delta_time;
-
- real_t ang_vel = constant_angular_velocity.length();
- if (!Math::is_zero_approx(ang_vel)) {
- Vector3 ang_vel_axis = constant_angular_velocity / ang_vel;
- Basis rot(ang_vel_axis, ang_vel * delta_time);
- new_transform.basis = rot * new_transform.basis;
- new_transform.orthonormalize();
- }
-
- if (sync_to_physics) {
- // Propagate transform change to node.
- set_global_transform(new_transform);
- } else {
- PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform);
-
- // Propagate transform change to node.
- set_ignore_transform_notification(true);
- set_global_transform(new_transform);
- set_ignore_transform_notification(false);
- _on_transform_changed();
- }
- } break;
}
}
-void StaticBody3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody3D::get_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody3D::get_constant_angular_velocity);
-
- ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody3D::set_kinematic_motion_enabled);
- ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody3D::is_kinematic_motion_enabled);
-
- ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override);
- ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override);
+void AnimatableBody3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &AnimatableBody3D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &AnimatableBody3D::is_sync_to_physics_enabled);
- ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody3D::set_sync_to_physics);
- ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody3D::is_sync_to_physics_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}
-StaticBody3D::StaticBody3D() :
- PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC) {
-}
-
-void StaticBody3D::_reload_physics_characteristics() {
- if (physics_material_override.is_null()) {
- PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, 0);
- PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, 1);
- } else {
- PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
- PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
- }
-}
-
-void StaticBody3D::_update_kinematic_motion() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- if (kinematic_motion && sync_to_physics) {
- set_only_update_transform_changes(true);
- set_notify_local_transform(true);
- } else {
- set_only_update_transform_changes(false);
- set_notify_local_transform(false);
- }
-
- bool needs_physics_process = false;
- if (kinematic_motion) {
- PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed));
+AnimatableBody3D::AnimatableBody3D() :
+ StaticBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) {
+ PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
- if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) {
- needs_physics_process = true;
- }
- } else {
- PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
- }
-
- set_physics_process_internal(needs_physics_process);
+ _update_kinematic_motion();
}
void RigidBody3D::_body_enter_tree(ObjectID p_id) {
@@ -582,25 +476,26 @@ struct _RigidBodyInOut {
int local_shape = 0;
};
-void RigidBody3D::_direct_state_changed(Object *p_state) {
-#ifdef DEBUG_ENABLED
- state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
-#else
- state = (PhysicsDirectBodyState3D *)p_state; //trust it
-#endif
+void RigidBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) {
+ RigidBody3D *body = (RigidBody3D *)p_instance;
+ body->_body_state_changed(p_state);
+}
+void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
set_ignore_transform_notification(true);
- set_global_transform(state->get_transform());
- linear_velocity = state->get_linear_velocity();
- angular_velocity = state->get_angular_velocity();
- inverse_inertia_tensor = state->get_inverse_inertia_tensor();
- if (sleeping != state->is_sleeping()) {
- sleeping = state->is_sleeping();
+ set_global_transform(p_state->get_transform());
+
+ linear_velocity = p_state->get_linear_velocity();
+ angular_velocity = p_state->get_angular_velocity();
+
+ inverse_inertia_tensor = p_state->get_inverse_inertia_tensor();
+
+ if (sleeping != p_state->is_sleeping()) {
+ sleeping = p_state->is_sleeping();
emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
}
- GDVIRTUAL_CALL(_integrate_forces, state);
+ GDVIRTUAL_CALL(_integrate_forces, p_state);
set_ignore_transform_notification(false);
_on_transform_changed();
@@ -617,18 +512,18 @@ void RigidBody3D::_direct_state_changed(Object *p_state) {
}
}
- _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(state->get_contact_count() * sizeof(_RigidBodyInOut));
+ _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut));
int toadd_count = 0; //state->get_contact_count();
RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction));
int toremove_count = 0;
//put the ones to add
- for (int i = 0; i < state->get_contact_count(); i++) {
- RID rid = state->get_contact_collider(i);
- ObjectID obj = state->get_contact_collider_id(i);
- int local_shape = state->get_contact_local_shape(i);
- int shape = state->get_contact_collider_shape(i);
+ for (int i = 0; i < p_state->get_contact_count(); i++) {
+ RID rid = p_state->get_contact_collider(i);
+ ObjectID obj = p_state->get_contact_collider_id(i);
+ int local_shape = p_state->get_contact_local_shape(i);
+ int shape = p_state->get_contact_collider_shape(i);
//bool found=false;
@@ -683,8 +578,6 @@ void RigidBody3D::_direct_state_changed(Object *p_state) {
contact_monitor->locked = false;
}
-
- state = nullptr;
}
void RigidBody3D::_notification(int p_what) {
@@ -738,6 +631,60 @@ real_t RigidBody3D::get_mass() const {
return mass;
}
+void RigidBody3D::set_inertia(const Vector3 &p_inertia) {
+ ERR_FAIL_COND(p_inertia.x < 0);
+ ERR_FAIL_COND(p_inertia.y < 0);
+ ERR_FAIL_COND(p_inertia.z < 0);
+
+ inertia = p_inertia;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia);
+}
+
+const Vector3 &RigidBody3D::get_inertia() const {
+ return inertia;
+}
+
+void RigidBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) {
+ if (center_of_mass_mode == p_mode) {
+ return;
+ }
+
+ center_of_mass_mode = p_mode;
+
+ switch (center_of_mass_mode) {
+ case CENTER_OF_MASS_MODE_AUTO: {
+ center_of_mass = Vector3();
+ PhysicsServer3D::get_singleton()->body_reset_mass_properties(get_rid());
+ if (inertia != Vector3()) {
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia);
+ }
+ } break;
+
+ case CENTER_OF_MASS_MODE_CUSTOM: {
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+ } break;
+ }
+}
+
+RigidBody3D::CenterOfMassMode RigidBody3D::get_center_of_mass_mode() const {
+ return center_of_mass_mode;
+}
+
+void RigidBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) {
+ if (center_of_mass == p_center_of_mass) {
+ return;
+ }
+
+ ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM);
+ center_of_mass = p_center_of_mass;
+
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+}
+
+const Vector3 &RigidBody3D::get_center_of_mass() const {
+ return center_of_mass;
+}
+
void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
if (physics_material_override.is_valid()) {
if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) {
@@ -787,25 +734,15 @@ real_t RigidBody3D::get_angular_damp() const {
}
void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) {
- Vector3 v = state ? state->get_linear_velocity() : linear_velocity;
Vector3 axis = p_axis.normalized();
- v -= axis * axis.dot(v);
- v += p_axis;
- if (state) {
- set_linear_velocity(v);
- } else {
- PhysicsServer3D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis);
- linear_velocity = v;
- }
+ linear_velocity -= axis * axis.dot(linear_velocity);
+ linear_velocity += p_axis;
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
}
void RigidBody3D::set_linear_velocity(const Vector3 &p_velocity) {
linear_velocity = p_velocity;
- if (state) {
- state->set_linear_velocity(linear_velocity);
- } else {
- PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
- }
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
}
Vector3 RigidBody3D::get_linear_velocity() const {
@@ -814,11 +751,7 @@ Vector3 RigidBody3D::get_linear_velocity() const {
void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) {
angular_velocity = p_velocity;
- if (state) {
- state->set_angular_velocity(angular_velocity);
- } else {
- PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity);
- }
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity);
}
Vector3 RigidBody3D::get_angular_velocity() const {
@@ -972,6 +905,15 @@ void RigidBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody3D::set_mass);
ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody3D::get_mass);
+ ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody3D::set_inertia);
+ ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody3D::get_inertia);
+
+ ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody3D::set_center_of_mass_mode);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody3D::get_center_of_mass_mode);
+
+ ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody3D::set_center_of_mass);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody3D::get_center_of_mass);
+
ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody3D::set_physics_material_override);
ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody3D::get_physics_material_override);
@@ -1025,7 +967,11 @@ void RigidBody3D::_bind_methods() {
GDVIRTUAL_BIND(_integrate_forces, "state");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass");
+ ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
@@ -1051,11 +997,22 @@ void RigidBody3D::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_STATIC);
BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
BIND_ENUM_CONSTANT(MODE_KINEMATIC);
+
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
+}
+
+void RigidBody3D::_validate_property(PropertyInfo &property) const {
+ if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) {
+ if (property.name == "center_of_mass") {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
}
RigidBody3D::RigidBody3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) {
- PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed));
+ PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
}
RigidBody3D::~RigidBody3D() {
@@ -1083,7 +1040,7 @@ bool CharacterBody3D::move_and_slide() {
bool was_on_floor = on_floor;
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
+ double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
@@ -1113,7 +1070,7 @@ bool CharacterBody3D::move_and_slide() {
PhysicsServer3D::MotionResult floor_result;
Set<RID> exclude;
exclude.insert(on_floor_body);
- if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, exclude)) {
+ if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, false, exclude)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
@@ -1174,7 +1131,7 @@ bool CharacterBody3D::move_and_slide() {
// Apply snap.
Transform3D gt = get_global_transform();
PhysicsServer3D::MotionResult result;
- if (move_and_collide(snap, result, margin, true, false)) {
+ if (move_and_collide(snap, result, margin, true, false, true)) {
bool apply = true;
if (up_direction != Vector3()) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
@@ -1229,8 +1186,11 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
on_ceiling = true;
} else {
on_wall = true;
- on_floor_body = p_result.collider;
- floor_velocity = p_result.collider_velocity;
+ // Don't apply wall velocity when the collider is a CharacterBody3D.
+ if (Object::cast_to<CharacterBody3D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) {
+ on_floor_body = p_result.collider;
+ floor_velocity = p_result.collider_velocity;
+ }
}
}
}
@@ -2249,23 +2209,19 @@ void PhysicalBone3D::_notification(int p_what) {
}
}
-void PhysicalBone3D::_direct_state_changed(Object *p_state) {
+void PhysicalBone3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) {
+ PhysicalBone3D *bone = (PhysicalBone3D *)p_instance;
+ bone->_body_state_changed(p_state);
+}
+
+void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
if (!simulate_physics || !_internal_simulate_physics) {
return;
}
- /// Update bone transform
-
- PhysicsDirectBodyState3D *state;
-
-#ifdef DEBUG_ENABLED
- state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
-#else
- state = (PhysicsDirectBodyState3D *)p_state; //trust it
-#endif
+ /// Update bone transform.
- Transform3D global_transform(state->get_transform());
+ Transform3D global_transform(p_state->get_transform());
set_ignore_transform_notification(true);
set_global_transform(global_transform);
@@ -2329,7 +2285,7 @@ void PhysicalBone3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset"), "set_body_offset", "get_body_offset");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
@@ -2727,7 +2683,7 @@ void PhysicalBone3D::_start_physics_simulation() {
set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC);
PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
- PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed));
+ PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
set_as_top_level(true);
_internal_simulate_physics = true;
}
@@ -2746,7 +2702,7 @@ void PhysicalBone3D::_stop_physics_simulation() {
PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0);
}
if (_internal_simulate_physics) {
- PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
+ PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr);
parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false);
set_as_top_level(false);
_internal_simulate_physics = false;
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 26b9a39047..8e6463f838 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_test_only = false, real_t p_margin = 0.001);
public:
- bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>());
+ bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
@@ -73,23 +73,13 @@ public:
class StaticBody3D : public PhysicsBody3D {
GDCLASS(StaticBody3D, PhysicsBody3D);
+private:
Vector3 constant_linear_velocity;
Vector3 constant_angular_velocity;
- Vector3 linear_velocity;
- Vector3 angular_velocity;
-
Ref<PhysicsMaterial> physics_material_override;
- bool kinematic_motion = false;
- bool sync_to_physics = false;
-
- Transform3D last_valid_transform;
-
- void _direct_state_changed(Object *p_state);
-
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
@@ -102,20 +92,38 @@ public:
Vector3 get_constant_linear_velocity() const;
Vector3 get_constant_angular_velocity() const;
- virtual Vector3 get_linear_velocity() const override;
- virtual Vector3 get_angular_velocity() const override;
+ StaticBody3D(PhysicsServer3D::BodyMode p_mode = PhysicsServer3D::BODY_MODE_STATIC);
- virtual TypedArray<String> get_configuration_warnings() const override;
+private:
+ void _reload_physics_characteristics();
+};
- StaticBody3D();
+class AnimatableBody3D : public StaticBody3D {
+ GDCLASS(AnimatableBody3D, StaticBody3D);
private:
- void _reload_physics_characteristics();
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
- void _update_kinematic_motion();
+ bool sync_to_physics = false;
- void set_kinematic_motion_enabled(bool p_enabled);
- bool is_kinematic_motion_enabled() const;
+ Transform3D last_valid_transform;
+
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
+ void _body_state_changed(PhysicsDirectBodyState3D *p_state);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual Vector3 get_linear_velocity() const override;
+ virtual Vector3 get_angular_velocity() const override;
+
+ AnimatableBody3D();
+
+private:
+ void _update_kinematic_motion();
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
@@ -132,14 +140,22 @@ public:
MODE_KINEMATIC,
};
+ enum CenterOfMassMode {
+ CENTER_OF_MASS_MODE_AUTO,
+ CENTER_OF_MASS_MODE_CUSTOM,
+ };
+
GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *)
protected:
bool can_sleep = true;
- PhysicsDirectBodyState3D *state = nullptr;
Mode mode = MODE_DYNAMIC;
real_t mass = 1.0;
+ Vector3 inertia;
+ CenterOfMassMode center_of_mass_mode = CENTER_OF_MASS_MODE_AUTO;
+ Vector3 center_of_mass;
+
Ref<PhysicsMaterial> physics_material_override;
Vector3 linear_velocity;
@@ -197,11 +213,14 @@ protected:
void _body_exit_tree(ObjectID p_id);
void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape);
- virtual void _direct_state_changed(Object *p_state);
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
+ virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state);
void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
+
public:
void set_mode(Mode p_mode);
Mode get_mode() const;
@@ -211,6 +230,15 @@ public:
virtual real_t get_inverse_mass() const override { return 1.0 / mass; }
+ void set_inertia(const Vector3 &p_inertia);
+ const Vector3 &get_inertia() const;
+
+ void set_center_of_mass_mode(CenterOfMassMode p_mode);
+ CenterOfMassMode get_center_of_mass_mode() const;
+
+ void set_center_of_mass(const Vector3 &p_center_of_mass);
+ const Vector3 &get_center_of_mass() const;
+
void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
Ref<PhysicsMaterial> get_physics_material_override() const;
@@ -271,6 +299,7 @@ private:
};
VARIANT_ENUM_CAST(RigidBody3D::Mode);
+VARIANT_ENUM_CAST(RigidBody3D::CenterOfMassMode);
class KinematicCollision3D;
@@ -525,7 +554,8 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _notification(int p_what);
- void _direct_state_changed(Object *p_state);
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
+ void _body_state_changed(PhysicsDirectBodyState3D *p_state);
static void _bind_methods();
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index 28ff3e9412..7eb189e890 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -355,6 +355,11 @@ void SoftBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient);
ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient);
+ ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform);
+
+ ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath()));
+ ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned);
+
ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable);
ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable);
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index a28382f4cb..b9a2736918 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1177,7 +1177,7 @@ void AnimatedSprite3D::stop() {
}
bool AnimatedSprite3D::is_playing() const {
- return is_processing();
+ return playing;
}
void AnimatedSprite3D::_reset_timeout() {
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index 92c0e09947..daeea81891 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -802,24 +802,21 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) {
}
}
-void VehicleBody3D::_direct_state_changed(Object *p_state) {
- RigidBody3D::_direct_state_changed(p_state);
+void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
+ RigidBody3D::_body_state_changed(p_state);
- state = Object::cast_to<PhysicsDirectBodyState3D>(p_state);
- ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument");
-
- real_t step = state->get_step();
+ real_t step = p_state->get_step();
for (int i = 0; i < wheels.size(); i++) {
- _update_wheel(i, state);
+ _update_wheel(i, p_state);
}
for (int i = 0; i < wheels.size(); i++) {
- _ray_cast(i, state);
- wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform);
+ _ray_cast(i, p_state);
+ wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform);
}
- _update_suspension(state);
+ _update_suspension(p_state);
for (int i = 0; i < wheels.size(); i++) {
//apply suspension force
@@ -831,20 +828,20 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) {
suspensionForce = wheel.m_maxSuspensionForce;
}
Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step;
- Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin;
+ Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - p_state->get_transform().origin;
- state->apply_impulse(impulse, relative_position);
+ p_state->apply_impulse(impulse, relative_position);
}
- _update_friction(state);
+ _update_friction(p_state);
for (int i = 0; i < wheels.size(); i++) {
VehicleWheel3D &wheel = *wheels[i];
- Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin;
- Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos);
+ Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - p_state->get_transform().origin;
+ Vector3 vel = p_state->get_linear_velocity() + (p_state->get_angular_velocity()).cross(relpos); // * mPos);
if (wheel.m_raycastInfo.m_isInContact) {
- const Transform3D &chassisWorldTransform = state->get_transform();
+ const Transform3D &chassisWorldTransform = p_state->get_transform();
Vector3 fwd(
chassisWorldTransform.basis[0][Vector3::AXIS_Z],
@@ -864,8 +861,6 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) {
wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact
}
-
- state = nullptr;
}
void VehicleBody3D::set_engine_force(real_t p_engine_force) {
@@ -926,7 +921,5 @@ void VehicleBody3D::_bind_methods() {
VehicleBody3D::VehicleBody3D() {
exclude.insert(get_rid());
- //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed));
-
set_mass(40);
}
diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h
index 2c10205ea3..f29c3d89b7 100644
--- a/scene/3d/vehicle_body_3d.h
+++ b/scene/3d/vehicle_body_3d.h
@@ -192,7 +192,8 @@ class VehicleBody3D : public RigidBody3D {
static void _bind_methods();
- void _direct_state_changed(Object *p_state) override;
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
+ virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state) override;
public:
void set_engine_force(real_t p_engine_force);
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 9383355292..f7b7604fd5 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -31,119 +31,18 @@
#include "audio_stream_player.h"
#include "core/config/engine.h"
-
-void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames, int p_amount) {
- int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
-
- AudioFrame *targets[4] = { nullptr, nullptr, nullptr, nullptr };
-
- if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
- targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
- } else {
- switch (mix_target) {
- case MIX_TARGET_STEREO: {
- targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
- } break;
- case MIX_TARGET_SURROUND: {
- for (int i = 0; i < AudioServer::get_singleton()->get_channel_count(); i++) {
- targets[i] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, i);
- }
- } break;
- case MIX_TARGET_CENTER: {
- targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1);
- } break;
- }
- }
-
- for (int c = 0; c < 4; c++) {
- if (!targets[c]) {
- break;
- }
- for (int i = 0; i < p_amount; i++) {
- targets[c][i] += p_frames[i];
- }
- }
-}
-
-void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
- //get data
- AudioFrame *buffer = mix_buffer.ptrw();
- int buffer_size = mix_buffer.size();
-
- if (p_fadeout) {
- // Short fadeout ramp
- buffer_size = MIN(buffer_size, 128);
- }
-
- stream_playback->mix(buffer, pitch_scale, buffer_size);
-
- //multiply volume interpolating to avoid clicks if this changes
- float target_volume = p_fadeout ? -80.0 : volume_db;
- float vol = Math::db2linear(mix_volume_db);
- float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
-
- for (int i = 0; i < buffer_size; i++) {
- buffer[i] *= vol;
- vol += vol_inc;
- }
-
- //set volume for next mix
- mix_volume_db = target_volume;
-
- _mix_to_bus(buffer, buffer_size);
-}
-
-void AudioStreamPlayer::_mix_audio() {
- if (use_fadeout) {
- _mix_to_bus(fadeout_buffer.ptr(), fadeout_buffer.size());
- use_fadeout = false;
- }
-
- if (!stream_playback.is_valid() || !active.is_set() ||
- (stream_paused && !stream_paused_fade)) {
- return;
- }
-
- if (stream_paused) {
- if (stream_paused_fade && stream_playback->is_playing()) {
- _mix_internal(true);
- stream_paused_fade = false;
- }
- return;
- }
-
- if (setstop.is_set()) {
- _mix_internal(true);
- stream_playback->stop();
- setstop.clear();
- }
-
- if (setseek.get() >= 0.0 && !stop_has_priority.is_set()) {
- if (stream_playback->is_playing()) {
- //fade out to avoid pops
- _mix_internal(true);
- }
-
- stream_playback->start(setseek.get());
- setseek.set(-1.0); //reset seek
- mix_volume_db = volume_db; //reset ramp
- }
-
- stop_has_priority.clear();
-
- _mix_internal(false);
-}
+#include "core/math/audio_frame.h"
+#include "servers/audio_server.h"
void AudioStreamPlayer::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- AudioServer::get_singleton()->add_callback(_mix_audios, this);
if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
}
}
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (!active.is_set() || (setseek.get() < 0 && !stream_playback->is_playing())) {
+ if (stream_playback.is_valid() && active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
active.clear();
set_process_internal(false);
emit_signal(SNAME("finished"));
@@ -151,7 +50,9 @@ void AudioStreamPlayer::_notification(int p_what) {
}
if (p_what == NOTIFICATION_EXIT_TREE) {
- AudioServer::get_singleton()->remove_callback(_mix_audios, this);
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playback);
+ }
}
if (p_what == NOTIFICATION_PAUSED) {
@@ -167,38 +68,10 @@ void AudioStreamPlayer::_notification(int p_what) {
}
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
- AudioServer::get_singleton()->lock();
-
- if (active.is_set() && stream_playback.is_valid() && !stream_paused) {
- //changing streams out of the blue is not a great idea, but at least
- //let's try to somehow avoid a click
-
- AudioFrame *buffer = fadeout_buffer.ptrw();
- int buffer_size = fadeout_buffer.size();
-
- stream_playback->mix(buffer, pitch_scale, buffer_size);
-
- //multiply volume interpolating to avoid clicks if this changes
- float target_volume = -80.0;
- float vol = Math::db2linear(mix_volume_db);
- float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
-
- for (int i = 0; i < buffer_size; i++) {
- buffer[i] *= vol;
- vol += vol_inc;
- }
-
- use_fadeout = true;
- }
-
- mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
-
if (stream_playback.is_valid()) {
+ stop();
stream_playback.unref();
stream.unref();
- active.clear();
- setseek.set(-1);
- setstop.clear();
}
if (p_stream.is_valid()) {
@@ -210,8 +83,6 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
}
}
- AudioServer::get_singleton()->unlock();
-
if (p_stream.is_valid() && stream_playback.is_null()) {
stream.unref();
}
@@ -223,6 +94,8 @@ Ref<AudioStream> AudioStreamPlayer::get_stream() const {
void AudioStreamPlayer::set_volume_db(float p_volume) {
volume_db = p_volume;
+
+ AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(stream_playback, _get_volume_vector());
}
float AudioStreamPlayer::get_volume_db() const {
@@ -232,6 +105,8 @@ float AudioStreamPlayer::get_volume_db() const {
void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(p_pitch_scale <= 0.0);
pitch_scale = p_pitch_scale;
+
+ AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
}
float AudioStreamPlayer::get_pitch_scale() const {
@@ -239,31 +114,32 @@ float AudioStreamPlayer::get_pitch_scale() const {
}
void AudioStreamPlayer::play(float p_from_pos) {
+ stop();
+ if (stream.is_valid()) {
+ stream_playback = stream->instance_playback();
+ }
if (stream_playback.is_valid()) {
- //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
- setseek.set(p_from_pos);
- stop_has_priority.clear();
+ AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
active.set();
- set_process_internal(true);
}
}
void AudioStreamPlayer::seek(float p_seconds) {
- if (stream_playback.is_valid()) {
- setseek.set(p_seconds);
+ if (stream_playback.is_valid() && active.is_set()) {
+ play(p_seconds);
}
}
void AudioStreamPlayer::stop() {
- if (stream_playback.is_valid() && active.is_set()) {
- setstop.set();
- stop_has_priority.set();
+ if (stream_playback.is_valid()) {
+ active.clear();
+ AudioServer::get_singleton()->stop_playback_stream(stream_playback);
}
}
bool AudioStreamPlayer::is_playing() const {
if (stream_playback.is_valid()) {
- return active.is_set() && !setstop.is_set(); //&& stream_playback->is_playing();
+ return AudioServer::get_singleton()->is_playback_active(stream_playback);
}
return false;
@@ -271,26 +147,22 @@ bool AudioStreamPlayer::is_playing() const {
float AudioStreamPlayer::get_playback_position() {
if (stream_playback.is_valid()) {
- float ss = setseek.get();
- if (ss >= 0.0) {
- return ss;
- }
- return stream_playback->get_playback_position();
+ return AudioServer::get_singleton()->get_playback_position(stream_playback);
}
return 0;
}
void AudioStreamPlayer::set_bus(const StringName &p_bus) {
- //if audio is active, must lock this
- AudioServer::get_singleton()->lock();
bus = p_bus;
- AudioServer::get_singleton()->unlock();
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, p_bus, _get_volume_vector());
+ }
}
StringName AudioStreamPlayer::get_bus() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
- if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
+ if (AudioServer::get_singleton()->get_bus_name(i) == String(bus)) {
return bus;
}
}
@@ -322,18 +194,61 @@ void AudioStreamPlayer::_set_playing(bool p_enable) {
}
bool AudioStreamPlayer::_is_active() const {
- return active.is_set();
+ if (stream_playback.is_valid()) {
+ return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ }
+ return false;
}
void AudioStreamPlayer::set_stream_paused(bool p_pause) {
- if (p_pause != stream_paused) {
- stream_paused = p_pause;
- stream_paused_fade = p_pause;
+ // TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
+ if (stream_playback.is_valid()) {
+ AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
}
}
bool AudioStreamPlayer::get_stream_paused() const {
- return stream_paused;
+ if (stream_playback.is_valid()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ }
+ return false;
+}
+
+Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() {
+ Vector<AudioFrame> volume_vector;
+ // We need at most four stereo pairs (for 7.1 systems).
+ volume_vector.resize(4);
+
+ // Initialize the volume vector to zero.
+ for (AudioFrame &channel_volume_db : volume_vector) {
+ channel_volume_db = AudioFrame(0, 0);
+ }
+
+ float volume_linear = Math::db2linear(volume_db);
+
+ // Set the volume vector up according to the speaker mode and mix target.
+ // TODO do we need to scale the volume down when we output to more channels?
+ if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
+ volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
+ } else {
+ switch (mix_target) {
+ case MIX_TARGET_STEREO: {
+ volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
+ } break;
+ case MIX_TARGET_SURROUND: {
+ // TODO Make sure this is right.
+ volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
+ volume_vector.write[1] = AudioFrame(volume_linear, /* LFE= */ 1.0f);
+ volume_vector.write[2] = AudioFrame(volume_linear, volume_linear);
+ volume_vector.write[3] = AudioFrame(volume_linear, volume_linear);
+ } break;
+ case MIX_TARGET_CENTER: {
+ // TODO Make sure this is right.
+ volume_vector.write[1] = AudioFrame(volume_linear, /* LFE= */ 1.0f);
+ } break;
+ }
+ }
+ return volume_vector;
}
void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
@@ -410,8 +325,6 @@ void AudioStreamPlayer::_bind_methods() {
}
AudioStreamPlayer::AudioStreamPlayer() {
- fadeout_buffer.resize(512);
-
AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer::_bus_layout_changed));
}
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index aa8d088be5..76b6698c9d 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -48,22 +48,13 @@ public:
private:
Ref<AudioStreamPlayback> stream_playback;
Ref<AudioStream> stream;
- Vector<AudioFrame> mix_buffer;
- Vector<AudioFrame> fadeout_buffer;
- bool use_fadeout = false;
- SafeNumeric<float> setseek{ -1.0 };
SafeFlag active;
- SafeFlag setstop;
- SafeFlag stop_has_priority;
- float mix_volume_db = 0.0;
float pitch_scale = 1.0;
float volume_db = 0.0;
bool autoplay = false;
- bool stream_paused = false;
- bool stream_paused_fade = false;
- StringName bus;
+ StringName bus = "Master";
MixTarget mix_target = MIX_TARGET_STEREO;
@@ -77,6 +68,8 @@ private:
void _bus_layout_changed();
void _mix_to_bus(const AudioFrame *p_frames, int p_amount);
+ Vector<AudioFrame> _get_volume_vector();
+
protected:
void _validate_property(PropertyInfo &property) const override;
void _notification(int p_what);
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index a2f1d2b15a..cf2df4e1a2 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -351,11 +351,11 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
Label *l = memnew(Label);
l->set_theme_type_variation("HeaderSmall");
l->set_text(p_label);
- add_child(l);
+ add_child(l, false, INTERNAL_MODE_FRONT);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
mc->add_child(p_control);
- add_child(mc);
+ add_child(mc, false, INTERNAL_MODE_FRONT);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
}
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index d93107df2d..411fb2e1f0 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -34,11 +34,13 @@
Size2 CheckBox::get_icon_size() const {
Ref<Texture2D> checked = Control::get_theme_icon(SNAME("checked"));
- Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled"));
Ref<Texture2D> unchecked = Control::get_theme_icon(SNAME("unchecked"));
- Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled"));
Ref<Texture2D> radio_checked = Control::get_theme_icon(SNAME("radio_checked"));
Ref<Texture2D> radio_unchecked = Control::get_theme_icon(SNAME("radio_unchecked"));
+ Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled"));
+ Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled"));
+ Ref<Texture2D> radio_checked_disabled = Control::get_theme_icon(SNAME("radio_checked_disabled"));
+ Ref<Texture2D> radio_unchecked_disabled = Control::get_theme_icon(SNAME("radio_unchecked_disabled"));
Size2 tex_size = Size2(0, 0);
if (!checked.is_null()) {
@@ -53,6 +55,18 @@ Size2 CheckBox::get_icon_size() const {
if (!radio_unchecked.is_null()) {
tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height()));
}
+ if (!checked_disabled.is_null()) {
+ tex_size = Size2(MAX(tex_size.width, checked_disabled->get_width()), MAX(tex_size.height, checked_disabled->get_height()));
+ }
+ if (!unchecked_disabled.is_null()) {
+ tex_size = Size2(MAX(tex_size.width, unchecked_disabled->get_width()), MAX(tex_size.height, unchecked_disabled->get_height()));
+ }
+ if (!radio_checked_disabled.is_null()) {
+ tex_size = Size2(MAX(tex_size.width, radio_checked_disabled->get_width()), MAX(tex_size.height, radio_checked_disabled->get_height()));
+ }
+ if (!radio_unchecked_disabled.is_null()) {
+ tex_size = Size2(MAX(tex_size.width, radio_unchecked_disabled->get_width()), MAX(tex_size.height, radio_unchecked_disabled->get_height()));
+ }
return tex_size;
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 5f3ab18cca..d05762b6c0 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -1400,7 +1400,8 @@ void CodeEdit::fold_line(int p_line) {
}
/* Find the last line to be hidden. */
- int end_line = get_line_count();
+ const int line_count = get_line_count() - 1;
+ int end_line = line_count;
int in_comment = is_in_comment(p_line);
int in_string = (in_comment == -1) ? is_in_string(p_line) : -1;
@@ -1408,7 +1409,7 @@ void CodeEdit::fold_line(int p_line) {
end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
/* End line is the same therefore we have a block. */
if (end_line == p_line) {
- for (int i = p_line + 1; i < get_line_count(); i++) {
+ for (int i = p_line + 1; i <= line_count; i++) {
if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
end_line = i - 1;
break;
@@ -1417,14 +1418,27 @@ void CodeEdit::fold_line(int p_line) {
}
} else {
int start_indent = get_indent_level(p_line);
- for (int i = p_line + 1; i < get_line_count(); i++) {
+ for (int i = p_line + 1; i <= line_count; i++) {
if (get_line(p_line).strip_edges().size() == 0 || is_in_string(i) != -1 || is_in_comment(i) != -1) {
end_line = i;
continue;
}
- if (get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0) {
+ if (i == line_count) {
+ /* Do not fold empty last line of script if any */
+ end_line = i;
+ if (get_line(i).strip_edges().size() == 0) {
+ end_line--;
+ }
+ break;
+ }
+
+ if ((get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0)) {
+ /* Keep an empty line unfolded if any */
end_line = i - 1;
+ if (get_line(i - 1).strip_edges().size() == 0 && i - 2 > p_line) {
+ end_line = i - 2;
+ }
break;
}
}
@@ -1937,7 +1951,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column() > 0 && get_caret_column() < get_line(caret_line).length()) {
pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column() + 1);
- if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
+ if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
remove_text(caret_line, get_caret_column() - 2, caret_line, get_caret_column());
if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1) != pre_brace_pair) {
set_caret_column(get_caret_column() - 1);
@@ -2744,7 +2758,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
/* If we have a space, previous word might be a keyword. eg "func |". */
} else if (cofs > 0 && line[cofs - 1] == ' ') {
int ofs = cofs - 1;
- while (ofs >= 0 && line[ofs] == ' ') {
+ while (ofs > 0 && line[ofs] == ' ') {
ofs--;
}
prev_is_word = _is_char(line[ofs]);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 0dddb2b09a..1afb0b8e9d 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -1139,7 +1139,7 @@ void ColorPicker::_bind_methods() {
ColorPicker::ColorPicker() :
BoxContainer(true) {
HBoxContainer *hb_edit = memnew(HBoxContainer);
- add_child(hb_edit);
+ add_child(hb_edit, false, INTERNAL_MODE_FRONT);
hb_edit->set_v_size_flags(SIZE_EXPAND_FILL);
hb_edit->add_child(uv_edit);
@@ -1151,7 +1151,7 @@ ColorPicker::ColorPicker() :
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
HBoxContainer *hb_smpl = memnew(HBoxContainer);
- add_child(hb_smpl);
+ add_child(hb_smpl, false, INTERNAL_MODE_FRONT);
hb_smpl->add_child(sample);
sample->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1165,12 +1165,12 @@ ColorPicker::ColorPicker() :
btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed));
VBoxContainer *vbl = memnew(VBoxContainer);
- add_child(vbl);
+ add_child(vbl, false, INTERNAL_MODE_FRONT);
- add_child(memnew(HSeparator));
+ add_child(memnew(HSeparator), false, INTERNAL_MODE_FRONT);
VBoxContainer *vbr = memnew(VBoxContainer);
- add_child(vbr);
+ add_child(vbr, false, INTERNAL_MODE_FRONT);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
for (int i = 0; i < 4; i++) {
@@ -1273,11 +1273,11 @@ ColorPicker::ColorPicker() :
set_pick_color(Color(1, 1, 1));
- add_child(preset_separator);
+ add_child(preset_separator, false, INTERNAL_MODE_FRONT);
preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
preset_container->set_columns(preset_column_count);
- add_child(preset_container);
+ add_child(preset_container, false, INTERNAL_MODE_FRONT);
btn_add_preset->set_icon_align(Button::ALIGN_CENTER);
btn_add_preset->set_tooltip(RTR("Add current color as a preset."));
@@ -1405,7 +1405,7 @@ void ColorPickerButton::_update_picker() {
picker = memnew(ColorPicker);
picker->set_anchors_and_offsets_preset(PRESET_WIDE);
popup->add_child(picker);
- add_child(popup);
+ add_child(popup, false, INTERNAL_MODE_FRONT);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));
popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 4ac6a58d36..81411d5844 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -30,6 +30,7 @@
#include "control.h"
+#include "container.h"
#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
#include "core/object/message_queue.h"
@@ -168,6 +169,20 @@ Size2 Control::_edit_get_minimum_size() const {
}
#endif
+String Control::properties_managed_by_container[] = {
+ "offset_left",
+ "offset_top",
+ "offset_right",
+ "offset_bottom",
+ "anchor_left",
+ "anchor_top",
+ "anchor_right",
+ "anchor_bottom",
+ "rect_position",
+ "rect_scale",
+ "rect_size"
+};
+
void Control::accept_event() {
if (is_inside_tree()) {
get_viewport()->_gui_accept_event();
@@ -442,6 +457,20 @@ void Control::_validate_property(PropertyInfo &property) const {
property.hint_string = hint_string;
}
+ if (!Object::cast_to<Container>(get_parent())) {
+ return;
+ }
+ // Disable the property if it's managed by the parent container.
+ bool property_is_managed_by_container = false;
+ for (unsigned i = 0; i < properties_managed_by_container_count; i++) {
+ property_is_managed_by_container = properties_managed_by_container[i] == property.name;
+ if (property_is_managed_by_container) {
+ break;
+ }
+ }
+ if (property_is_managed_by_container) {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
}
Control *Control::get_parent_control() const {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 0faa617f8d..9cec5d6e8d 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -230,6 +230,9 @@ private:
} data;
+ static constexpr unsigned properties_managed_by_container_count = 11;
+ static String properties_managed_by_container[properties_managed_by_container_count];
+
// used internally
Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index da858e8e83..5d98aaa698 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -319,7 +319,7 @@ AcceptDialog::AcceptDialog() {
set_clamp_to_embedder(true);
bg = memnew(Panel);
- add_child(bg);
+ add_child(bg, false, INTERNAL_MODE_FRONT);
hbc = memnew(HBoxContainer);
@@ -331,9 +331,9 @@ AcceptDialog::AcceptDialog() {
label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END);
label->set_begin(Point2(margin, margin));
label->set_end(Point2(-margin, -button_margin - 10));
- add_child(label);
+ add_child(label, false, INTERNAL_MODE_FRONT);
- add_child(hbc);
+ add_child(hbc, false, INTERNAL_MODE_FRONT);
hbc->add_spacer();
ok = memnew(Button);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 2512b24623..973b72973d 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -924,7 +924,7 @@ FileDialog::FileDialog() {
show_hidden_files = default_show_hidden_files;
vbox = memnew(VBoxContainer);
- add_child(vbox);
+ add_child(vbox, false, INTERNAL_MODE_FRONT);
vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed));
mode = FILE_MODE_SAVE_FILE;
@@ -1023,8 +1023,7 @@ FileDialog::FileDialog() {
filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected));
confirm_save = memnew(ConfirmationDialog);
- // confirm_save->set_as_top_level(true);
- add_child(confirm_save);
+ add_child(confirm_save, false, INTERNAL_MODE_FRONT);
confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed));
@@ -1036,16 +1035,16 @@ FileDialog::FileDialog() {
makedirname = memnew(LineEdit);
makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
makevb->add_margin_child(TTRC("Name:"), makedirname);
- add_child(makedialog);
+ add_child(makedialog, false, INTERNAL_MODE_FRONT);
makedialog->register_text_enter(makedirname);
makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm));
mkdirerr = memnew(AcceptDialog);
mkdirerr->set_text(TTRC("Could not create folder."));
- add_child(mkdirerr);
+ add_child(mkdirerr, false, INTERNAL_MODE_FRONT);
exterr = memnew(AcceptDialog);
exterr->set_text(TTRC("Must use a valid extension."));
- add_child(exterr);
+ add_child(exterr, false, INTERNAL_MODE_FRONT);
update_filters();
update_dir();
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index bbab3008e2..56b8a936e1 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -48,7 +48,7 @@ GradientEdit::GradientEdit() {
picker = memnew(ColorPicker);
popup->add_child(picker);
- add_child(popup);
+ add_child(popup, false, INTERNAL_MODE_FRONT);
}
int GradientEdit::_get_point_from_pos(int x) {
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 0281bc7efb..cabae9feb2 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -366,7 +366,6 @@ void GraphEdit::_graph_node_raised(Node *p_gn) {
}
move_child(connections_layer, first_not_comment);
- top_layer->raise();
emit_signal(SNAME("node_selected"), p_gn);
}
@@ -2246,14 +2245,14 @@ GraphEdit::GraphEdit() {
zoom_max = (1 * Math::pow(zoom_step, 4));
top_layer = memnew(GraphEditFilter(this));
- add_child(top_layer);
+ add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw));
top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input));
connections_layer = memnew(Control);
- add_child(connections_layer);
+ add_child(connections_layer, false, INTERNAL_MODE_FRONT);
connections_layer->connect("draw", callable_mp(this, &GraphEdit::_connections_layer_draw));
connections_layer->set_name("CLAYER");
connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offset
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 8297de9f30..d10ad90c1f 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -1656,7 +1656,7 @@ void ItemList::_bind_methods() {
ItemList::ItemList() {
scroll_bar = memnew(VScrollBar);
- add_child(scroll_bar);
+ add_child(scroll_bar, false, INTERNAL_MODE_FRONT);
scroll_bar->connect("value_changed", callable_mp(this, &ItemList::_scroll_changed));
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 50db1fc3ce..3f87003423 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -66,11 +66,11 @@ bool Label::is_uppercase() const {
int Label::get_line_height(int p_line) const {
Ref<Font> font = get_theme_font(SNAME("font"));
if (p_line >= 0 && p_line < lines_rid.size()) {
- return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
} else if (lines_rid.size() > 0) {
int h = 0;
for (int i = 0; i < lines_rid.size(); i++) {
- h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
}
return h;
} else {
@@ -89,7 +89,10 @@ void Label::_shape() {
} else {
TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction);
}
- TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, get_theme_font(SNAME("font"))->get_rids(), get_theme_font_size(SNAME("font_size")), opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ const Ref<Font> &font = get_theme_font(SNAME("font"));
+ int font_size = get_theme_font_size(SNAME("font_size"));
+ ERR_FAIL_COND(font.is_null());
+ TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text));
dirty = false;
lines_dirty = true;
@@ -217,7 +220,7 @@ void Label::_update_visible() {
minsize.height = 0;
int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
for (int64_t i = lines_skipped; i < last_line; i++) {
- minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -286,7 +289,7 @@ void Label::_notification(int p_what) {
// Get number of lines to fit to the height.
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -302,7 +305,7 @@ void Label::_notification(int p_what) {
// Get real total height.
total_h = 0;
for (int64_t i = lines_skipped; i < last_line; i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
}
total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
@@ -357,7 +360,7 @@ void Label::_notification(int p_what) {
for (int i = lines_skipped; i < last_line; i++) {
Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
ofs.x = 0;
- ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(Font::SPACING_TOP);
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
switch (align) {
case ALIGN_FILL:
if (rtl && autowrap_mode != AUTOWRAP_OFF) {
@@ -433,7 +436,7 @@ void Label::_notification(int p_what) {
}
}
}
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
}
}
@@ -455,7 +458,7 @@ Size2 Label::get_minimum_size() const {
Size2 min_size = minsize;
Ref<Font> font = get_theme_font(SNAME("font"));
- min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM));
+ min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
if (autowrap_mode != AUTOWRAP_OFF) {
@@ -486,7 +489,7 @@ int Label::get_visible_line_count() const {
int lines_visible = 0;
float total_h = 0.0;
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5e9c2d891f..d9acbeb828 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -641,7 +641,7 @@ void LineEdit::_notification(int p_what) {
int x_ofs = 0;
bool using_placeholder = text.is_empty() && ime_text.is_empty();
float text_width = TS->shaped_text_get_size(text_rid).x;
- float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
switch (align) {
case ALIGN_FILL:
@@ -674,7 +674,7 @@ void LineEdit::_notification(int p_what) {
int y_ofs = style->get_offset().y + (y_area - text_height) / 2;
Color selection_color = get_theme_color(SNAME("selection_color"));
- Color font_color = is_editable() ? get_theme_color(SNAME("font_color")) : get_theme_color(SNAME("font_uneditable_color"));
+ Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color"));
Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
Color caret_color = get_theme_color(SNAME("caret_color"));
@@ -1531,7 +1531,7 @@ Size2 LineEdit::get_minimum_size() const {
min_size.width = MAX(min_size.width, full_width + em_space_size);
}
- min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size));
+ min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM), font->get_height(font_size));
// Take icons into account.
bool using_placeholder = text.is_empty() && ime_text.is_empty();
@@ -1941,6 +1941,7 @@ void LineEdit::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
+ ERR_FAIL_COND(font.is_null());
TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
@@ -2219,7 +2220,7 @@ void LineEdit::_bind_methods() {
void LineEdit::_ensure_menu() {
if (!menu) {
menu = memnew(PopupMenu);
- add_child(menu);
+ add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
@@ -2304,7 +2305,7 @@ LineEdit::LineEdit() {
set_mouse_filter(MOUSE_FILTER_STOP);
caret_blink_timer = memnew(Timer);
- add_child(caret_blink_timer);
+ add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index e312aaed57..737ba84617 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -172,7 +172,7 @@ MenuButton::MenuButton() {
popup = memnew(PopupMenu);
popup->hide();
- add_child(popup);
+ add_child(popup, false, INTERNAL_MODE_FRONT);
popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(true));
popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false));
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index cd55f258b3..d16e96dbec 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -351,7 +351,7 @@ OptionButton::OptionButton() {
popup = memnew(PopupMenu);
popup->hide();
- add_child(popup);
+ add_child(popup, false, INTERNAL_MODE_FRONT);
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index f7e7e1cd60..e9414598a2 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -255,5 +255,5 @@ void PopupPanel::_notification(int p_what) {
PopupPanel::PopupPanel() {
panel = memnew(Panel);
- add_child(panel);
+ add_child(panel, false, INTERNAL_MODE_FRONT);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index f75d6755a8..c0a559e624 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -228,6 +228,7 @@ void PopupMenu::_activate_submenu(int p_over) {
// Set autohide areas
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
if (submenu_pum) {
+ submenu_pum->take_mouse_focus();
// Make the position of the parent popup relative to submenu popup
this_rect.position = this_rect.position - submenu_pum->get_position();
@@ -1689,7 +1690,7 @@ PopupMenu::PopupMenu() {
// Margin Container
margin_container = memnew(MarginContainer);
margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- add_child(margin_container);
+ add_child(margin_container, false, INTERNAL_MODE_FRONT);
margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
// Scroll Container
@@ -1703,7 +1704,7 @@ PopupMenu::PopupMenu() {
control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- scroll_container->add_child(control);
+ scroll_container->add_child(control, false, INTERNAL_MODE_FRONT);
control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
connect("window_input", callable_mp(this, &PopupMenu::gui_input));
@@ -1712,13 +1713,13 @@ PopupMenu::PopupMenu() {
submenu_timer->set_wait_time(0.3);
submenu_timer->set_one_shot(true);
submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
- add_child(submenu_timer);
+ add_child(submenu_timer, false, INTERNAL_MODE_FRONT);
minimum_lifetime_timer = memnew(Timer);
minimum_lifetime_timer->set_wait_time(0.3);
minimum_lifetime_timer->set_one_shot(true);
minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
- add_child(minimum_lifetime_timer);
+ add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT);
}
PopupMenu::~PopupMenu() {
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index e674b2f62f..f5506542bb 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -49,7 +49,7 @@ public:
Color color;
double elapsed_time = 0.0f;
Dictionary environment;
- uint32_t glpyh_index = 0;
+ uint32_t glyph_index = 0;
RID font;
CharFXTransform();
@@ -68,8 +68,8 @@ public:
Color get_color() { return color; }
void set_color(Color p_color) { color = p_color; }
- uint32_t get_glyph_index() const { return glpyh_index; };
- void set_glyph_index(uint32_t p_glpyh_index) { glpyh_index = p_glpyh_index; };
+ uint32_t get_glyph_index() const { return glyph_index; };
+ void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; };
RID get_font() const { return font; };
void set_font(RID p_font) { font = p_font; };
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 21a87d4e76..fbc67d8a24 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -874,7 +874,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->visibility = visible;
charfx->outline = true;
charfx->font = frid;
- charfx->glpyh_index = gl;
+ charfx->glyph_index = gl;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -884,7 +884,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
fx_offset += charfx->offset;
font_color = charfx->color;
frid = charfx->font;
- gl = charfx->glpyh_index;
+ gl = charfx->glyph_index;
visible &= charfx->visibility;
}
} else if (item_fx->type == ITEM_SHAKE) {
@@ -1026,7 +1026,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->visibility = visible;
charfx->outline = false;
charfx->font = frid;
- charfx->glpyh_index = gl;
+ charfx->glyph_index = gl;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -1036,7 +1036,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
fx_offset += charfx->offset;
font_color = charfx->color;
frid = charfx->font;
- gl = charfx->glpyh_index;
+ gl = charfx->glyph_index;
visible &= charfx->visibility;
}
} else if (item_fx->type == ITEM_SHAKE) {
@@ -4361,7 +4361,7 @@ RichTextLabel::RichTextLabel() {
current_frame = main;
vscroll = memnew(VScrollBar);
- add_child(vscroll);
+ add_child(vscroll, false, INTERNAL_MODE_FRONT);
vscroll->set_drag_node(String(".."));
vscroll->set_step(1);
vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index b5daa8b3f1..0c051f61e2 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -228,9 +228,6 @@ void ScrollContainer::_update_scrollbar_position() {
v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- h_scroll->raise();
- v_scroll->raise();
-
_updating_scrollbars = false;
}
@@ -618,12 +615,12 @@ void ScrollContainer::_bind_methods() {
ScrollContainer::ScrollContainer() {
h_scroll = memnew(HScrollBar);
h_scroll->set_name("_h_scroll");
- add_child(h_scroll);
+ add_child(h_scroll, false, INTERNAL_MODE_BACK);
h_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
v_scroll = memnew(VScrollBar);
v_scroll->set_name("_v_scroll");
- add_child(v_scroll);
+ add_child(v_scroll, false, INTERNAL_MODE_BACK);
v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 65a3eb3adf..1074d0d8a0 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -278,7 +278,7 @@ void SpinBox::_bind_methods() {
SpinBox::SpinBox() {
line_edit = memnew(LineEdit);
- add_child(line_edit);
+ add_child(line_edit, false, INTERNAL_MODE_FRONT);
line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -291,5 +291,5 @@ SpinBox::SpinBox() {
range_click_timer = memnew(Timer);
range_click_timer->connect("timeout", callable_mp(this, &SpinBox::_range_click_timeout));
- add_child(range_click_timer);
+ add_child(range_click_timer, false, INTERNAL_MODE_FRONT);
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 5884d2a854..137ce7e96f 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -434,14 +434,14 @@ void TabContainer::_notification(int p_what) {
}
int tab_width = tab_widths[i];
- if (get_tab_disabled(index)) {
+ if (index == current) {
+ x_current = x;
+ } else if (get_tab_disabled(index)) {
if (rtl) {
_draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
} else {
_draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x);
}
- } else if (index == current) {
- x_current = x;
} else {
if (rtl) {
_draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
@@ -459,12 +459,13 @@ void TabContainer::_notification(int p_what) {
panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
}
- // Draw selected tab in front. only draw selected tab when it's in visible range.
+ // Draw selected tab in front. Only draw selected tab when it's in visible range.
if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) {
+ Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected;
if (rtl) {
- _draw_tab(tab_selected, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
+ _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
} else {
- _draw_tab(tab_selected, font_selected_color, current, tabs_ofs_cache + x_current);
+ _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current);
}
}
@@ -640,8 +641,8 @@ void TabContainer::_on_mouse_exited() {
int TabContainer::_get_tab_width(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
- Control *control = Object::cast_to<Control>(_get_tabs()[p_index]);
- if (!control || control->is_set_as_top_level() || get_tab_hidden(p_index)) {
+ Control *control = get_tab_control(p_index);
+ if (!control || get_tab_hidden(p_index)) {
return 0;
}
@@ -905,7 +906,7 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
from_tabc->remove_child(moving_tabc);
- add_child(moving_tabc);
+ add_child(moving_tabc, false, INTERNAL_MODE_FRONT);
if (hover_now < 0) {
hover_now = get_tab_count() - 1;
}
@@ -1211,6 +1212,7 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled);
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
+ ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 61dfec6abe..b9e9a4d450 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -557,13 +557,25 @@ void TextEdit::_notification(int p_what) {
}
int minimap_draw_amount = minimap_visible_lines + get_line_wrap_count(minimap_line + 1);
- // draw the minimap
- Color viewport_color = (background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1);
+ // Draw the minimap.
+
+ // Add visual feedback when dragging or hovering the the visible area rectangle.
+ float viewport_alpha;
+ if (dragging_minimap) {
+ viewport_alpha = 0.25;
+ } else if (hovering_minimap) {
+ viewport_alpha = 0.175;
+ } else {
+ viewport_alpha = 0.1;
+ }
+
+ const Color viewport_color = (background_color.get_v() < 0.5) ? Color(1, 1, 1, viewport_alpha) : Color(0, 0, 0, viewport_alpha);
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, viewport_offset_y, minimap_width, viewport_height), viewport_color);
} else {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, minimap_width, viewport_height), viewport_color);
}
+
for (int i = 0; i < minimap_draw_amount; i++) {
minimap_line++;
@@ -888,7 +900,7 @@ void TextEdit::_notification(int p_what) {
// Draw line.
RID rid = ldata->get_line_rid(line_wrap_index);
- float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
if (rtl) {
char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
@@ -1549,6 +1561,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ if (draw_minimap && !dragging_selection) {
+ _update_minimap_hover();
+ }
+
if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
accept_event(); // Accept event if scroll changed.
}
@@ -5004,7 +5020,7 @@ void TextEdit::_paste_internal() {
void TextEdit::_generate_context_menu() {
if (!menu) {
menu = memnew(PopupMenu);
- add_child(menu);
+ add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
@@ -5012,7 +5028,7 @@ void TextEdit::_generate_context_menu() {
menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO);
menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR);
menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL);
- menu->add_child(menu_dir);
+ menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
menu_ctl->set_name("CTLMenu");
@@ -5034,7 +5050,7 @@ void TextEdit::_generate_context_menu() {
menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
- menu->add_child(menu_ctl);
+ menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
@@ -5644,6 +5660,33 @@ void TextEdit::_scroll_lines_down() {
}
// Minimap
+
+void TextEdit::_update_minimap_hover() {
+ const Point2 mp = get_local_mouse_pos();
+ const int xmargin_end = get_size().width - style_normal->get_margin(SIDE_RIGHT);
+
+ const bool hovering_sidebar = mp.x > xmargin_end - minimap_width && mp.x < xmargin_end;
+ if (!hovering_sidebar) {
+ if (hovering_minimap) {
+ // Only redraw if the hovering status changed.
+ hovering_minimap = false;
+ update();
+ }
+
+ // Return early to avoid running the operations below when not needed.
+ return;
+ }
+
+ const int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y));
+
+ const bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line();
+ if (new_hovering_minimap != hovering_minimap) {
+ // Only redraw if the hovering status changed.
+ hovering_minimap = new_hovering_minimap;
+ update();
+ }
+}
+
void TextEdit::_update_minimap_click() {
Point2 mp = get_local_mouse_pos();
@@ -5948,8 +5991,8 @@ TextEdit::TextEdit() {
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
- add_child(h_scroll);
- add_child(v_scroll);
+ add_child(h_scroll, false, INTERNAL_MODE_FRONT);
+ add_child(v_scroll, false, INTERNAL_MODE_FRONT);
h_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
@@ -5958,19 +6001,19 @@ TextEdit::TextEdit() {
/* Caret. */
caret_blink_timer = memnew(Timer);
- add_child(caret_blink_timer);
+ add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
/* Selection. */
click_select_held = memnew(Timer);
- add_child(click_select_held);
+ add_child(click_select_held, false, INTERNAL_MODE_FRONT);
click_select_held->set_wait_time(0.05);
click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held));
idle_detect = memnew(Timer);
- add_child(idle_detect);
+ add_child(idle_detect, false, INTERNAL_MODE_FRONT);
idle_detect->set_one_shot(true);
idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec"));
idle_detect->connect("timeout", callable_mp(this, &TextEdit::_push_current_op));
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index ced03e19d0..07999c2442 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -446,12 +446,14 @@ private:
// minimap scroll
bool minimap_clicked = false;
+ bool hovering_minimap = false;
bool dragging_minimap = false;
bool can_drag_minimap = false;
double minimap_scroll_ratio = 0.0;
double minimap_scroll_click_pos = 0.0;
+ void _update_minimap_hover();
void _update_minimap_click();
void _update_minimap_drag();
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 5e5dec3579..286f01ee33 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -100,6 +100,15 @@ Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
return progress;
}
+void TextureProgressBar::set_progress_offset(Point2 p_offset) {
+ progress_offset = p_offset;
+ update();
+}
+
+Point2 TextureProgressBar::get_progress_offset() const {
+ return progress_offset;
+}
+
void TextureProgressBar::set_tint_under(const Color &p_tint) {
tint_under = p_tint;
update();
@@ -360,6 +369,9 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
}
}
+ if (p_texture == progress) {
+ dst_rect.position += progress_offset;
+ }
p_texture->get_rect_region(dst_rect, src_rect, dst_rect, src_rect);
RID ci = get_canvas_item();
@@ -403,20 +415,24 @@ void TextureProgressBar::_notification(int p_what) {
Size2 s = progress->get_size();
switch (mode) {
case FILL_LEFT_TO_RIGHT: {
- Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region, tint_progress);
+ Rect2 region = Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y));
+ Rect2 source = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
+ draw_texture_rect_region(progress, region, source, tint_progress);
} break;
case FILL_RIGHT_TO_LEFT: {
- Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region, tint_progress);
+ Rect2 region = Rect2(progress_offset + Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
+ Rect2 source = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
+ draw_texture_rect_region(progress, region, source, tint_progress);
} break;
case FILL_TOP_TO_BOTTOM: {
- Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region, tint_progress);
+ Rect2 region = Rect2(progress_offset + Point2(), Size2(s.x, s.y * get_as_ratio()));
+ Rect2 source = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
+ draw_texture_rect_region(progress, region, source, tint_progress);
} break;
case FILL_BOTTOM_TO_TOP: {
- Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region, tint_progress);
+ Rect2 region = Rect2(progress_offset + Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
+ Rect2 source = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
+ draw_texture_rect_region(progress, region, source, tint_progress);
} break;
case FILL_CLOCKWISE:
case FILL_COUNTER_CLOCKWISE:
@@ -427,8 +443,9 @@ 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(progress, region, false, tint_progress);
+ Rect2 region = Rect2(progress_offset, s);
+ Rect2 source = Rect2(Point2(), s);
+ draw_texture_rect_region(progress, region, source, tint_progress);
} else if (val != 0) {
Array pts;
float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
@@ -454,14 +471,14 @@ void TextureProgressBar::_notification(int p_what) {
Vector<Point2> uvs;
Vector<Point2> points;
uvs.push_back(get_relative_center());
- points.push_back(Point2(s.x * get_relative_center().x, s.y * get_relative_center().y));
+ points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y));
for (int i = 0; i < pts.size(); i++) {
Point2 uv = unit_val_to_uv(pts[i]);
if (uvs.find(uv) >= 0) {
continue;
}
uvs.push_back(uv);
- points.push_back(Point2(uv.x * s.x, uv.y * s.y));
+ points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y));
}
Vector<Color> colors;
colors.push_back(tint_progress);
@@ -484,17 +501,19 @@ void TextureProgressBar::_notification(int p_what) {
}
} break;
case FILL_BILINEAR_LEFT_AND_RIGHT: {
- Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region, tint_progress);
+ Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
+ Rect2 source = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
+ draw_texture_rect_region(progress, region, source, tint_progress);
} break;
case FILL_BILINEAR_TOP_AND_BOTTOM: {
- 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);
+ Rect2 region = Rect2(progress_offset + Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio()));
+ Rect2 source = 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, source, 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);
+ draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
}
}
if (over.is_valid()) {
@@ -585,6 +604,9 @@ void TextureProgressBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over);
ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over);
+ ClassDB::bind_method(D_METHOD("set_texture_progress_offset", "offset"), &TextureProgressBar::set_progress_offset);
+ ClassDB::bind_method(D_METHOD("get_texture_progress_offset"), &TextureProgressBar::get_progress_offset);
+
ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle);
ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle);
@@ -604,6 +626,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::VECTOR2, "texture_progress_offset"), "set_texture_progress_offset", "get_texture_progress_offset");
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");
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index d147c43a26..c508f41387 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -61,6 +61,9 @@ public:
void set_fill_mode(int p_fill);
int get_fill_mode();
+ void set_progress_offset(Point2 p_offset);
+ Point2 get_progress_offset() const;
+
void set_radial_initial_angle(float p_angle);
float get_radial_initial_angle();
@@ -100,6 +103,7 @@ public:
private:
FillMode mode = FILL_LEFT_TO_RIGHT;
+ Point2 progress_offset;
float rad_init_angle = 0.0;
float rad_max_degrees = 360.0;
Point2 rad_center_off;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 5f07f5216a..cb990892ed 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -889,11 +889,22 @@ void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].custom_font = p_font;
}
+
Ref<Font> TreeItem::get_custom_font(int p_column) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Font>());
return cells[p_column].custom_font;
}
+void TreeItem::set_custom_font_size(int p_column, int p_font_size) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+ cells.write[p_column].custom_font_size = p_font_size;
+}
+
+int TreeItem::get_custom_font_size(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
+ return cells[p_column].custom_font_size;
+}
+
void TreeItem::set_tooltip(int p_column, const String &p_tooltip) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].tooltip = p_tooltip;
@@ -1132,6 +1143,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_font", "column", "font"), &TreeItem::set_custom_font);
ClassDB::bind_method(D_METHOD("get_custom_font", "column"), &TreeItem::get_custom_font);
+ ClassDB::bind_method(D_METHOD("set_custom_font_size", "column", "font_size"), &TreeItem::set_custom_font_size);
+ ClassDB::bind_method(D_METHOD("get_custom_font_size", "column"), &TreeItem::get_custom_font_size);
+
ClassDB::bind_method(D_METHOD("set_custom_bg_color", "column", "color", "just_outline"), &TreeItem::set_custom_bg_color, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_custom_bg_color", "column"), &TreeItem::clear_custom_bg_color);
ClassDB::bind_method(D_METHOD("get_custom_bg_color", "column"), &TreeItem::get_custom_bg_color);
@@ -1507,7 +1521,14 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
font = cache.font;
}
- p_item->cells.write[p_col].text_buf->add_string(valtext, font, cache.font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+
+ int font_size;
+ if (p_item->cells[p_col].custom_font_size > 0) {
+ font_size = p_item->cells[p_col].custom_font_size;
+ } else {
+ font_size = cache.font_size;
+ }
+ p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
@@ -1696,24 +1717,30 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
- if (drop_mode_flags && drop_mode_over == p_item) {
+ if (drop_mode_flags && drop_mode_over) {
Rect2 r = cell_rect;
- bool has_parent = p_item->get_first_child() != nullptr;
if (rtl) {
r.position.x = get_size().width - r.position.x - r.size.x;
}
-
- if (drop_mode_section == -1 || has_parent || drop_mode_section == 0) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color);
- }
-
- if (drop_mode_section == 0) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), cache.drop_position_color);
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color);
- }
-
- if ((drop_mode_section == 1 && !has_parent) || drop_mode_section == 0) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), cache.drop_position_color);
+ if (drop_mode_over == p_item) {
+ if (drop_mode_section == 0 || drop_mode_section == -1) {
+ // Line above.
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color);
+ }
+ if (drop_mode_section == 0) {
+ // Side lines.
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), cache.drop_position_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color);
+ }
+ if (drop_mode_section == 0 || (drop_mode_section == 1 && (!p_item->get_first_child() || p_item->is_collapsed()))) {
+ // Line below.
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), cache.drop_position_color);
+ }
+ } else if (drop_mode_over == p_item->get_parent()) {
+ if (drop_mode_section == 1 && !p_item->get_prev() /* && !drop_mode_over->is_collapsed() */) { // The drop_mode_over shouldn't ever be collapsed in here, otherwise we would be drawing a child of a collapsed item.
+ // Line above.
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color);
+ }
}
}
@@ -4768,12 +4795,11 @@ Tree::Tree() {
popup_menu = memnew(PopupMenu);
popup_menu->hide();
- add_child(popup_menu);
- // popup_menu->set_as_top_level(true);
+ add_child(popup_menu, false, INTERNAL_MODE_FRONT);
popup_editor = memnew(Popup);
popup_editor->set_wrap_controls(true);
- add_child(popup_editor);
+ add_child(popup_editor, false, INTERNAL_MODE_FRONT);
popup_editor_vb = memnew(VBoxContainer);
popup_editor->add_child(popup_editor_vb);
popup_editor_vb->add_theme_constant_override("separation", 0);
@@ -4791,12 +4817,12 @@ Tree::Tree() {
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
- add_child(h_scroll);
- add_child(v_scroll);
+ add_child(h_scroll, false, INTERNAL_MODE_FRONT);
+ add_child(v_scroll, false, INTERNAL_MODE_FRONT);
range_click_timer = memnew(Timer);
range_click_timer->connect("timeout", callable_mp(this, &Tree::_range_click_timeout));
- add_child(range_click_timer);
+ add_child(range_click_timer, false, INTERNAL_MODE_FRONT);
h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index cce8b527db..8b7ddc3faf 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -114,6 +114,7 @@ private:
Vector<Button> buttons;
Ref<Font> custom_font;
+ int custom_font_size = -1;
Cell() {
text_buf.instantiate();
@@ -299,6 +300,9 @@ public:
void set_custom_font(int p_column, const Ref<Font> &p_font);
Ref<Font> get_custom_font(int p_column) const;
+ void set_custom_font_size(int p_column, int p_font_size);
+ int get_custom_font_size(int p_column) const;
+
void set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline = false);
void clear_custom_bg_color(int p_column);
Color get_custom_bg_color(int p_column) const;
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 229b5384ea..8734037a57 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -129,7 +129,7 @@ void VideoPlayer::_mix_audio() {
void VideoPlayer::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
- AudioServer::get_singleton()->add_callback(_mix_audios, this);
+ AudioServer::get_singleton()->add_mix_callback(_mix_audios, this);
if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
@@ -138,8 +138,7 @@ void VideoPlayer::_notification(int p_notification) {
} break;
case NOTIFICATION_EXIT_TREE: {
- AudioServer::get_singleton()->remove_callback(_mix_audios, this);
-
+ AudioServer::get_singleton()->remove_mix_callback(_mix_audios, this);
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index f329332725..64b169b1fb 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -571,6 +571,12 @@ void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const
p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
+void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, double p_outline, double p_pixel_range) {
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL_COND(p_texture.is_null());
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range);
+}
+
void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@@ -881,6 +887,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box);
ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index a591cab485..01ed47d4dc 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -226,6 +226,7 @@ public:
void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
+ void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0);
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1);
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cafa4fa2d2..6ffc8475f5 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -48,6 +48,7 @@
#include <stdint.h>
VARIANT_ENUM_CAST(Node::ProcessMode);
+VARIANT_ENUM_CAST(Node::InternalMode);
int Node::orphan_node_count = 0;
@@ -122,28 +123,27 @@ void Node::_notification(int p_notification) {
}
} break;
case NOTIFICATION_READY: {
- if (get_script_instance()) {
- if (GDVIRTUAL_IS_OVERRIDDEN(_input)) {
- set_process_input(true);
- }
-
- if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
- set_process_unhandled_input(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_input)) {
+ set_process_input(true);
+ }
- if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) {
- set_process_unhandled_key_input(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
+ set_process_unhandled_input(true);
+ }
- if (GDVIRTUAL_IS_OVERRIDDEN(_process)) {
- set_process(true);
- }
- if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) {
- set_physics_process(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) {
+ set_process_unhandled_key_input(true);
+ }
- GDVIRTUAL_CALL(_ready);
+ if (GDVIRTUAL_IS_OVERRIDDEN(_process)) {
+ set_process(true);
}
+ if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) {
+ set_physics_process(true);
+ }
+
+ GDVIRTUAL_CALL(_ready);
+
if (data.filename.length()) {
ERR_FAIL_COND(!is_inside_tree());
get_multiplayer()->scene_enter_exit_notify(data.filename, this, true);
@@ -291,14 +291,40 @@ void Node::_propagate_exit_tree() {
void Node::move_child(Node *p_child, int p_pos) {
ERR_FAIL_NULL(p_child);
- ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos));
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
+
+ // We need to check whether node is internal and move it only in the relevant node range.
+ if (p_child->_is_internal_front()) {
+ ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos));
+ _move_child(p_child, p_pos);
+ } else if (p_child->_is_internal_back()) {
+ ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos));
+ _move_child(p_child, data.children.size() - data.internal_children_back + p_pos);
+ } else {
+ ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos));
+ _move_child(p_child, p_pos + data.internal_children_front);
+ }
+}
+
+void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup).");
// Specifying one place beyond the end
// means the same as moving to the last position
- if (p_pos == data.children.size()) {
- p_pos--;
+ if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly.
+ if (p_child->_is_internal_front()) {
+ if (p_pos == data.internal_children_front) {
+ p_pos--;
+ }
+ } else if (p_child->_is_internal_back()) {
+ if (p_pos == data.children.size()) {
+ p_pos--;
+ }
+ } else {
+ if (p_pos == data.children.size() - data.internal_children_back) {
+ p_pos--;
+ }
+ }
}
if (p_child->data.pos == p_pos) {
@@ -339,7 +365,14 @@ void Node::raise() {
return;
}
- data.parent->move_child(this, data.parent->data.children.size() - 1);
+ // Internal children move within a different index range.
+ if (_is_internal_front()) {
+ data.parent->move_child(this, data.parent->data.internal_children_front - 1);
+ } else if (_is_internal_back()) {
+ data.parent->move_child(this, data.parent->data.internal_children_back - 1);
+ } else {
+ data.parent->move_child(this, data.parent->get_child_count(false) - 1);
+ }
}
void Node::add_child_notify(Node *p_child) {
@@ -483,32 +516,32 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int
}
}
-void Node::set_network_master(int p_peer_id, bool p_recursive) {
- data.network_master = p_peer_id;
+void Node::set_network_authority(int p_peer_id, bool p_recursive) {
+ data.network_authority = p_peer_id;
if (p_recursive) {
for (int i = 0; i < data.children.size(); i++) {
- data.children[i]->set_network_master(p_peer_id, true);
+ data.children[i]->set_network_authority(p_peer_id, true);
}
}
}
-int Node::get_network_master() const {
- return data.network_master;
+int Node::get_network_authority() const {
+ return data.network_authority;
}
-bool Node::is_network_master() const {
+bool Node::is_network_authority() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer()->get_network_unique_id() == data.network_master;
+ return get_multiplayer()->get_network_unique_id() == data.network_authority;
}
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) {
+uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
for (int i = 0; i < data.rpc_methods.size(); i++) {
if (data.rpc_methods[i].name == p_method) {
- MultiplayerAPI::RPCConfig &nd = data.rpc_methods.write[i];
+ Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
nd.channel = p_channel;
@@ -516,7 +549,7 @@ uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_
}
}
// New method
- MultiplayerAPI::RPCConfig nd;
+ Multiplayer::RPCConfig nd;
nd.name = p_method;
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
@@ -609,7 +642,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->rpcp(this, p_peer_id, true, p_method, p_arg, p_argcount);
+ get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -630,7 +663,7 @@ void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
multiplayer = p_multiplayer;
}
-Vector<MultiplayerAPI::RPCConfig> Node::get_node_rpc_methods() const {
+Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
return data.rpc_methods;
}
@@ -1058,6 +1091,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
p_child->data.pos = data.children.size();
data.children.push_back(p_child);
p_child->data.parent = this;
+
+ if (data.internal_children_back > 0) {
+ _move_child(p_child, data.children.size() - data.internal_children_back - 1);
+ }
p_child->notification(NOTIFICATION_PARENTED);
if (data.tree) {
@@ -1070,7 +1107,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
add_child_notify(p_child);
}
-void Node::add_child(Node *p_child, bool p_legible_unique_name) {
+void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) {
ERR_FAIL_NULL(p_child);
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
@@ -1079,19 +1116,35 @@ void Node::add_child(Node *p_child, bool p_legible_unique_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.");
- /* Validate name */
_validate_child_name(p_child, p_legible_unique_name);
-
_add_child_nocheck(p_child, p_child->data.name);
+
+ if (p_internal == INTERNAL_MODE_FRONT) {
+ _move_child(p_child, data.internal_children_front);
+ data.internal_children_front++;
+ } else if (p_internal == INTERNAL_MODE_BACK) {
+ if (data.internal_children_back > 0) {
+ _move_child(p_child, data.children.size() - 1, true);
+ }
+ data.internal_children_back++;
+ }
}
void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
ERR_FAIL_NULL(p_sibling);
+ ERR_FAIL_NULL(data.parent);
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead.");
- get_parent()->add_child(p_sibling, p_legible_unique_name);
- get_parent()->move_child(p_sibling, this->get_index() + 1);
+ InternalMode internal = INTERNAL_MODE_DISABLED;
+ if (_is_internal_front()) { // The sibling will have the same internal status.
+ internal = INTERNAL_MODE_FRONT;
+ } else if (_is_internal_back()) {
+ internal = INTERNAL_MODE_BACK;
+ }
+
+ data.parent->add_child(p_sibling, p_legible_unique_name, internal);
+ data.parent->_move_child(p_sibling, get_index() + 1);
}
void Node::_propagate_validate_owner() {
@@ -1145,7 +1198,12 @@ void Node::remove_child(Node *p_child) {
ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name()));
//ERR_FAIL_COND( p_child->data.blocked > 0 );
- //if (data.scene) { does not matter
+ // If internal child, update the counter.
+ if (p_child->_is_internal_front()) {
+ data.internal_children_front--;
+ } else if (p_child->_is_internal_back()) {
+ data.internal_children_back--;
+ }
p_child->_set_tree(nullptr);
//}
@@ -1175,17 +1233,29 @@ void Node::remove_child(Node *p_child) {
}
}
-int Node::get_child_count() const {
- return data.children.size();
+int Node::get_child_count(bool p_include_internal) const {
+ if (p_include_internal) {
+ return data.children.size();
+ } else {
+ return data.children.size() - data.internal_children_front - data.internal_children_back;
+ }
}
-Node *Node::get_child(int p_index) const {
- if (p_index < 0) {
- p_index += data.children.size();
+Node *Node::get_child(int p_index, bool p_include_internal) const {
+ if (p_include_internal) {
+ if (p_index < 0) {
+ p_index += data.children.size();
+ }
+ ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
+ return data.children[p_index];
+ } else {
+ if (p_index < 0) {
+ p_index += data.children.size() - data.internal_children_front - data.internal_children_back;
+ }
+ ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr);
+ p_index += data.internal_children_front;
+ return data.children[p_index];
}
- ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
-
- return data.children[p_index];
}
Node *Node::_get_child_by_name(const StringName &p_name) const {
@@ -1717,7 +1787,13 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
data.blocked--;
}
-int Node::get_index() const {
+int Node::get_index(bool p_include_internal) const {
+ // p_include_internal = false doesn't make sense if the node is internal.
+ ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false.");
+
+ if (data.parent && !p_include_internal) {
+ return data.pos - data.parent->data.internal_children_front;
+ }
return data.pos;
}
@@ -2429,12 +2505,12 @@ void Node::queue_delete() {
}
}
-TypedArray<Node> Node::_get_children() const {
+TypedArray<Node> Node::_get_children(bool p_include_internal) const {
TypedArray<Node> arr;
- int cc = get_child_count();
+ int cc = get_child_count(p_include_internal);
arr.resize(cc);
for (int i = 0; i < cc; i++) {
- arr[i] = get_child(i);
+ arr[i] = get_child(i, p_include_internal);
}
return arr;
@@ -2581,11 +2657,11 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name);
ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name);
- ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name"), &Node::add_child, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0));
ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child);
- ClassDB::bind_method(D_METHOD("get_child_count"), &Node::get_child_count);
- ClassDB::bind_method(D_METHOD("get_children"), &Node::_get_children);
- ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child);
+ ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript.
+ ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node);
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);
ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null);
@@ -2609,7 +2685,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner);
ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner);
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
- ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
+ ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false));
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
@@ -2661,15 +2737,15 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready);
- ClassDB::bind_method(D_METHOD("set_network_master", "id", "recursive"), &Node::set_network_master, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_network_master"), &Node::get_network_master);
+ ClassDB::bind_method(D_METHOD("set_network_authority", "id", "recursive"), &Node::set_network_authority, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_network_authority"), &Node::get_network_authority);
- ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
+ ClassDB::bind_method(D_METHOD("is_network_authority"), &Node::is_network_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
@@ -2747,6 +2823,10 @@ void Node::_bind_methods() {
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT);
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK);
+
ADD_SIGNAL(MethodInfo("ready"));
ADD_SIGNAL(MethodInfo("renamed"));
ADD_SIGNAL(MethodInfo("tree_entered"));
diff --git a/scene/main/node.h b/scene/main/node.h
index a07accba2f..27d6958140 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -73,6 +73,12 @@ public:
NAME_CASING_SNAKE_CASE
};
+ enum InternalMode {
+ INTERNAL_MODE_DISABLED,
+ INTERNAL_MODE_FRONT,
+ INTERNAL_MODE_BACK,
+ };
+
struct Comparator {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
@@ -97,6 +103,8 @@ private:
Node *parent = nullptr;
Node *owner = nullptr;
Vector<Node *> children;
+ int internal_children_front = 0;
+ int internal_children_back = 0;
int pos = -1;
int depth = -1;
int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
@@ -119,8 +127,8 @@ private:
ProcessMode process_mode = PROCESS_MODE_INHERIT;
Node *process_owner = nullptr;
- int network_master = 1; // Server by default.
- Vector<MultiplayerAPI::RPCConfig> rpc_methods;
+ int network_authority = 1; // Server by default.
+ Vector<Multiplayer::RPCConfig> rpc_methods;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@@ -172,12 +180,15 @@ private:
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const;
- TypedArray<Node> _get_children() const;
+ TypedArray<Node> _get_children(bool p_include_internal = true) const;
Array _get_groups() const;
Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; }
+ _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
+
friend class SceneTree;
void _set_tree(SceneTree *p_tree);
@@ -284,12 +295,12 @@ public:
StringName get_name() const;
void set_name(const String &p_name);
- void add_child(Node *p_child, bool p_legible_unique_name = false);
+ void add_child(Node *p_child, bool p_legible_unique_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
void add_sibling(Node *p_sibling, bool p_legible_unique_name = false);
void remove_child(Node *p_child);
- int get_child_count() const;
- Node *get_child(int p_index) const;
+ int get_child_count(bool p_include_internal = true) const;
+ Node *get_child(int p_index, bool p_include_internal = true) const;
bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;
@@ -327,6 +338,7 @@ public:
int get_persistent_group_count() const;
void move_child(Node *p_child, int p_pos);
+ void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false);
void raise();
void set_owner(Node *p_owner);
@@ -334,7 +346,7 @@ public:
void get_owned_by(Node *p_by, List<Node *> *p_owned);
void remove_and_skip();
- int get_index() const;
+ int get_index(bool p_include_internal = true) const;
Ref<Tween> create_tween();
@@ -450,12 +462,12 @@ public:
bool is_displayed_folded() const;
/* NETWORK */
- void set_network_master(int p_peer_id, bool p_recursive = true);
- int get_network_master() const;
- bool is_network_master() const;
+ void set_network_authority(int p_peer_id, bool p_recursive = true);
+ int get_network_authority() const;
+ bool is_network_authority() const;
- uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
- Vector<MultiplayerAPI::RPCConfig> get_node_rpc_methods() const;
+ uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
+ Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel
void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); // RPC to specific peer(s), honors RPCMode, TransferMode, channel
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index b1b94ae910..19331c1906 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -31,7 +31,7 @@
#ifndef SCENE_TREE_H
#define SCENE_TREE_H
-#include "core/io/multiplayer_api.h"
+#include "core/multiplayer/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/self_list.h"
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 801c7cf86e..8ec8c193fb 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -46,12 +46,14 @@
#include "scene/gui/control.h"
#include "scene/gui/label.h"
#include "scene/gui/popup.h"
+#include "scene/gui/popup_menu.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/resources/mesh.h"
#include "scene/resources/text_line.h"
#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
+#include "servers/audio_server.h"
void ViewportTexture::setup_local_to_scene() {
if (vp) {
@@ -820,12 +822,7 @@ Rect2 Viewport::get_visible_rect() const {
}
void Viewport::_update_listener_2d() {
- /*
- if (is_inside_tree() && audio_listener_3d && (!get_parent() || (Object::cast_to<Control>(get_parent()) && Object::cast_to<Control>(get_parent())->is_visible_in_tree())))
- SpatialSound2DServer::get_singleton()->listener_set_space(internal_listener_2d, find_world_2d()->get_sound_space());
- else
- SpatialSound2DServer::get_singleton()->listener_set_space(internal_listener_2d, RID());
-*/
+ AudioServer::get_singleton()->notify_listener_changed();
}
void Viewport::set_as_audio_listener_2d(bool p_enable) {
@@ -1105,6 +1102,12 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont
while (p_control) {
tooltip = p_control->get_tooltip(pos);
+ //Temporary solution for PopupMenus
+ PopupMenu *menu = Object::cast_to<PopupMenu>(this);
+ if (menu) {
+ tooltip = menu->get_tooltip(pos);
+ }
+
if (r_tooltip_owner) {
*r_tooltip_owner = p_control;
}
@@ -3072,6 +3075,7 @@ bool Viewport::is_audio_listener_3d() const {
}
void Viewport::_update_listener_3d() {
+ AudioServer::get_singleton()->notify_listener_changed();
}
void Viewport::_listener_transform_3d_changed_notify() {
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index b9c0ae5a4a..ca5a3915d0 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -659,8 +659,8 @@ void Window::_update_viewport_size() {
if (!use_font_oversampling) {
font_oversampling = 1.0;
}
- if (TS->font_get_oversampling() != font_oversampling) {
- TS->font_set_oversampling(font_oversampling);
+ if (TS->font_get_global_oversampling() != font_oversampling) {
+ TS->font_set_global_oversampling(font_oversampling);
}
}
@@ -1497,7 +1497,7 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_screen"), "set_current_screen", "get_current_screen");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen");
ADD_GROUP("Flags", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2cea3c2337..25c8e3e0b7 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -160,6 +160,8 @@
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/resource_format_text.h"
#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/separation_ray_shape_2d.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/skeleton_modification_2d.h"
#include "scene/resources/skeleton_modification_2d_ccdik.h"
#include "scene/resources/skeleton_modification_2d_fabrik.h"
@@ -247,12 +249,6 @@
static Ref<ResourceFormatSaverText> resource_saver_text;
static Ref<ResourceFormatLoaderText> resource_loader_text;
-static Ref<ResourceFormatLoaderFont> resource_loader_font;
-
-#ifndef DISABLE_DEPRECATED
-static Ref<ResourceFormatLoaderCompatFont> resource_loader_compat_font;
-#endif /* DISABLE_DEPRECATED */
-
static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
@@ -267,14 +263,6 @@ void register_scene_types() {
Node::init_node_hrcr();
- resource_loader_font.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_font);
-
-#ifndef DISABLE_DEPRECATED
- resource_loader_compat_font.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_compat_font);
-#endif /* DISABLE_DEPRECATED */
-
resource_loader_stream_texture.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_stream_texture);
@@ -500,6 +488,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(CollisionObject3D);
GDREGISTER_VIRTUAL_CLASS(PhysicsBody3D);
GDREGISTER_CLASS(StaticBody3D);
+ GDREGISTER_CLASS(AnimatableBody3D);
GDREGISTER_CLASS(RigidBody3D);
GDREGISTER_CLASS(KinematicCollision3D);
GDREGISTER_CLASS(CharacterBody3D);
@@ -659,6 +648,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(CollisionObject2D);
GDREGISTER_VIRTUAL_CLASS(PhysicsBody2D);
GDREGISTER_CLASS(StaticBody2D);
+ GDREGISTER_CLASS(AnimatableBody2D);
GDREGISTER_CLASS(RigidBody2D);
GDREGISTER_CLASS(CharacterBody2D);
GDREGISTER_CLASS(KinematicCollision2D);
@@ -752,6 +742,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
GDREGISTER_VIRTUAL_CLASS(Shape3D);
+ GDREGISTER_CLASS(SeparationRayShape3D);
GDREGISTER_CLASS(SphereShape3D);
GDREGISTER_CLASS(BoxShape3D);
GDREGISTER_CLASS(CapsuleShape3D);
@@ -840,6 +831,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Shape2D);
GDREGISTER_CLASS(WorldMarginShape2D);
GDREGISTER_CLASS(SegmentShape2D);
+ GDREGISTER_CLASS(SeparationRayShape2D);
GDREGISTER_CLASS(CircleShape2D);
GDREGISTER_CLASS(RectangleShape2D);
GDREGISTER_CLASS(CapsuleShape2D);
@@ -960,6 +952,8 @@ void register_scene_types() {
ClassDB::add_compatibility_class("ProceduralSky", "Sky");
ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D");
ClassDB::add_compatibility_class("RayCast", "RayCast3D");
+ ClassDB::add_compatibility_class("RayShape", "SeparationRayShape3D");
+ ClassDB::add_compatibility_class("RayShape2D", "SeparationRayShape2D");
ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D");
ClassDB::add_compatibility_class("RigidBody", "RigidBody3D");
ClassDB::add_compatibility_class("Shape", "Shape3D");
@@ -1065,14 +1059,6 @@ void unregister_scene_types() {
SceneDebugger::deinitialize();
clear_default_theme();
- ResourceLoader::remove_resource_format_loader(resource_loader_font);
- resource_loader_font.unref();
-
-#ifndef DISABLE_DEPRECATED
- ResourceLoader::remove_resource_format_loader(resource_loader_compat_font);
- resource_loader_compat_font.unref();
-#endif /* DISABLE_DEPRECATED */
-
ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered);
resource_loader_texture_layered.unref();
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index ef070589e4..2ab9b7b5a4 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -221,12 +221,12 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds
}
}
-void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (!base->data || !active) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
- return;
+ return 0;
}
int len = base->data_bytes;
@@ -395,12 +395,15 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in
}
if (todo) {
+ int mixed_frames = p_frames - todo;
//bit was missing from mix
int todo_ofs = p_frames - todo;
for (int i = todo_ofs; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
+ return mixed_frames;
}
+ return p_frames;
}
AudioStreamPlaybackSample::AudioStreamPlaybackSample() {}
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 70b8ba79ad..8bf3d29123 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -73,7 +73,7 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
- virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
AudioStreamPlaybackSample();
};
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index dd7b348498..4845c556c6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -1020,8 +1020,9 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Ref<FontData> dynamic_font_data;
dynamic_font_data.instantiate();
- dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size);
+ dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
dynamic_font->add_data(dynamic_font_data);
+ dynamic_font->set_base_size(default_font_size);
default_font = dynamic_font;
}
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 032171847d..7af8e900fd 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -36,54 +36,129 @@
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
-void FontData::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16));
- ClassDB::bind_method(D_METHOD("new_bitmap", "height", "ascent", "base_size"), &FontData::new_bitmap);
-
- ClassDB::bind_method(D_METHOD("bitmap_add_texture", "texture"), &FontData::bitmap_add_texture);
- ClassDB::bind_method(D_METHOD("bitmap_add_char", "char", "texture_idx", "rect", "align", "advance"), &FontData::bitmap_add_char);
- ClassDB::bind_method(D_METHOD("bitmap_add_kerning_pair", "A", "B", "kerning"), &FontData::bitmap_add_kerning_pair);
+_FORCE_INLINE_ void FontData::_clear_cache() {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->free(cache[i]);
+ cache.write[i] = RID();
+ }
+ }
+}
- ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path);
- ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path);
+_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
+ if (unlikely(p_cache_index >= cache.size())) {
+ cache.resize(p_cache_index + 1);
+ }
+ if (unlikely(!cache[p_cache_index].is_valid())) {
+ cache.write[p_cache_index] = TS->create_font();
+ TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size);
+ TS->font_set_antialiased(cache[p_cache_index], antialiased);
+ TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf);
+ TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
+ TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
+ TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
+ TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
+ TS->font_set_hinting(cache[p_cache_index], hinting);
+ TS->font_set_oversampling(cache[p_cache_index], oversampling);
+ }
+}
- ClassDB::bind_method(D_METHOD("get_height", "size"), &FontData::get_height);
- ClassDB::bind_method(D_METHOD("get_ascent", "size"), &FontData::get_ascent);
- ClassDB::bind_method(D_METHOD("get_descent", "size"), &FontData::get_descent);
+void FontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data);
- ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position);
- ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness);
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &FontData::get_spacing);
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
+ ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
- ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
- ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased);
+ ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range);
+ ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range);
- ClassDB::bind_method(D_METHOD("get_variation_list"), &FontData::get_variation_list);
+ ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size);
+ ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size);
- ClassDB::bind_method(D_METHOD("set_variation", "tag", "value"), &FontData::set_variation);
- ClassDB::bind_method(D_METHOD("get_variation", "tag"), &FontData::get_variation);
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter);
ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
- ClassDB::bind_method(D_METHOD("set_force_autohinter", "enabled"), &FontData::set_force_autohinter);
- ClassDB::bind_method(D_METHOD("get_force_autohinter"), &FontData::get_force_autohinter);
+ ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
+ ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
- ClassDB::bind_method(D_METHOD("set_distance_field_hint", "distance_field"), &FontData::set_distance_field_hint);
- ClassDB::bind_method(D_METHOD("get_distance_field_hint"), &FontData::get_distance_field_hint);
+ ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache);
- ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
- ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
+ ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count);
+ ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache);
+ ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache);
+
+ ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list);
+ ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache);
+ ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache);
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates);
+
+ ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent);
+ ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent);
+
+ ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent);
+ ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent);
+
+ ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position);
+ ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position);
+
+ ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness);
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness);
+
+ ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale);
+ ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale);
+
+ ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing);
+
+ ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count);
+ ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures);
+ ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image);
+ ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image);
+
+ ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets);
+ ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list);
+ ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs);
+ ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance);
+ ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset);
+ ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size);
+ ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size);
- ClassDB::bind_method(D_METHOD("get_glyph_advance", "index", "size"), &FontData::get_glyph_advance);
- ClassDB::bind_method(D_METHOD("get_glyph_kerning", "index_a", "index_b", "size"), &FontData::get_glyph_kerning);
+ ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect);
+ ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect);
- ClassDB::bind_method(D_METHOD("get_base_size"), &FontData::get_base_size);
+ ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx);
+ ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx);
- ClassDB::bind_method(D_METHOD("has_outline"), &FontData::has_outline);
+ ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list);
+ ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map);
+ ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning);
+
+ ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning);
+ ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning);
+
+ ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range);
+ ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph);
+
+ ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid);
ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported);
ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override);
@@ -97,511 +172,915 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
- ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index, DEFVAL(0x0000));
- ClassDB::bind_method(D_METHOD("draw_glyph", "canvas", "size", "pos", "index", "color"), &FontData::draw_glyph, DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas", "size", "outline_size", "pos", "index", "color"), &FontData::draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.ttf,*.otf,*.woff,*.fnt,*.font"), "set_data_path", "get_data_path");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint");
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_glyph"), "set_spacing", "get_spacing", SPACING_GLYPH);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index);
- BIND_ENUM_CONSTANT(SPACING_GLYPH);
- BIND_ENUM_CONSTANT(SPACING_SPACE);
+ ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
+ ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
}
bool FontData::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("language_support_override/")) {
- String lang = str.get_slicec('/', 1);
- if (lang == "_new") {
- return false;
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ set_data(p_value);
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ set_antialiased(p_value);
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ set_multichannel_signed_distance_field(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ set_msdf_pixel_range(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ set_msdf_size(p_value);
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ set_fixed_size(p_value);
+ return true;
+ } else if (tokens[0] == "hinting") {
+ set_hinting((TextServer::Hinting)p_value.operator int());
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ set_force_autohinter(p_value);
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ set_oversampling(p_value);
+ return true;
}
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
set_language_support_override(lang, p_value);
return true;
- }
- if (str.begins_with("script_support_override/")) {
- String scr = str.get_slicec('/', 1);
- if (scr == "_new") {
- return false;
- }
- set_script_support_override(scr, p_value);
- return true;
- }
- if (str.begins_with("variation/")) {
- String name = str.get_slicec('/', 1);
- set_variation(name, p_value);
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ set_script_support_override(script, p_value);
return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ set_variation_coordinates(cache_index, p_value);
+ return true;
+ }
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ set_ascent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "descent") {
+ set_descent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ set_underline_position(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ set_underline_thickness(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "scale") {
+ set_scale(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ set_texture_image(cache_index, sz, texture_index, p_value);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ set_texture_offsets(cache_index, sz, texture_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ set_glyph_advance(cache_index, sz.x, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "offset") {
+ set_glyph_offset(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "size") {
+ set_glyph_size(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ set_glyph_uv_rect(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ set_glyph_texture_idx(cache_index, sz, glyph_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ set_kerning(cache_index, sz.x, gp, p_value);
+ return true;
+ }
+ }
}
-
return false;
}
bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("language_support_override/")) {
- String lang = str.get_slicec('/', 1);
- if (lang == "_new") {
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ r_ret = get_data();
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ r_ret = is_antialiased();
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ r_ret = is_multichannel_signed_distance_field();
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ r_ret = get_msdf_pixel_range();
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ r_ret = get_msdf_size();
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ r_ret = get_fixed_size();
+ return true;
+ } else if (tokens[0] == "hinting") {
+ r_ret = get_hinting();
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ r_ret = is_force_autohinter();
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ r_ret = get_oversampling();
return true;
}
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
r_ret = get_language_support_override(lang);
return true;
- }
- if (str.begins_with("script_support_override/")) {
- String scr = str.get_slicec('/', 1);
- if (scr == "_new") {
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ r_ret = get_script_support_override(script);
+ return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ r_ret = get_variation_coordinates(cache_index);
return true;
}
- r_ret = get_script_support_override(scr);
- return true;
- }
- if (str.begins_with("variation/")) {
- String name = str.get_slicec('/', 1);
-
- r_ret = get_variation(name);
- return true;
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ r_ret = get_ascent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "descent") {
+ r_ret = get_descent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ r_ret = get_underline_position(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ r_ret = get_underline_thickness(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "scale") {
+ r_ret = get_scale(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ r_ret = get_texture_image(cache_index, sz, texture_index);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ r_ret = get_texture_offsets(cache_index, sz, texture_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ r_ret = get_glyph_advance(cache_index, sz.x, glyph_index);
+ return true;
+ } else if (tokens[6] == "offset") {
+ r_ret = get_glyph_offset(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "size") {
+ r_ret = get_glyph_size(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ r_ret = get_kerning(cache_index, sz.x, gp);
+ return true;
+ }
+ }
}
-
return false;
}
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
Vector<String> lang_over = get_language_support_overrides();
for (int i = 0; i < lang_over.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
}
- p_list->push_back(PropertyInfo(Variant::NIL, "language_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-
Vector<String> scr_over = get_script_support_overrides();
for (int i = 0; i < scr_over.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ for (int i = 0; i < cache.size(); i++) {
+ String prefix = "cache/" + itos(i) + "/";
+ Array sizes = get_size_cache_list(i);
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ for (int j = 0; j < sizes.size(); j++) {
+ Vector2i sz = sizes[j];
+ String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/";
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
- Dictionary variations = get_variation_list();
- for (const Variant *ftr = variations.next(nullptr); ftr != nullptr; ftr = variations.next(ftr)) {
- Vector3i v = variations[*ftr];
- p_list->push_back(PropertyInfo(Variant::FLOAT, "variation/" + TS->tag_to_name(*ftr), PROPERTY_HINT_RANGE, itos(v.x) + "," + itos(v.y), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
+ int tx_cnt = get_texture_count(i, sz);
+ for (int k = 0; k < tx_cnt; k++) {
+ p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT));
+ }
+ Array glyphs = get_glyph_list(i, sz);
+ for (int k = 0; k < glyphs.size(); k++) {
+ const int32_t &gl = glyphs[k];
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ if (sz.y == 0) {
+ Array kerning_map = get_kerning_list(i, sz.x);
+ for (int k = 0; k < kerning_map.size(); k++) {
+ const Vector2i &gl_pair = kerning_map[k];
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ }
+ }
}
}
void FontData::reset_state() {
- if (rid != RID()) {
- TS->free(rid);
- }
- base_size = 16;
- path = String();
-}
+ _clear_cache();
+ data.clear();
+ data_ptr = nullptr;
+ data_size = 0;
+ cache.clear();
-RID FontData::get_rid() const {
- return rid;
+ antialiased = true;
+ msdf = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_LIGHT;
+ msdf_pixel_range = 14;
+ msdf_size = 128;
+ oversampling = 0.f;
}
-void FontData::load_resource(const String &p_filename, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+/*************************************************************************/
+
+void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) {
+ data.clear();
+ data_ptr = p_data;
+ data_size = p_size;
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
}
- rid = TS->create_font_resource(p_filename, p_base_size);
- path = p_filename;
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::_load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+void FontData::set_data(const PackedByteArray &p_data) {
+ data = p_data;
+ data_ptr = data.ptr();
+ data_size = data.size();
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
}
- rid = TS->create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size);
- path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data.ptr(), 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
- }
- rid = TS->create_font_memory(p_data, p_size, p_type, p_base_size);
- path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data, 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
+PackedByteArray FontData::get_data() const {
+ return data;
}
-void FontData::new_bitmap(float p_height, float p_ascent, int p_base_size) {
- if (rid != RID()) {
- TS->free(rid);
+void FontData::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ antialiased = p_antialiased;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_antialiased(cache[i], antialiased);
+ }
+ emit_changed();
}
- rid = TS->create_font_bitmap(p_height, p_ascent, p_base_size);
- path = TTR("(Bitmap: " + String::num_int64(rid.get_id(), 16, true) + ")");
- base_size = TS->font_get_base_size(rid);
- emit_changed();
}
-void FontData::bitmap_add_texture(const Ref<Texture> &p_texture) {
- if (rid != RID()) {
- TS->font_bitmap_add_texture(rid, p_texture);
- }
+bool FontData::is_antialiased() const {
+ return antialiased;
}
-void FontData::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- if (rid != RID()) {
- TS->font_bitmap_add_char(rid, p_char, p_texture_idx, p_rect, p_align, p_advance);
+void FontData::set_multichannel_signed_distance_field(bool p_msdf) {
+ if (msdf != p_msdf) {
+ msdf = p_msdf;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_multichannel_signed_distance_field(cache[i], msdf);
+ }
+ emit_changed();
}
}
-void FontData::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- if (rid != RID()) {
- TS->font_bitmap_add_kerning_pair(rid, p_A, p_B, p_kerning);
- }
+bool FontData::is_multichannel_signed_distance_field() const {
+ return msdf;
}
-void FontData::set_data_path(const String &p_path) {
- load_resource(p_path, base_size);
+void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) {
+ if (msdf_pixel_range != p_msdf_pixel_range) {
+ msdf_pixel_range = p_msdf_pixel_range;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_pixel_range(cache[i], msdf_pixel_range);
+ }
+ emit_changed();
+ }
}
-String FontData::get_data_path() const {
- return path;
+int FontData::get_msdf_pixel_range() const {
+ return msdf_pixel_range;
}
-float FontData::get_height(int p_size) const {
- if (rid == RID()) {
- return 0.f; // Do not raise errors in getters, to prevent editor from spamming errors on incomplete (without data_path set) fonts.
+void FontData::set_msdf_size(int p_msdf_size) {
+ if (msdf_size != p_msdf_size) {
+ msdf_size = p_msdf_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_size(cache[i], msdf_size);
+ }
+ emit_changed();
}
- return TS->font_get_height(rid, (p_size < 0) ? base_size : p_size);
}
-float FontData::get_ascent(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size);
+int FontData::get_msdf_size() const {
+ return msdf_size;
}
-float FontData::get_descent(int p_size) const {
- if (rid == RID()) {
- return 0.f;
+void FontData::set_fixed_size(int p_fixed_size) {
+ if (fixed_size != p_fixed_size) {
+ fixed_size = p_fixed_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_fixed_size(cache[i], fixed_size);
+ }
+ emit_changed();
}
- return TS->font_get_descent(rid, (p_size < 0) ? base_size : p_size);
}
-float FontData::get_underline_position(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_underline_position(rid, (p_size < 0) ? base_size : p_size);
+int FontData::get_fixed_size() const {
+ return fixed_size;
}
-Dictionary FontData::get_feature_list() const {
- if (rid == RID()) {
- return Dictionary();
+void FontData::set_force_autohinter(bool p_force_autohinter) {
+ if (force_autohinter != p_force_autohinter) {
+ force_autohinter = p_force_autohinter;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_force_autohinter(cache[i], force_autohinter);
+ }
+ emit_changed();
}
- return TS->font_get_feature_list(rid);
}
-float FontData::get_underline_thickness(int p_size) const {
- if (rid == RID()) {
- return 0.f;
- }
- return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size);
+bool FontData::is_force_autohinter() const {
+ return force_autohinter;
}
-Dictionary FontData::get_variation_list() const {
- if (rid == RID()) {
- return Dictionary();
+void FontData::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ hinting = p_hinting;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_hinting(cache[i], hinting);
+ }
+ emit_changed();
}
- return TS->font_get_variation_list(rid);
}
-void FontData::set_variation(const String &p_name, double p_value) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_variation(rid, p_name, p_value);
- emit_changed();
+TextServer::Hinting FontData::get_hinting() const {
+ return hinting;
}
-double FontData::get_variation(const String &p_name) const {
- if (rid == RID()) {
- return 0;
+void FontData::set_oversampling(real_t p_oversampling) {
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_oversampling(cache[i], oversampling);
+ }
+ emit_changed();
+ }
+}
+
+real_t FontData::get_oversampling() const {
+ return oversampling;
+}
+
+RID FontData::find_cache(const Dictionary &p_variation_coordinates) const {
+ // Find existing variation cache.
+ const Dictionary &supported_coords = get_supported_variation_list();
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]);
+ bool match = true;
+ for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) {
+ const Vector3 &def = supported_coords[*V];
+
+ real_t c_v = def.z;
+ if (cache_var.has(*V)) {
+ real_t val = cache_var[*V];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+ if (cache_var.has(TS->tag_to_name(*V))) {
+ real_t val = cache_var[TS->tag_to_name(*V)];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+
+ real_t s_v = def.z;
+ if (p_variation_coordinates.has(*V)) {
+ real_t val = p_variation_coordinates[*V];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+ if (p_variation_coordinates.has(TS->tag_to_name(*V))) {
+ real_t val = p_variation_coordinates[TS->tag_to_name(*V)];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+
+ match = match && (c_v == s_v);
+ }
+ if (match) {
+ return cache[i];
+ }
+ }
}
- return TS->font_get_variation(rid, p_name);
+
+ // Create new variation cache.
+ int idx = cache.size();
+ _ensure_rid(idx);
+ TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates);
+ return cache[idx];
}
-int FontData::get_spacing(int p_type) const {
- if (rid == RID()) {
- return 0;
- }
- if (p_type == SPACING_GLYPH) {
- return TS->font_get_spacing_glyph(rid);
- } else {
- return TS->font_get_spacing_space(rid);
- }
+int FontData::get_cache_count() const {
+ return cache.size();
}
-void FontData::set_spacing(int p_type, int p_value) {
- ERR_FAIL_COND(rid == RID());
- if (p_type == SPACING_GLYPH) {
- TS->font_set_spacing_glyph(rid, p_value);
- } else {
- TS->font_set_spacing_space(rid, p_value);
+void FontData::clear_cache() {
+ _clear_cache();
+ cache.clear();
+}
+
+void FontData::remove_cache(int p_cache_index) {
+ ERR_FAIL_INDEX(p_cache_index, cache.size());
+ if (cache[p_cache_index].is_valid()) {
+ TS->free(cache.write[p_cache_index]);
}
+ cache.remove(p_cache_index);
emit_changed();
}
-void FontData::set_antialiased(bool p_antialiased) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_antialiased(rid, p_antialiased);
- emit_changed();
+Array FontData::get_size_cache_list(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_size_cache_list(cache[p_cache_index]);
}
-bool FontData::get_antialiased() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_antialiased(rid);
+void FontData::clear_size_cache(int p_cache_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_size_cache(cache[p_cache_index]);
+}
+
+void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_size_cache(cache[p_cache_index], p_size);
}
-void FontData::set_distance_field_hint(bool p_distance_field) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_distance_field_hint(rid, p_distance_field);
+void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates);
emit_changed();
}
-bool FontData::get_distance_field_hint() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_distance_field_hint(rid);
+Dictionary FontData::get_variation_coordinates(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_variation_coordinates(cache[p_cache_index]);
}
-void FontData::set_hinting(TextServer::Hinting p_hinting) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_hinting(rid, p_hinting);
- emit_changed();
+void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent);
}
-TextServer::Hinting FontData::get_hinting() const {
- if (rid == RID()) {
- return TextServer::HINTING_NONE;
- }
- return TS->font_get_hinting(rid);
+real_t FontData::get_ascent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_ascent(cache[p_cache_index], p_size);
}
-void FontData::set_force_autohinter(bool p_enabeld) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_force_autohinter(rid, p_enabeld);
- emit_changed();
+void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_descent(cache[p_cache_index], p_size, p_descent);
}
-bool FontData::get_force_autohinter() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_force_autohinter(rid);
+real_t FontData::get_descent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_descent(cache[p_cache_index], p_size);
}
-bool FontData::has_char(char32_t p_char) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_has_char(rid, p_char);
+void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position);
}
-String FontData::get_supported_chars() const {
- ERR_FAIL_COND_V(rid == RID(), String());
- return TS->font_get_supported_chars(rid);
+real_t FontData::get_underline_position(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_position(cache[p_cache_index], p_size);
}
-Vector2 FontData::get_glyph_advance(uint32_t p_index, int p_size) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_get_glyph_advance(rid, p_index, (p_size < 0) ? base_size : p_size);
+void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness);
}
-Vector2 FontData::get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_get_glyph_kerning(rid, p_index_a, p_index_b, (p_size < 0) ? base_size : p_size);
+real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_thickness(cache[p_cache_index], p_size);
}
-bool FontData::has_outline() const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_has_outline(rid);
+void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_scale(cache[p_cache_index], p_size, p_scale);
}
-float FontData::get_base_size() const {
- return base_size;
+real_t FontData::get_scale(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_scale(cache[p_cache_index], p_size);
+}
+
+void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value);
+}
+
+int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing);
+}
+
+int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_count(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_textures(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index);
+}
+
+void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image);
+}
+
+Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index);
+}
+
+void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset);
+}
+
+PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index);
+}
+
+Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_list(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_glyphs(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance);
+}
+
+Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset);
+}
+
+Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size);
+}
+
+Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect);
+}
+
+Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph);
+}
+
+void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx);
+}
+
+int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph);
+}
+
+Array FontData::get_kerning_list(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning_list(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_kerning_map(int p_cache_index, int p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_kerning_map(cache[p_cache_index], p_size);
+}
+
+void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning);
+}
+
+Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end);
+}
+
+void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_glyph(cache[p_cache_index], p_size, p_index);
+}
+
+RID FontData::get_cache_rid(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return cache[p_cache_index];
}
bool FontData::is_language_supported(const String &p_language) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_is_language_supported(rid, p_language);
+ _ensure_rid(0);
+ return TS->font_is_language_supported(cache[0], p_language);
}
void FontData::set_language_support_override(const String &p_language, bool p_supported) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_language_support_override(rid, p_language, p_supported);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_set_language_support_override(cache[0], p_language, p_supported);
}
bool FontData::get_language_support_override(const String &p_language) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_language_support_override(rid, p_language);
+ _ensure_rid(0);
+ return TS->font_get_language_support_override(cache[0], p_language);
}
void FontData::remove_language_support_override(const String &p_language) {
- ERR_FAIL_COND(rid == RID());
- TS->font_remove_language_support_override(rid, p_language);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_remove_language_support_override(cache[0], p_language);
}
Vector<String> FontData::get_language_support_overrides() const {
- if (rid == RID()) {
- return Vector<String>();
- }
- return TS->font_get_language_support_overrides(rid);
+ _ensure_rid(0);
+ return TS->font_get_language_support_overrides(cache[0]);
}
bool FontData::is_script_supported(const String &p_script) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_is_script_supported(rid, p_script);
+ _ensure_rid(0);
+ return TS->font_is_script_supported(cache[0], p_script);
}
void FontData::set_script_support_override(const String &p_script, bool p_supported) {
- ERR_FAIL_COND(rid == RID());
- TS->font_set_script_support_override(rid, p_script, p_supported);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_set_script_support_override(cache[0], p_script, p_supported);
}
bool FontData::get_script_support_override(const String &p_script) const {
- if (rid == RID()) {
- return false;
- }
- return TS->font_get_script_support_override(rid, p_script);
+ _ensure_rid(0);
+ return TS->font_get_script_support_override(cache[0], p_script);
}
void FontData::remove_script_support_override(const String &p_script) {
- ERR_FAIL_COND(rid == RID());
- TS->font_remove_script_support_override(rid, p_script);
- emit_changed();
+ _ensure_rid(0);
+ TS->font_remove_script_support_override(cache[0], p_script);
}
Vector<String> FontData::get_script_support_overrides() const {
- if (rid == RID()) {
- return Vector<String>();
- }
- return TS->font_get_script_support_overrides(rid);
+ _ensure_rid(0);
+ return TS->font_get_script_support_overrides(cache[0]);
}
-uint32_t FontData::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const {
- ERR_FAIL_COND_V(rid == RID(), 0);
- return TS->font_get_glyph_index(rid, p_char, p_variation_selector);
+bool FontData::has_char(char32_t p_char) const {
+ _ensure_rid(0);
+ return TS->font_has_char(cache[0], p_char);
}
-Vector2 FontData::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_draw_glyph(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_pos, p_index, p_color);
+String FontData::get_supported_chars() const {
+ _ensure_rid(0);
+ return TS->font_get_supported_chars(cache[0]);
}
-Vector2 FontData::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND_V(rid == RID(), Vector2());
- return TS->font_draw_glyph_outline(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_outline_size, p_pos, p_index, p_color);
+int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ _ensure_rid(0);
+ return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector);
}
-FontData::FontData() {}
+Dictionary FontData::get_supported_feature_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_feature_list(cache[0]);
+}
-FontData::FontData(const String &p_filename, int p_base_size) {
- load_resource(p_filename, p_base_size);
+Dictionary FontData::get_supported_variation_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_variation_list(cache[0]);
}
-FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
- _load_memory(p_data, p_type, p_base_size);
+FontData::FontData() {
+ /* NOP */
}
FontData::~FontData() {
- if (rid != RID()) {
- TS->free(rid);
- }
+ _clear_cache();
}
/*************************************************************************/
+void Font::_data_changed() {
+ for (int i = 0; i < rids.size(); i++) {
+ rids.write[i] = RID();
+ }
+ emit_changed();
+}
+
+void Font::_ensure_rid(int p_index) const {
+ // Find or create cache record.
+ for (int i = 0; i < rids.size(); i++) {
+ if (!rids[i].is_valid() && data[i].is_valid()) {
+ rids.write[i] = data[i]->find_cache(variation_coordinates);
+ }
+ }
+}
+
void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data);
ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data);
ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count);
ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data);
+ ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid);
+ ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data);
ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
+ ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size);
+ ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size");
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates);
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates");
+
+ ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing);
+
+ ADD_GROUP("Extra Spacing", "spacing");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM);
+
ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
-
ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing);
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing);
-
- ClassDB::bind_method(D_METHOD("get_string_size", "text", "size"), &Font::get_string_size, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
- ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
-
ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
- ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
-
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
-
- BIND_ENUM_CONSTANT(SPACING_TOP);
- BIND_ENUM_CONSTANT(SPACING_BOTTOM);
-}
-
-void Font::_data_changed() {
- cache.clear();
- cache_wrap.clear();
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
- emit_changed();
- notify_property_list_changed();
+ ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
}
bool Font::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
+ Vector<String> tokens = p_name.operator String().split("/");
#ifndef DISABLE_DEPRECATED
- if (str == "font_data") { // Compatibility, DynamicFont main data
+ if (tokens.size() == 1 && tokens[0] == "font_data") {
+ // Compatibility, DynamicFont main data.
Ref<FontData> fd = p_value;
if (fd.is_valid()) {
add_data(fd);
return true;
}
return false;
- } else if (str.begins_with("fallback/")) { // Compatibility, DynamicFont fallback data
+ } else if (tokens.size() == 2 && tokens[0] == "fallback") {
+ // Compatibility, DynamicFont fallback data.
Ref<FontData> fd = p_value;
if (fd.is_valid()) {
add_data(fd);
return true;
}
return false;
- } else if (str == "fallback") { // Compatibility, BitmapFont fallback
+ } else if (tokens.size() == 1 && tokens[0] == "fallback") {
+ // Compatibility, BitmapFont fallback data.
Ref<Font> f = p_value;
if (f.is_valid()) {
for (int i = 0; i < f->get_data_count(); i++) {
@@ -612,10 +1091,9 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
#endif /* DISABLE_DEPRECATED */
- if (str.begins_with("data/")) {
- int idx = str.get_slicec('/', 1).to_int();
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
Ref<FontData> fd = p_value;
-
if (fd.is_valid()) {
if (idx == data.size()) {
add_data(fd);
@@ -631,14 +1109,13 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
}
-
return false;
}
bool Font::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("data/")) {
- int idx = str.get_slicec('/', 1).to_int();
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
if (idx == data.size()) {
r_ret = Ref<FontData>();
@@ -656,24 +1133,44 @@ void Font::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < data.size(); i++) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
-
p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
void Font::reset_state() {
- spacing_top = 0;
- spacing_bottom = 0;
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
cache.clear();
cache_wrap.clear();
data.clear();
+ rids.clear();
+
+ base_size = 16;
+ variation_coordinates.clear();
+ spacing_bottom = 0;
+ spacing_top = 0;
+}
+
+Dictionary Font::get_feature_list() const {
+ Dictionary out;
+ for (int i = 0; i < data.size(); i++) {
+ Dictionary data_ftrs = data[i]->get_supported_feature_list();
+ for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
+ out[*ftr] = data_ftrs[*ftr];
+ }
+ }
+ return out;
}
void Font::add_data(const Ref<FontData> &p_data) {
ERR_FAIL_COND(p_data.is_null());
data.push_back(p_data);
+ rids.push_back(RID());
if (data[data.size() - 1].is_valid()) {
- data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
cache.clear();
@@ -688,13 +1185,14 @@ void Font::set_data(int p_idx, const Ref<FontData> &p_data) {
ERR_FAIL_INDEX(p_idx, data.size());
if (data[p_idx].is_valid()) {
- data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
data.write[p_idx] = p_data;
+ rids.write[p_idx] = RID();
if (data[p_idx].is_valid()) {
- data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
cache.clear();
@@ -713,14 +1211,31 @@ Ref<FontData> Font::get_data(int p_idx) const {
return data[p_idx];
}
+RID Font::get_data_rid(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), RID());
+ _ensure_rid(p_idx);
+ return rids[p_idx];
+}
+
+void Font::clear_data() {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
+ data.clear();
+ rids.clear();
+}
+
void Font::remove_data(int p_idx) {
ERR_FAIL_INDEX(p_idx, data.size());
if (data[p_idx].is_valid()) {
- data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
data.remove(p_idx);
+ rids.remove(p_idx);
cache.clear();
cache_wrap.clear();
@@ -729,117 +1244,148 @@ void Font::remove_data(int p_idx) {
notify_property_list_changed();
}
-Dictionary Font::get_feature_list() const {
- Dictionary out;
- for (int i = 0; i < data.size(); i++) {
- Dictionary data_ftrs = data[i]->get_feature_list();
- for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
- out[*ftr] = data_ftrs[*ftr];
- }
+void Font::set_base_size(int p_size) {
+ base_size = p_size;
+}
+
+int Font::get_base_size() const {
+ return base_size;
+}
+
+void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) {
+ _data_changed();
+ variation_coordinates = p_variation_coordinates;
+}
+
+Dictionary Font::get_variation_coordinates() const {
+ return variation_coordinates;
+}
+
+void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) {
+ _data_changed();
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ spacing_top = p_value;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ spacing_bottom = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
+
+int Font::get_spacing(TextServer::SpacingType p_spacing) const {
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ return spacing_top;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ return spacing_bottom;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
}
- return out;
}
-float Font::get_height(int p_size) const {
- float ret = 0.f;
+real_t Font::get_height(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_height(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
}
- return ret + spacing_top + spacing_bottom;
+ return ret + spacing_bottom + spacing_top;
}
-float Font::get_ascent(int p_size) const {
- float ret = 0.f;
+real_t Font::get_ascent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_ascent(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size));
}
return ret + spacing_top;
}
-float Font::get_descent(int p_size) const {
- float ret = 0.f;
+real_t Font::get_descent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_descent(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_descent(rids[i], size));
}
return ret + spacing_bottom;
}
-float Font::get_underline_position(int p_size) const {
- float ret = 0.f;
+real_t Font::get_underline_position(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_underline_position(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_position(rids[i], size));
}
- return ret;
+ return ret + spacing_top;
}
-float Font::get_underline_thickness(int p_size) const {
- float ret = 0.f;
+real_t Font::get_underline_thickness(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
- ret = MAX(ret, data[i]->get_underline_thickness(p_size));
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size));
}
return ret;
}
-int Font::get_spacing(int p_type) const {
- if (p_type == SPACING_TOP) {
- return spacing_top;
- } else if (p_type == SPACING_BOTTOM) {
- return spacing_bottom;
- }
+Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const {
+ ERR_FAIL_COND_V(data.is_empty(), Size2());
- return 0;
-}
+ int size = (p_size <= 0) ? base_size : p_size;
-void Font::set_spacing(int p_type, int p_value) {
- if (p_type == SPACING_TOP) {
- spacing_top = p_value;
- } else if (p_type == SPACING_BOTTOM) {
- spacing_bottom = p_value;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
}
- emit_changed();
- notify_property_list_changed();
-}
-
-// Drawing string and string sizes, cached.
-
-Size2 Font::get_string_size(const String &p_text, int p_size) const {
- ERR_FAIL_COND_V(data.is_empty(), Size2());
-
uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
+ }
+ hash = hash_djb2_one_64(size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
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);
}
- if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- return buffer->get_size() + Vector2(0, spacing_top + spacing_bottom);
- } else {
- return buffer->get_size() + Vector2(spacing_top + spacing_bottom, 0);
- }
+ return buffer->get_size();
}
-Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint8_t p_flags) const {
+Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ int size = (p_size <= 0) ? base_size : p_size;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
+ uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
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);
lines_buffer->set_flags(p_flags);
@@ -851,40 +1397,50 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p
Size2 line_size = lines_buffer->get_line_size(i);
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
ret.x = MAX(ret.x, line_size.x);
- ret.y += line_size.y + spacing_top + spacing_bottom;
+ ret.y += line_size.y;
} else {
ret.y = MAX(ret.y, line_size.y);
- ret.x += line_size.x + spacing_top + spacing_bottom;
+ ret.x += line_size.x;
}
}
return ret;
}
-void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
+ }
+ hash = hash_djb2_one_64(size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
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);
}
Vector2 ofs = p_pos;
if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.y += spacing_top - buffer->get_line_ascent();
+ ofs.y -= buffer->get_line_ascent();
} else {
- ofs.x += spacing_top - buffer->get_line_ascent();
+ ofs.x -= buffer->get_line_ascent();
}
buffer->set_width(p_width);
buffer->set_align(p_align);
+ buffer->set_flags(p_flags);
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate);
@@ -895,18 +1451,22 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_size, hash);
+ int size = (p_size <= 0) ? base_size : p_size;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+
+ uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
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);
lines_buffer->set_flags(p_flags);
@@ -918,12 +1478,10 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
Vector2 lofs = p_pos;
for (int i = 0; i < lines_buffer->get_line_count(); i++) {
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- lofs.y += spacing_top;
if (i == 0) {
lofs.y -= lines_buffer->get_line_ascent(0);
}
} else {
- lofs.x += spacing_top;
if (i == 0) {
lofs.x -= lines_buffer->get_line_ascent(0);
}
@@ -939,9 +1497,9 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
Size2 line_size = lines_buffer->get_line_size(i);
if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
- lofs.y += line_size.y + spacing_bottom;
+ lofs.y += line_size.y;
} else {
- lofs.x += line_size.x + spacing_bottom;
+ lofs.x += line_size.x;
}
if ((p_max_lines > 0) && (i >= p_max_lines)) {
@@ -950,37 +1508,17 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
}
-bool Font::has_char(char32_t p_char) const {
- for (int i = 0; i < data.size(); i++) {
- if (data[i]->has_char(p_char)) {
- return true;
- }
- }
- return false;
-}
-
-String Font::get_supported_chars() const {
- String chars;
- for (int i = 0; i < data.size(); i++) {
- String data_chars = data[i]->get_supported_chars();
- for (int j = 0; j < data_chars.length(); j++) {
- if (chars.find_char(data_chars[j]) == -1) {
- chars += data_chars[j];
- }
- }
- }
- return chars;
-}
-
Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
- uint32_t glyph_a = data[i]->get_glyph_index(p_char);
- Size2 ret = Size2(data[i]->get_glyph_advance(glyph_a, size).x, data[i]->get_height(size));
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
if ((p_next != 0) && data[i]->has_char(p_next)) {
- uint32_t glyph_b = data[i]->get_glyph_index(p_next);
- ret.x -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
}
return ret;
}
@@ -988,35 +1526,55 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
return Size2();
}
-float Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
- uint32_t glyph_a = data[i]->get_glyph_index(p_char);
- float ret = data[i]->get_glyph_advance(glyph_a, size).x;
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x;
if ((p_next != 0) && data[i]->has_char(p_next)) {
- uint32_t glyph_b = data[i]->get_glyph_index(p_next);
- ret -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
}
+
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
- data[i]->draw_glyph_outline(p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
}
- data[i]->draw_glyph(p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate);
return ret;
}
}
return 0;
}
-Vector<RID> Font::get_rids() const {
- Vector<RID> ret;
+bool Font::has_char(char32_t p_char) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char))
+ return true;
+ }
+ return false;
+}
+
+String Font::get_supported_chars() const {
+ String chars;
for (int i = 0; i < data.size(); i++) {
- RID rid = data[i]->get_rid();
- if (rid != RID()) {
- ret.push_back(rid);
+ String data_chars = data[i]->get_supported_chars();
+ for (int j = 0; j < data_chars.length(); j++) {
+ if (chars.find_char(data_chars[j]) == -1) {
+ chars += data_chars[j];
+ }
}
}
- return ret;
+ return chars;
+}
+
+Vector<RID> Font::get_rids() const {
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+ return rids;
}
void Font::update_changes() {
@@ -1029,103 +1587,7 @@ Font::Font() {
}
Font::~Font() {
+ clear_data();
cache.clear();
cache_wrap.clear();
}
-
-/*************************************************************************/
-
-RES ResourceFormatLoaderFont::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) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<FontData> dfont;
- dfont.instantiate();
- dfont->load_resource(p_path);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return dfont;
-}
-
-void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
-#ifndef DISABLE_DEPRECATED
- if (p_type == "DynamicFontData") {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
- p_extensions->push_back("woff");
- return;
- }
- if (p_type == "BitmapFont") { // BitmapFont (*.font, *fnt) is handled by ResourceFormatLoaderCompatFont
- return;
- }
-#endif /* DISABLE_DEPRECATED */
- if (p_type == "" || handles_type(p_type)) {
- get_recognized_extensions(p_extensions);
- }
-}
-
-void ResourceFormatLoaderFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
- p_extensions->push_back("woff");
- p_extensions->push_back("font");
- p_extensions->push_back("fnt");
-}
-
-bool ResourceFormatLoaderFont::handles_type(const String &p_type) const {
- return (p_type == "FontData");
-}
-
-String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "ttf" || el == "otf" || el == "woff" || el == "font" || el == "fnt") {
- return "FontData";
- }
- return "";
-}
-
-#ifndef DISABLE_DEPRECATED
-
-RES ResourceFormatLoaderCompatFont::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) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<FontData> dfont;
- dfont.instantiate();
- dfont->load_resource(p_path);
-
- Ref<Font> font;
- font.instantiate();
- font->add_data(dfont);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return font;
-}
-
-void ResourceFormatLoaderCompatFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
- if (p_type == "BitmapFont") {
- p_extensions->push_back("font");
- p_extensions->push_back("fnt");
- }
-}
-
-void ResourceFormatLoaderCompatFont::get_recognized_extensions(List<String> *p_extensions) const {
-}
-
-bool ResourceFormatLoaderCompatFont::handles_type(const String &p_type) const {
- return (p_type == "Font");
-}
-
-String ResourceFormatLoaderCompatFont::get_resource_type(const String &p_path) const {
- return "";
-}
-
-#endif /* DISABLE_DEPRECATED */
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 200373aa8c..9a34edce64 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -41,17 +41,27 @@
class FontData : public Resource {
GDCLASS(FontData, Resource);
+ RES_BASE_EXTENSION("fontdata");
-public:
- enum SpacingType {
- SPACING_GLYPH,
- SPACING_SPACE,
- };
+ // Font source data.
+ const uint8_t *data_ptr = nullptr;
+ size_t data_size = 0;
+ PackedByteArray data;
-private:
- RID rid;
- int base_size = 16;
- String path;
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_pixel_range = 16;
+ int msdf_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ real_t oversampling = 0.f;
+
+ // Cache.
+ mutable Vector<RID> cache;
+
+ _FORCE_INLINE_ void _clear_cache();
+ _FORCE_INLINE_ void _ensure_rid(int p_cache_index) const;
protected:
static void _bind_methods();
@@ -63,79 +73,132 @@ protected:
virtual void reset_state() override;
public:
- virtual RID get_rid() const override;
+ // Font source data.
+ virtual void set_data_ptr(const uint8_t *p_data, size_t p_size);
+ virtual void set_data(const PackedByteArray &p_data);
+ virtual PackedByteArray get_data() const;
- void load_resource(const String &p_filename, int p_base_size = 16);
- void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16);
- void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16);
+ // Common properties.
+ virtual void set_antialiased(bool p_antialiased);
+ virtual bool is_antialiased() const;
- void new_bitmap(float p_height, float p_ascent, int p_base_size = 16);
+ virtual void set_multichannel_signed_distance_field(bool p_msdf);
+ virtual bool is_multichannel_signed_distance_field() const;
- void bitmap_add_texture(const Ref<Texture> &p_texture);
- void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance);
- void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning);
+ virtual void set_msdf_pixel_range(int p_msdf_pixel_range);
+ virtual int get_msdf_pixel_range() const;
- void set_data_path(const String &p_path);
- String get_data_path() const;
+ virtual void set_msdf_size(int p_msdf_size);
+ virtual int get_msdf_size() const;
- float get_height(int p_size) const;
- float get_ascent(int p_size) const;
- float get_descent(int p_size) const;
+ virtual void set_fixed_size(int p_fixed_size);
+ virtual int get_fixed_size() const;
- Dictionary get_feature_list() const;
- Dictionary get_variation_list() const;
+ virtual void set_force_autohinter(bool p_force_autohinter);
+ virtual bool is_force_autohinter() const;
- void set_variation(const String &p_name, double p_value);
- double get_variation(const String &p_name) const;
+ virtual void set_hinting(TextServer::Hinting p_hinting);
+ virtual TextServer::Hinting get_hinting() const;
- float get_underline_position(int p_size) const;
- float get_underline_thickness(int p_size) const;
+ virtual void set_oversampling(real_t p_oversampling);
+ virtual real_t get_oversampling() const;
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
+ // Cache.
+ virtual RID find_cache(const Dictionary &p_variation_coordinates) const;
- void set_antialiased(bool p_antialiased);
- bool get_antialiased() const;
+ virtual int get_cache_count() const;
+ virtual void clear_cache();
+ virtual void remove_cache(int p_cache_index);
- void set_distance_field_hint(bool p_distance_field);
- bool get_distance_field_hint() const;
+ virtual Array get_size_cache_list(int p_cache_index) const;
+ virtual void clear_size_cache(int p_cache_index);
+ virtual void remove_size_cache(int p_cache_index, const Vector2i &p_size);
- void set_force_autohinter(bool p_enabeld);
- bool get_force_autohinter() const;
+ virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates(int p_cache_index) const;
- void set_hinting(TextServer::Hinting p_hinting);
- TextServer::Hinting get_hinting() const;
+ virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent);
+ virtual real_t get_ascent(int p_cache_index, int p_size) const;
- bool has_char(char32_t p_char) const;
- String get_supported_chars() const;
+ virtual void set_descent(int p_cache_index, int p_size, real_t p_descent);
+ virtual real_t get_descent(int p_cache_index, int p_size) const;
- Vector2 get_glyph_advance(uint32_t p_index, int p_size) const;
- Vector2 get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const;
+ virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position);
+ virtual real_t get_underline_position(int p_cache_index, int p_size) const;
- bool has_outline() const;
- float get_base_size() const;
+ virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness);
+ virtual real_t get_underline_thickness(int p_cache_index, int p_size) const;
- bool is_language_supported(const String &p_language) const;
- void set_language_support_override(const String &p_language, bool p_supported);
- bool get_language_support_override(const String &p_language) const;
- void remove_language_support_override(const String &p_language);
- Vector<String> get_language_support_overrides() const;
+ virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts).
+ virtual real_t get_scale(int p_cache_index, int p_size) const;
- bool is_script_supported(const String &p_script) const;
- void set_script_support_override(const String &p_script, bool p_supported);
- bool get_script_support_override(const String &p_script) const;
- void remove_script_support_override(const String &p_script);
- Vector<String> get_script_support_overrides() const;
+ virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const;
- uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+ virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_textures(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index);
- Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
- Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
+ virtual void set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image);
+ virtual Ref<Image> get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
- FontData();
- FontData(const String &p_filename, int p_base_size);
- FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size);
+ virtual void set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset);
+ virtual PackedInt32Array get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
+
+ virtual Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_glyphs(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph);
+
+ virtual void set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance);
+ virtual Vector2 get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset);
+ virtual Vector2 get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size);
+ virtual Vector2 get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect);
+ virtual Rect2 get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx);
+ virtual int get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+ virtual Array get_kerning_list(int p_cache_index, int p_size) const;
+ virtual void clear_kerning_map(int p_cache_index, int p_size);
+ virtual void remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair);
+
+ virtual void set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning);
+ virtual Vector2 get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const;
+
+ virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end);
+ virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index);
+
+ virtual RID get_cache_rid(int p_cache_index) const;
+
+ // Language/script support override.
+ virtual bool is_language_supported(const String &p_language) const;
+ virtual void set_language_support_override(const String &p_language, bool p_supported);
+ virtual bool get_language_support_override(const String &p_language) const;
+ virtual void remove_language_support_override(const String &p_language);
+ virtual Vector<String> get_language_support_overrides() const;
+
+ virtual bool is_script_supported(const String &p_script) const;
+ virtual void set_script_support_override(const String &p_script, bool p_supported);
+ virtual bool get_script_support_override(const String &p_script) const;
+ virtual void remove_script_support_override(const String &p_script);
+ virtual Vector<String> get_script_support_overrides() const;
+
+ // Base font properties.
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
+
+ virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+
+ virtual Dictionary get_supported_feature_list() const;
+ virtual Dictionary get_supported_variation_list() const;
+
+ FontData();
~FontData();
};
@@ -147,20 +210,22 @@ class TextParagraph;
class Font : public Resource {
GDCLASS(Font, Resource);
-public:
- enum SpacingType {
- SPACING_TOP,
- SPACING_BOTTOM,
- };
-
-private:
- int spacing_top = 0;
- int spacing_bottom = 0;
-
+ // Shaped string cache.
mutable LRUCache<uint64_t, Ref<TextLine>> cache;
mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
+ // Font data cache.
Vector<Ref<FontData>> data;
+ mutable Vector<RID> rids;
+
+ // Font config.
+ int base_size = 16;
+ Dictionary variation_coordinates;
+ int spacing_bottom = 0;
+ int spacing_top = 0;
+
+ _FORCE_INLINE_ void _data_changed();
+ _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record.
protected:
static void _bind_methods();
@@ -171,41 +236,49 @@ protected:
virtual void reset_state() override;
- void _data_changed();
-
public:
Dictionary get_feature_list() const;
- // Font data control.
- void add_data(const Ref<FontData> &p_data);
- void set_data(int p_idx, const Ref<FontData> &p_data);
- int get_data_count() const;
- Ref<FontData> get_data(int p_idx) const;
- void remove_data(int p_idx);
+ // Font data.
+ virtual void add_data(const Ref<FontData> &p_data);
+ virtual void set_data(int p_idx, const Ref<FontData> &p_data);
+ virtual int get_data_count() const;
+ virtual Ref<FontData> get_data(int p_idx) const;
+ virtual RID get_data_rid(int p_idx) const;
+ virtual void clear_data();
+ virtual void remove_data(int p_idx);
+
+ // Font configuration.
+ virtual void set_base_size(int p_size);
+ virtual int get_base_size() const;
- float get_height(int p_size = -1) const;
- float get_ascent(int p_size = -1) const;
- float get_descent(int p_size = -1) const;
+ virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates() const;
- float get_underline_position(int p_size = -1) const;
- float get_underline_thickness(int p_size = -1) const;
+ virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(TextServer::SpacingType p_spacing) const;
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
+ // Font metrics.
+ virtual real_t get_height(int p_size = -1) const;
+ virtual real_t get_ascent(int p_size = -1) const;
+ virtual real_t get_descent(int p_size = -1) const;
+ virtual real_t get_underline_position(int p_size = -1) const;
+ virtual real_t get_underline_thickness(int p_size = -1) const;
// Drawing string.
- Size2 get_string_size(const String &p_text, int p_size = -1) const;
- Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
- bool has_char(char32_t p_char) const;
- String get_supported_chars() const;
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
- Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
+ // Drawing char.
+ virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
+ virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
Vector<RID> get_rids() const;
@@ -215,31 +288,4 @@ public:
~Font();
};
-VARIANT_ENUM_CAST(FontData::SpacingType);
-VARIANT_ENUM_CAST(Font::SpacingType);
-
-/*************************************************************************/
-
-class ResourceFormatLoaderFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#ifndef DISABLE_DEPRECATED
-
-class ResourceFormatLoaderCompatFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#endif /* DISABLE_DEPRECATED */
-
#endif /* FONT_H */
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 54bfc427c4..e74f759855 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -379,10 +379,17 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
return OK;
}
+ bool is_editable_instance = false;
+
// 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));
+ // Node is the root of an editable instance.
+ is_editable_instance = true;
+ } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ // Node is part of an editable instance.
+ is_editable_instance = true;
}
NodeData nd;
@@ -610,7 +617,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
- if (pack_state_stack.is_empty()) {
+ if (pack_state_stack.is_empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map);
} else {
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index dbe118a262..b863a309c0 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -424,7 +424,7 @@ Error ResourceLoaderText::load() {
}
}
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -768,7 +768,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
}
}
- if (!using_uid && path.find("://") == -1 && path.is_rel_path()) {
+ if (!using_uid && path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp
new file mode 100644
index 0000000000..0acd6d268d
--- /dev/null
+++ b/scene/resources/separation_ray_shape_2d.cpp
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* separation_ray_shape_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "separation_ray_shape_2d.h"
+
+#include "servers/physics_server_2d.h"
+#include "servers/rendering_server.h"
+
+void SeparationRayShape2D::_update_shape() {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d);
+ emit_changed();
+}
+
+void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+ const Vector2 target_position = Vector2(0, get_length());
+
+ const float max_arrow_size = 6;
+ const float line_width = 1.4;
+ bool no_line = target_position.length() < line_width;
+ float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size);
+
+ if (no_line) {
+ arrow_size = target_position.length();
+ } else {
+ RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width);
+ }
+
+ Transform2D xf;
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+
+ Vector<Vector2> pts;
+ pts.push_back(xf.xform(Vector2(arrow_size, 0)));
+ pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
+ pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++) {
+ cols.push_back(p_color);
+ }
+
+ RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID());
+}
+
+Rect2 SeparationRayShape2D::get_rect() const {
+ Rect2 rect;
+ rect.position = Vector2();
+ rect.expand_to(Vector2(0, length));
+ rect = rect.grow(Math_SQRT12 * 4);
+ return rect;
+}
+
+real_t SeparationRayShape2D::get_enclosing_radius() const {
+ return length;
+}
+
+void SeparationRayShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape2D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape2D::get_length);
+
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
+}
+
+void SeparationRayShape2D::set_length(real_t p_length) {
+ length = p_length;
+ _update_shape();
+}
+
+real_t SeparationRayShape2D::get_length() const {
+ return length;
+}
+
+void SeparationRayShape2D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
+ _update_shape();
+}
+
+bool SeparationRayShape2D::get_slide_on_slope() const {
+ return slide_on_slope;
+}
+
+SeparationRayShape2D::SeparationRayShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->separation_ray_shape_create()) {
+ _update_shape();
+}
diff --git a/scene/resources/separation_ray_shape_2d.h b/scene/resources/separation_ray_shape_2d.h
new file mode 100644
index 0000000000..5b74e6c727
--- /dev/null
+++ b/scene/resources/separation_ray_shape_2d.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* separation_ray_shape_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SEPARATION_RAY_SHAPE_2D_H
+#define SEPARATION_RAY_SHAPE_2D_H
+
+#include "scene/resources/shape_2d.h"
+
+class SeparationRayShape2D : public Shape2D {
+ GDCLASS(SeparationRayShape2D, Shape2D);
+
+ real_t length = 20.0;
+ bool slide_on_slope = false;
+
+ void _update_shape();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_length(real_t p_length);
+ real_t get_length() const;
+
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
+
+ virtual void draw(const RID &p_to_rid, const Color &p_color) override;
+ virtual Rect2 get_rect() const override;
+ virtual real_t get_enclosing_radius() const override;
+
+ SeparationRayShape2D();
+};
+
+#endif // SEPARATION_RAY_SHAPE_2D_H
diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp
new file mode 100644
index 0000000000..376e04c844
--- /dev/null
+++ b/scene/resources/separation_ray_shape_3d.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* separation_ray_shape_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "separation_ray_shape_3d.h"
+
+#include "servers/physics_server_3d.h"
+
+Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ points.push_back(Vector3(0, 0, get_length()));
+
+ return points;
+}
+
+real_t SeparationRayShape3D::get_enclosing_radius() const {
+ return length;
+}
+
+void SeparationRayShape3D::_update_shape() {
+ Dictionary d;
+ d["length"] = length;
+ d["slide_on_slope"] = slide_on_slope;
+ PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
+ Shape3D::_update_shape();
+}
+
+void SeparationRayShape3D::set_length(float p_length) {
+ length = p_length;
+ _update_shape();
+ notify_change_to_owners();
+}
+
+float SeparationRayShape3D::get_length() const {
+ return length;
+}
+
+void SeparationRayShape3D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
+ _update_shape();
+ notify_change_to_owners();
+}
+
+bool SeparationRayShape3D::get_slide_on_slope() const {
+ return slide_on_slope;
+}
+
+void SeparationRayShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape3D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape3D::get_length);
+
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
+}
+
+SeparationRayShape3D::SeparationRayShape3D() :
+ Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
+ /* Code copied from setters to prevent the use of uninitialized variables */
+ _update_shape();
+ notify_change_to_owners();
+}
diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h
new file mode 100644
index 0000000000..54058b6095
--- /dev/null
+++ b/scene/resources/separation_ray_shape_3d.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* separation_ray_shape_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SEPARATION_RAY_SHAPE_H
+#define SEPARATION_RAY_SHAPE_H
+#include "scene/resources/shape_3d.h"
+
+class SeparationRayShape3D : public Shape3D {
+ GDCLASS(SeparationRayShape3D, Shape3D);
+ float length = 1.0;
+ bool slide_on_slope = false;
+
+protected:
+ static void _bind_methods();
+ virtual void _update_shape() override;
+
+public:
+ void set_length(float p_length);
+ float get_length() const;
+
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
+
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
+ virtual real_t get_enclosing_radius() const override;
+
+ SeparationRayShape3D();
+};
+#endif // SEPARATION_RAY_SHAPE_H
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 0807a062f2..d2f38ba836 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -211,8 +211,8 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
dirty = true;
return res;
}
@@ -409,8 +409,8 @@ int TextLine::hit_test(float p_coords) const {
TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
rid = TS->create_shaped_text(p_direction, p_orientation);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
}
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 763033354a..62949b9b98 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -333,8 +333,8 @@ void TextParagraph::clear_dropcap() {
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
lines_dirty = true;
return res;
}
@@ -829,8 +829,8 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
rid = TS->create_shaped_text(p_direction, p_orientation);
TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
- spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
width = p_width;
}
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index fcd31143a8..d383961d87 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -205,25 +205,46 @@ bool TileSet::is_uv_clipping() const {
return uv_clipping;
};
-void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
- ERR_FAIL_COND(p_occlusion_layers_count < 0);
- if (occlusion_layers.size() == p_occlusion_layers_count) {
- return;
- }
+int TileSet::get_occlusion_layers_count() const {
+ return occlusion_layers.size();
+};
- occlusion_layers.resize(p_occlusion_layers_count);
+void TileSet::add_occlusion_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = occlusion_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_index, OcclusionLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_occlusion_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_occlusion_layers_count() const {
- return occlusion_layers.size();
-};
+void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occlusion_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]);
+ occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size());
+ occlusion_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_occlusion_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) {
ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
@@ -247,25 +268,45 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const {
return occlusion_layers[p_layer_index].sdf_collision;
}
-// Physics
-void TileSet::set_physics_layers_count(int p_physics_layers_count) {
- ERR_FAIL_COND(p_physics_layers_count < 0);
- if (physics_layers.size() == p_physics_layers_count) {
- return;
- }
+int TileSet::get_physics_layers_count() const {
+ return physics_layers.size();
+}
- physics_layers.resize(p_physics_layers_count);
+void TileSet::add_physics_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = physics_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, physics_layers.size() + 1);
+ physics_layers.insert(p_index, PhysicsLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_physics_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_physics_layers_count() const {
- return physics_layers.size();
+void TileSet::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1);
+ physics_layers.insert(p_to_pos, physics_layers[p_from_index]);
+ physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics_layers.size());
+ physics_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_physics_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) {
@@ -301,17 +342,45 @@ Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_ind
}
// Terrains
-void TileSet::set_terrain_sets_count(int p_terrains_sets_count) {
- ERR_FAIL_COND(p_terrains_sets_count < 0);
+int TileSet::get_terrain_sets_count() const {
+ return terrain_sets.size();
+}
+
+void TileSet::add_terrain_set(int p_index) {
+ if (p_index < 0) {
+ p_index = terrain_sets.size();
+ }
+ ERR_FAIL_INDEX(p_index, terrain_sets.size() + 1);
+ terrain_sets.insert(p_index, TerrainSet());
- terrain_sets.resize(p_terrains_sets_count);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain_set(p_index);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrain_sets_count() const {
- return terrain_sets.size();
+void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, terrain_sets.size());
+ ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1);
+ terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]);
+ terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_terrain_set(int p_index) {
+ ERR_FAIL_INDEX(p_index, terrain_sets.size());
+ terrain_sets.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain_set(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
@@ -330,36 +399,61 @@ TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const {
return terrain_sets[p_terrain_set].mode;
}
-void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) {
+int TileSet::get_terrains_count(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
+ return terrain_sets[p_terrain_set].terrains.size();
+}
+
+void TileSet::add_terrain(int p_terrain_set, int p_index) {
ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
- ERR_FAIL_COND(p_terrains_layers_count < 0);
- if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) {
- return;
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+ if (p_index < 0) {
+ p_index = terrains.size();
}
-
- int old_size = terrain_sets[p_terrain_set].terrains.size();
- terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count);
+ ERR_FAIL_INDEX(p_index, terrains.size() + 1);
+ terrains.insert(p_index, Terrain());
// Default name and color
- for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) {
- float hue_rotate = (i * 2 % 16) / 16.0;
- Color c;
- c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
- terrain_sets.write[p_terrain_set].terrains.write[i].color = c;
- terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i));
+ float hue_rotate = (terrains.size() % 16) / 16.0;
+ Color c;
+ c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
+ terrains.write[p_index].color = c;
+ terrains.write[p_index].name = String(vformat("Terrain %d", p_index));
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain(p_terrain_set, p_index);
}
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
- }
+ notify_property_list_changed();
+ emit_changed();
+}
+void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_from_index, terrains.size());
+ ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1);
+ terrains.insert(p_to_pos, terrains[p_from_index]);
+ terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrains_count(int p_terrain_set) const {
- ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
- return terrain_sets[p_terrain_set].terrains.size();
+void TileSet::remove_terrain(int p_terrain_set, int p_index) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_index, terrains.size());
+ terrains.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain(p_terrain_set, p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) {
@@ -485,24 +579,45 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh
}
// Navigation
-void TileSet::set_navigation_layers_count(int p_navigation_layers_count) {
- ERR_FAIL_COND(p_navigation_layers_count < 0);
- if (navigation_layers.size() == p_navigation_layers_count) {
- return;
- }
+int TileSet::get_navigation_layers_count() const {
+ return navigation_layers.size();
+}
- navigation_layers.resize(p_navigation_layers_count);
+void TileSet::add_navigation_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = navigation_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, navigation_layers.size() + 1);
+ navigation_layers.insert(p_index, NavigationLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_navigation_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_navigation_layers_count() const {
- return navigation_layers.size();
+void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1);
+ navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]);
+ navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation_layers.size());
+ navigation_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_navigation_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) {
@@ -517,30 +632,52 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
}
// Custom data.
-void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) {
- ERR_FAIL_COND(p_custom_data_layers_count < 0);
- if (custom_data_layers.size() == p_custom_data_layers_count) {
- return;
- }
-
- custom_data_layers.resize(p_custom_data_layers_count);
+int TileSet::get_custom_data_layers_count() const {
+ return custom_data_layers.size();
+}
- for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) {
- if (E->get() >= custom_data_layers.size()) {
- custom_data_layers_by_name.erase(E);
- }
+void TileSet::add_custom_data_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = custom_data_layers.size();
}
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_index, CustomDataLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_custom_data_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_custom_data_layers_count() const {
- return custom_data_layers.size();
+void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]);
+ custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size());
+ custom_data_layers.remove(p_index);
+ for (KeyValue<String, int> E : custom_data_layers_by_name) {
+ if (E.value == p_index) {
+ custom_data_layers_by_name.erase(E.key);
+ break;
+ }
+ }
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_custom_data_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
int TileSet::get_custom_data_layer_by_name(String p_value) const {
@@ -1110,7 +1247,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
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;
+ if (terrain >= (int)bit_counts.size()) {
+ WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain));
+ } else {
+ bit_counts[terrain] += 1;
+ }
}
}
}
@@ -1831,13 +1972,13 @@ void TileSet::_compatibility_conversion() {
if (ctd->occluder.is_valid()) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->occluder);
}
if (ctd->navigation.is_valid()) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1847,7 +1988,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -1922,13 +2063,13 @@ void TileSet::_compatibility_conversion() {
tile_data->set_z_index(ctd->z_index);
if (ctd->autotile_occluder_map.has(coords)) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
}
if (ctd->autotile_navpoly_map.has(coords)) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1942,7 +2083,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -2206,15 +2347,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "light_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_light_mask(index, p_value);
return true;
} else if (components[1] == "sdf_collision") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_sdf_collision(index, p_value);
return true;
@@ -2225,23 +2366,22 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "collision_layer") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_layer(index, p_value);
return true;
} else if (components[1] == "collision_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_mask(index, p_value);
return true;
} else if (components[1] == "physics_material") {
Ref<PhysicsMaterial> physics_material = p_value;
- ERR_FAIL_COND_V(!physics_material.is_valid(), false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_physics_material(index, physics_material);
return true;
@@ -2252,37 +2392,30 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(terrain_set_index < 0, false);
if (components[1] == "mode") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value)));
- } else if (components[1] == "terrains_count") {
- ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
- }
- set_terrains_count(terrain_set_index, p_value);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
ERR_FAIL_COND_V(terrain_index < 0, false);
if (components[2] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_name(terrain_set_index, terrain_index, p_value);
return true;
} else if (components[2] == "color") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_color(terrain_set_index, terrain_index, p_value);
return true;
@@ -2294,8 +2427,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "layers") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= navigation_layers.size()) {
- set_navigation_layers_count(index + 1);
+ while (index >= navigation_layers.size()) {
+ add_navigation_layer();
}
set_navigation_layer_layers(index, p_value);
return true;
@@ -2306,15 +2439,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_name(index, p_value);
return true;
} else if (components[1] == "type") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_type(index, Variant::Type(int(p_value)));
return true;
@@ -2402,9 +2535,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
if (components[1] == "mode") {
r_ret = get_terrain_set_mode(terrain_set_index);
return true;
- } else if (components[1] == "terrains_count") {
- r_ret = get_terrains_count(terrain_set_index);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
@@ -2522,7 +2652,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
- p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index)));
for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index)));
@@ -2590,16 +2720,20 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
- ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("add_occlusion_layer", "to_position"), &TileSet::add_occlusion_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_occlusion_layer", "layer_index", "to_position"), &TileSet::move_occlusion_layer);
+ ClassDB::bind_method(D_METHOD("remove_occlusion_layer", "layer_index"), &TileSet::remove_occlusion_layer);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask);
ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision);
ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision);
// Physics
- ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count);
ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("add_physics_layer", "to_position"), &TileSet::add_physics_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_physics_layer", "layer_index", "to_position"), &TileSet::move_physics_layer);
+ ClassDB::bind_method(D_METHOD("remove_physics_layer", "layer_index"), &TileSet::remove_physics_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
@@ -2608,27 +2742,35 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
// Terrains
- ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count);
ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("add_terrain_set", "to_position"), &TileSet::add_terrain_set, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain_set", "layer_index", "to_position"), &TileSet::move_terrain_set);
+ ClassDB::bind_method(D_METHOD("remove_terrain_set", "layer_index"), &TileSet::remove_terrain_set);
ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode);
ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode);
- ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count);
ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count);
+ ClassDB::bind_method(D_METHOD("add_terrain", "terrain_set", "to_position"), &TileSet::add_terrain, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain", "terrain_set", "terrain_index", "to_position"), &TileSet::move_terrain);
+ ClassDB::bind_method(D_METHOD("remove_terrain", "terrain_set", "terrain_index"), &TileSet::remove_terrain);
ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name);
ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name);
ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color);
ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color);
// Navigation
- ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count);
ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("add_navigation_layer", "to_position"), &TileSet::add_navigation_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_navigation_layer", "layer_index", "to_position"), &TileSet::move_navigation_layer);
+ ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer);
ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
// Custom data
- ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count);
ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
+ ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer);
+ ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer);
// Tile proxies
ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy);
@@ -2653,19 +2795,19 @@ void TileSet::_bind_methods() {
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
+ ADD_ARRAY("occlusion_layers", "occlusion_layer_");
ADD_GROUP("Physics", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count");
+ ADD_ARRAY("physics_layers", "physics_layer_");
ADD_GROUP("Terrains", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count");
+ ADD_ARRAY("terrain_sets", "terrain_set_");
ADD_GROUP("Navigation", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count");
+ ADD_ARRAY("navigation_layers", "navigation_layer_");
ADD_GROUP("Custom data", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count");
+ ADD_ARRAY("custom_data_layers", "custom_data_layer_");
// -- Enum binding --
BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE);
@@ -2750,6 +2892,150 @@ void TileSetAtlasSource::notify_tile_data_properties_should_change() {
}
}
+void TileSetAtlasSource::add_occlusion_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_occlusion_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_occlusion_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_occlusion_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_physics_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_physics_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_physics_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_physics_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_physics_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain_set(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain_set(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain_set(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain_set(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain_set(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain(int p_terrain_set, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain(p_terrain_set, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain(int p_terrain_set, int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain(p_terrain_set, p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_navigation_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_navigation_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_navigation_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_navigation_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_navigation_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_custom_data_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_custom_data_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_custom_data_layer(p_index);
+ }
+ }
+}
+
void TileSetAtlasSource::reset_state() {
// Reset all TileData.
for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
@@ -3575,6 +3861,155 @@ void TileData::notify_tile_data_properties_should_change() {
emit_signal(SNAME("changed"));
}
+void TileData::add_occlusion_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = occluders.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, Ref<OccluderPolygon2D>());
+}
+
+void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occluders.size());
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, occluders[p_from_index]);
+ occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occluders.size());
+ occluders.remove(p_index);
+}
+
+void TileData::add_physics_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = physics.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, PhysicsLayerTileData());
+}
+
+void TileData::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics.size());
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, physics[p_from_index]);
+ physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics.size());
+ physics.remove(p_index);
+}
+
+void TileData::add_terrain_set(int p_to_pos) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+}
+
+void TileData::move_terrain_set(int p_from_index, int p_to_pos) {
+ if (p_from_index == terrain_set) {
+ terrain_set = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_set) {
+ terrain_set -= 1;
+ }
+ if (p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+ }
+}
+
+void TileData::remove_terrain_set(int p_index) {
+ if (p_index == terrain_set) {
+ terrain_set = -1;
+ for (int i = 0; i < 16; i++) {
+ terrain_peering_bits[i] = -1;
+ }
+ } else if (terrain_set > p_index) {
+ terrain_set -= 1;
+ }
+}
+
+void TileData::add_terrain(int p_terrain_set, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+}
+
+void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_from_index == terrain_peering_bits[i]) {
+ terrain_peering_bits[i] = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_peering_bits[i]) {
+ terrain_peering_bits[i] -= 1;
+ }
+ if (p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+ }
+}
+
+void TileData::remove_terrain(int p_terrain_set, int p_index) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (terrain_peering_bits[i] == p_index) {
+ terrain_peering_bits[i] = -1;
+ } else if (terrain_peering_bits[i] > p_index) {
+ terrain_peering_bits[i] -= 1;
+ }
+ }
+ }
+}
+
+void TileData::add_navigation_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = navigation.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, Ref<NavigationPolygon>());
+}
+
+void TileData::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, navigation[p_from_index]);
+ navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation.size());
+ navigation.remove(p_index);
+}
+
+void TileData::add_custom_data_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = custom_data.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, Variant());
+}
+
+void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, navigation[p_from_index]);
+ custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data.size());
+ custom_data.remove(p_index);
+}
+
void TileData::reset_state() {
occluders.clear();
physics.clear();
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 35e6999d13..29a71d31d2 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -225,10 +225,10 @@ private:
bool terrain_bits_meshes_dirty = true;
// Navigation
- struct Navigationlayer {
+ struct NavigationLayer {
uint32_t layers = 1;
};
- Vector<Navigationlayer> navigation_layers;
+ Vector<NavigationLayer> navigation_layers;
// CustomData
struct CustomDataLayer {
@@ -298,16 +298,20 @@ public:
void set_uv_clipping(bool p_uv_clipping);
bool is_uv_clipping() const;
- void set_occlusion_layers_count(int p_occlusion_layers_count);
int get_occlusion_layers_count() const;
+ void add_occlusion_layer(int p_index = -1);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
int get_occlusion_layer_light_mask(int p_layer_index) const;
void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
// Physics
- void set_physics_layers_count(int p_physics_layers_count);
int get_physics_layers_count() const;
+ void add_physics_layer(int p_index = -1);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
@@ -315,13 +319,19 @@ public:
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
- // Terrains
- void set_terrain_sets_count(int p_terrains_sets_count);
+ // Terrain sets
int get_terrain_sets_count() const;
+ void add_terrain_set(int p_index = -1);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
- void set_terrains_count(int p_terrain_set, int p_terrains_count);
+
+ // Terrains
int get_terrains_count(int p_terrain_set) const;
+ void add_terrain(int p_terrain_set, int p_index = -1);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
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);
@@ -330,14 +340,18 @@ public:
bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
// Navigation
- void set_navigation_layers_count(int p_navigation_layers_count);
int get_navigation_layers_count() const;
+ void add_navigation_layer(int p_index = -1);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
// Custom data
- void set_custom_data_layers_count(int p_custom_data_layers_count);
int get_custom_data_layers_count() const;
+ void add_custom_data_layer(int p_index = -1);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_name(int p_layer_id, String p_value);
String get_custom_data_name(int p_layer_id) const;
@@ -397,6 +411,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
virtual void notify_tile_data_properties_should_change(){};
+ virtual void add_occlusion_layer(int p_index){};
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_occlusion_layer(int p_index){};
+ virtual void add_physics_layer(int p_index){};
+ virtual void move_physics_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_physics_layer(int p_index){};
+ virtual void add_terrain_set(int p_index){};
+ virtual void move_terrain_set(int p_from_index, int p_to_pos){};
+ virtual void remove_terrain_set(int p_index){};
+ virtual void add_terrain(int p_terrain_set, int p_index){};
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
+ virtual void remove_terrain(int p_terrain_set, int p_index){};
+ virtual void add_navigation_layer(int p_index){};
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_navigation_layer(int p_index){};
+ virtual void add_custom_data_layer(int p_index){};
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_custom_data_layer(int p_index){};
virtual void reset_state() override{};
// Tiles.
@@ -448,6 +480,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
virtual void notify_tile_data_properties_should_change() override;
+ virtual void add_occlusion_layer(int p_index) override;
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_occlusion_layer(int p_index) override;
+ virtual void add_physics_layer(int p_index) override;
+ virtual void move_physics_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_physics_layer(int p_index) override;
+ virtual void add_terrain_set(int p_index) override;
+ virtual void move_terrain_set(int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain_set(int p_index) override;
+ virtual void add_terrain(int p_terrain_set, int p_index) override;
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain(int p_terrain_set, int p_index) override;
+ virtual void add_navigation_layer(int p_index) override;
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_navigation_layer(int p_index) override;
+ virtual void add_custom_data_layer(int p_index) override;
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_custom_data_layer(int p_index) override;
virtual void reset_state() override;
// Base properties.
@@ -528,7 +578,7 @@ public:
int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
- // Scenes sccessors. Lot are similar to "Alternative tiles".
+ // Scenes accessors. Lot are similar to "Alternative tiles".
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
@@ -597,6 +647,24 @@ public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
void notify_tile_data_properties_should_change();
+ void add_occlusion_layer(int p_index);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
+ void add_physics_layer(int p_index);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
+ void add_terrain_set(int p_index);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
+ void add_terrain(int p_terrain_set, int p_index);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
+ void add_navigation_layer(int p_index);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
+ void add_custom_data_layer(int p_index);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;