diff options
Diffstat (limited to 'scene')
59 files changed, 2421 insertions, 1747 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 aa82530690..3518d434c3 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, @@ -295,7 +295,7 @@ 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(); + double 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); @@ -318,7 +318,7 @@ void StaticBody2D::_notification(int p_what) { Transform2D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double 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); @@ -1029,12 +1029,17 @@ 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 PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); @@ -1059,7 +1064,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); } @@ -1079,7 +1084,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); @@ -1223,7 +1228,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(); @@ -1269,12 +1274,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) { @@ -1303,7 +1307,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; } @@ -1316,19 +1320,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) { @@ -1452,12 +1458,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) { @@ -1542,8 +1556,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); @@ -1585,7 +1601,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 ef55024134..1bf53ea53c 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(); @@ -312,7 +312,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; @@ -352,14 +353,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/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(¤t.filter, false); - current.filter_process[k * 2 + 1].set_filter(¤t.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(¤t.filter); - current.filter_process[k * 2 + 1].set_filter(¤t.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 e8fb884cf2..0356994cdb 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, @@ -349,7 +349,7 @@ 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(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -380,7 +380,7 @@ void StaticBody3D::_notification(int p_what) { Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -1070,7 +1070,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)) { @@ -1100,7 +1100,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); } @@ -1161,7 +1161,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) { @@ -1216,8 +1216,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; + } } } } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 0733d57f51..8c09f77846 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); 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/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 049f3483ff..d11387902a 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1124,6 +1124,7 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons void AnimationNodeBlendTree::reset_state() { graph_offset = Vector2(); nodes.clear(); + _initialize_node_tree(); emit_changed(); emit_signal(SNAME("tree_changed")); } @@ -1162,7 +1163,7 @@ void AnimationNodeBlendTree::_bind_methods() { BIND_CONSTANT(CONNECTION_ERROR_CONNECTION_EXISTS); } -AnimationNodeBlendTree::AnimationNodeBlendTree() { +void AnimationNodeBlendTree::_initialize_node_tree() { Ref<AnimationNodeOutput> output; output.instantiate(); Node n; @@ -1172,5 +1173,9 @@ AnimationNodeBlendTree::AnimationNodeBlendTree() { nodes["output"] = n; } +AnimationNodeBlendTree::AnimationNodeBlendTree() { + _initialize_node_tree(); +} + AnimationNodeBlendTree::~AnimationNodeBlendTree() { } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 8508aaf71b..258443a999 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -345,6 +345,8 @@ class AnimationNodeBlendTree : public AnimationRootNode { void _tree_changed(); void _node_changed(const StringName &p_node); + void _initialize_node_tree(); + protected: static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_value); 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/code_edit.cpp b/scene/gui/code_edit.cpp index 27cac73aef..5f3ab18cca 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -2651,7 +2651,7 @@ TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const { void CodeEdit::_filter_code_completion_candidates_impl() { int line_height = get_line_height(); - if (GDVIRTUAL_IS_OVERRIDEN(_filter_code_completion_candidates)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_filter_code_completion_candidates)) { code_completion_options.clear(); code_completion_base = ""; 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/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..3605842224 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: @@ -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..481e211eb3 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -905,7 +905,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; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 61dfec6abe..7e64c13b37 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -888,7 +888,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; @@ -5004,7 +5004,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 +5012,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 +5034,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)); @@ -5948,8 +5948,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 +5958,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/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 9fce00be60..f2a2648140 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; @@ -58,7 +59,7 @@ void Node::_notification(int p_notification) { } break; case NOTIFICATION_PHYSICS_PROCESS: { - GDVIRTUAL_CALL(_physics_process, get_process_delta_time()); + GDVIRTUAL_CALL(_physics_process, get_physics_process_delta_time()); } break; case NOTIFICATION_ENTER_TREE: { @@ -123,22 +124,22 @@ void Node::_notification(int p_notification) { } break; case NOTIFICATION_READY: { if (get_script_instance()) { - if (GDVIRTUAL_IS_OVERRIDEN(_input)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_input)) { set_process_input(true); } - if (GDVIRTUAL_IS_OVERRIDEN(_unhandled_input)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) { set_process_unhandled_input(true); } - if (GDVIRTUAL_IS_OVERRIDEN(_unhandled_key_input)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) { set_process_unhandled_key_input(true); } - if (GDVIRTUAL_IS_OVERRIDEN(_process)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_process)) { set_process(true); } - if (GDVIRTUAL_IS_OVERRIDEN(_physics_process)) { + if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) { set_physics_process(true); } @@ -291,14 +292,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 +366,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,24 +517,24 @@ 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 ********/ @@ -1058,6 +1092,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 +1108,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 +1117,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 +1199,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 +1234,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 +1788,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 +2506,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 +2658,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 +2686,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,10 +2738,10 @@ 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); @@ -2747,6 +2824,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..d0246ebe84 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,7 +127,7 @@ private: ProcessMode process_mode = PROCESS_MODE_INHERIT; Node *process_owner = nullptr; - int network_master = 1; // Server by default. + int network_authority = 1; // Server by default. Vector<MultiplayerAPI::RPCConfig> rpc_methods; // Variables used to properly sort the node when processing, ignored otherwise. @@ -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,9 +462,9 @@ 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; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index ea2323c651..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() { @@ -3451,6 +3455,17 @@ void Viewport::set_use_xr(bool p_use_xr) { bool Viewport::is_using_xr() { return use_xr; } + +void Viewport::set_scale_3d(const Scale3D p_scale_3d) { + scale_3d = p_scale_3d; + + RS::get_singleton()->viewport_set_scale_3d(viewport, RS::ViewportScale3D(scale_3d)); +} + +Viewport::Scale3D Viewport::get_scale_3d() const { + return scale_3d; +} + #endif // _3D_DISABLED void Viewport::_bind_methods() { @@ -3575,8 +3590,12 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr); ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr); + ClassDB::bind_method(D_METHOD("set_scale_3d", "scale"), &Viewport::set_scale_3d); + ClassDB::bind_method(D_METHOD("get_scale_3d"), &Viewport::get_scale_3d); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scale_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled,75%,50%,33%,25%")), "set_scale_3d", "get_scale_3d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); @@ -3620,6 +3639,12 @@ void Viewport::_bind_methods() { ADD_SIGNAL(MethodInfo("size_changed")); ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + BIND_ENUM_CONSTANT(SCALE_3D_DISABLED); + BIND_ENUM_CONSTANT(SCALE_3D_75_PERCENT); + BIND_ENUM_CONSTANT(SCALE_3D_50_PERCENT); + BIND_ENUM_CONSTANT(SCALE_3D_33_PERCENT); + BIND_ENUM_CONSTANT(SCALE_3D_25_PERCENT); + BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4); @@ -3734,6 +3759,11 @@ Viewport::Viewport() { gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.5); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers +#ifndef _3D_DISABLED + int scale = GLOBAL_GET("rendering/3d/viewport/scale"); + set_scale_3d((Scale3D)scale); +#endif // _3D_DISABLED + set_sdf_oversize(sdf_oversize); //set to server } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index b24de77e6b..d9b21ce6a8 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -88,6 +88,14 @@ class Viewport : public Node { GDCLASS(Viewport, Node); public: + enum Scale3D { + SCALE_3D_DISABLED, + SCALE_3D_75_PERCENT, + SCALE_3D_50_PERCENT, + SCALE_3D_33_PERCENT, + SCALE_3D_25_PERCENT + }; + enum ShadowAtlasQuadrantSubdiv { SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED, SHADOW_ATLAS_QUADRANT_SUBDIV_1, @@ -577,6 +585,7 @@ public: #ifndef _3D_DISABLED bool use_xr = false; + Scale3D scale_3d = SCALE_3D_DISABLED; friend class Listener3D; Listener3D *listener_3d = nullptr; Set<Listener3D *> listener_3d_set; @@ -647,6 +656,9 @@ public: void set_use_xr(bool p_use_xr); bool is_using_xr(); + + void set_scale_3d(const Scale3D p_scale_3d); + Scale3D get_scale_3d() const; #endif // _3D_DISABLED Viewport(); @@ -705,6 +717,7 @@ VARIANT_ENUM_CAST(SubViewport::UpdateMode); VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv); VARIANT_ENUM_CAST(Viewport::MSAA); VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA); +VARIANT_ENUM_CAST(Viewport::Scale3D); VARIANT_ENUM_CAST(Viewport::DebugDraw); VARIANT_ENUM_CAST(Viewport::SDFScale); VARIANT_ENUM_CAST(Viewport::SDFOversize); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index b9c0ae5a4a..1fcfce2ea9 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); } } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 2cea3c2337..0c97fee711 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); @@ -752,6 +740,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 +829,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 +950,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 +1057,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/visual_shader.cpp b/scene/resources/visual_shader.cpp index a7f99a2113..e8fe3ff3cd 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -335,7 +335,7 @@ String VisualShaderNodeCustom::get_output_port_name(int p_port) const { } String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDEN(_get_code), ""); + ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDDEN(_get_code), ""); Vector<String> input_vars; for (int i = 0; i < get_input_port_count(); i++) { input_vars.push_back(p_input_vars[i]); |