summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp3
-rw-r--r--scene/2d/audio_stream_player_2d.cpp17
-rw-r--r--scene/2d/audio_stream_player_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp2
-rw-r--r--scene/2d/navigation_agent_2d.cpp8
-rw-r--r--scene/2d/tile_map.cpp2
-rw-r--r--scene/3d/audio_stream_player_3d.cpp24
-rw-r--r--scene/3d/audio_stream_player_3d.h2
-rw-r--r--scene/3d/navigation_agent_3d.cpp8
-rw-r--r--scene/3d/sprite_3d.cpp3
-rw-r--r--scene/animation/animation_player.cpp234
-rw-r--r--scene/animation/animation_player.h29
-rw-r--r--scene/animation/animation_tree.cpp293
-rw-r--r--scene/animation/animation_tree.h30
-rw-r--r--scene/audio/audio_stream_player.cpp5
-rw-r--r--scene/audio/audio_stream_player.h1
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/graph_edit.cpp5
-rw-r--r--scene/main/window.cpp68
-rw-r--r--scene/main/window.h2
-rw-r--r--scene/resources/animation.cpp41
-rw-r--r--scene/resources/animation.h3
-rw-r--r--scene/resources/shader.cpp13
-rw-r--r--scene/resources/shader.h2
-rw-r--r--scene/resources/shader_include.cpp12
-rw-r--r--scene/resources/shader_include.h3
-rw-r--r--scene/resources/tile_set.cpp145
-rw-r--r--scene/resources/tile_set.h11
28 files changed, 699 insertions, 271 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index ccf9696d82..8f7006caca 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -475,8 +475,9 @@ void AnimatedSprite2D::play(const StringName &p_name, float p_custom_scale, bool
}
}
- notify_property_list_changed();
set_process_internal(true);
+ notify_property_list_changed();
+ queue_redraw();
}
void AnimatedSprite2D::play_backwards(const StringName &p_name) {
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 7b681eac7a..8c23d33402 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -72,12 +72,10 @@ void AudioStreamPlayer2D::_notification(int p_what) {
_update_panning();
}
- if (setplay.get() >= 0 && stream.is_valid()) {
+ if (setplayback.is_valid() && setplay.get() >= 0) {
active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
- ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
- AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
- stream_playbacks.push_back(new_playback);
+ AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
+ setplayback.unref();
setplay.set(-1);
}
@@ -255,7 +253,11 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
if (stream->is_monophonic() && is_playing()) {
stop();
}
+ Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
+ ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ stream_playbacks.push_back(stream_playback);
+ setplayback = stream_playback;
setplay.set(p_from_pos);
active.set();
set_physics_process_internal(true);
@@ -390,6 +392,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const {
return false;
}
+bool AudioStreamPlayer2D::has_stream_playback() {
+ return !stream_playbacks.is_empty();
+}
+
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
return stream_playbacks[stream_playbacks.size() - 1];
@@ -458,6 +464,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer2D::set_panning_strength);
ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer2D::get_panning_strength);
+ ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer2D::has_stream_playback);
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index a5fd584513..79a026fed2 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -56,6 +56,7 @@ private:
SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
+ Ref<AudioStreamPlayback> setplayback;
Vector<AudioFrame> volume_vector;
@@ -129,6 +130,7 @@ public:
void set_panning_strength(float p_panning_strength);
float get_panning_strength() const;
+ bool has_stream_playback();
Ref<AudioStreamPlayback> get_stream_playback();
AudioStreamPlayer2D();
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 9a3b7c8687..00d13c59b9 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -638,7 +638,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Collision", "collision_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,suffix:px"), "set_collision_base_size", "get_collision_base_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
ADD_GROUP("Drawing", "");
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "visibility_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_visibility_rect", "get_visibility_rect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 907fb7e6a9..e73b6e7e23 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -205,9 +205,9 @@ NavigationAgent2D::~NavigationAgent2D() {
void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- NavigationServer2D::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
+ NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
} else {
- NavigationServer2D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
}
}
@@ -217,7 +217,8 @@ bool NavigationAgent2D::get_avoidance_enabled() const {
void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
- NavigationServer2D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
+
if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Node2D>(p_agent_parent);
@@ -226,6 +227,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
} else {
NavigationServer2D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map());
}
+
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 8c5aab4c80..a1304ab991 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1379,7 +1379,7 @@ void TileMap::draw_tile(RID p_canvas_item, const Vector2i &p_position, const Ref
Color modulate = tile_data->get_modulate() * p_modulation;
// Compute the offset.
- Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile);
+ Vector2i tile_offset = tile_data->get_texture_origin();
// Get destination rect.
Rect2 dest_rect;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 7ed18d2d41..436b936586 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -284,14 +284,12 @@ void AudioStreamPlayer3D::_notification(int p_what) {
volume_vector = _update_panning();
}
- if (setplay.get() >= 0 && stream.is_valid()) {
+ if (setplayback.is_valid() && setplay.get() >= 0) {
active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
- ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
HashMap<StringName, Vector<AudioFrame>> bus_map;
bus_map[_get_actual_bus()] = volume_vector;
- AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
- stream_playbacks.push_back(new_playback);
+ AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
+ setplayback.unref();
setplay.set(-1);
}
@@ -580,14 +578,21 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
if (stream->is_monophonic() && is_playing()) {
stop();
}
+ Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
+ ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+
+ stream_playbacks.push_back(stream_playback);
+ setplayback = stream_playback;
setplay.set(p_from_pos);
active.set();
set_physics_process_internal(true);
}
void AudioStreamPlayer3D::seek(float p_seconds) {
- stop();
- play(p_seconds);
+ if (is_playing()) {
+ stop();
+ play(p_seconds);
+ }
}
void AudioStreamPlayer3D::stop() {
@@ -783,6 +788,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const {
return false;
}
+bool AudioStreamPlayer3D::has_stream_playback() {
+ return !stream_playbacks.is_empty();
+}
+
Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
return stream_playbacks[stream_playbacks.size() - 1];
@@ -875,6 +884,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer3D::set_panning_strength);
ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer3D::get_panning_strength);
+ ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer3D::has_stream_playback);
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 67cec91896..bba8f10761 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -69,6 +69,7 @@ private:
SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
+ Ref<AudioStreamPlayback> setplayback;
AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
float volume_db = 0.0;
@@ -188,6 +189,7 @@ public:
void set_panning_strength(float p_panning_strength);
float get_panning_strength() const;
+ bool has_stream_playback();
Ref<AudioStreamPlayback> get_stream_playback();
AudioStreamPlayer3D();
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index fe7ddcb06e..4aa6e61ec5 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -212,9 +212,9 @@ NavigationAgent3D::~NavigationAgent3D() {
void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
} else {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
}
}
@@ -224,7 +224,8 @@ bool NavigationAgent3D::get_avoidance_enabled() const {
void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
- NavigationServer3D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
+
if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Node3D>(p_agent_parent);
@@ -233,6 +234,7 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
} else {
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map());
}
+
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 635c566ef8..9712df0936 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1182,8 +1182,9 @@ void AnimatedSprite3D::play(const StringName &p_name, float p_custom_scale, bool
}
}
- notify_property_list_changed();
set_process_internal(true);
+ notify_property_list_changed();
+ _queue_redraw();
}
void AnimatedSprite3D::play_backwards(const StringName &p_name) {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 63e0fb6935..1b7615befa 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -431,6 +431,17 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
+ if (a->track_get_type(i) == Animation::TYPE_AUDIO) {
+ if (!node_cache->audio_anim.has(a->track_get_path(i).get_concatenated_names())) {
+ TrackNodeCache::AudioAnim aa;
+ aa.object = (Object *)child;
+ aa.audio_stream.instantiate();
+ aa.audio_stream->set_polyphony(audio_max_polyphony);
+
+ node_cache->audio_anim[a->track_get_path(i).get_concatenated_names()] = aa;
+ }
+ }
+
node_cache->last_setup_pass = setup_pass;
}
}
@@ -820,52 +831,40 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (!nc->node || is_stopping) {
continue;
}
-
- if (p_seeked) {
#ifdef TOOLS_ENABLED
- if (!can_call) {
- continue; // To avoid spamming the preview in editor.
- }
+ if (p_seeked && !can_call) {
+ continue; // To avoid spamming the preview in editor.
+ }
#endif // TOOLS_ENABLED
- int idx = a->track_find_key(i, p_time);
- if (idx < 0) {
- continue;
- }
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- } else {
- float start_ofs = a->audio_track_get_key_start_offset(i, idx);
- start_ofs += p_time - a->track_get_key_time(i, idx);
- float end_ofs = a->audio_track_get_key_end_offset(i, idx);
- float len = stream->get_length();
-
- if (start_ofs > len - end_ofs) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- continue;
- }
-
- nc->node->call(SNAME("set_stream"), stream);
- nc->node->call(SNAME("play"), start_ofs);
+ HashMap<StringName, TrackNodeCache::AudioAnim>::Iterator E = nc->audio_anim.find(a->track_get_path(i).get_concatenated_names());
+ ERR_CONTINUE(!E); //should it continue, or create a new one?
- nc->audio_playing = true;
- playing_caches.insert(nc);
- if (len && end_ofs > 0) { //force an end at a time
- nc->audio_len = len - start_ofs - end_ofs;
- } else {
- nc->audio_len = 0;
- }
+ TrackNodeCache::AudioAnim *aa = &E->value;
+ Node *asp = Object::cast_to<Node>(aa->object);
+ if (!asp) {
+ continue;
+ }
+ aa->length = a->get_length();
+ aa->time = p_time;
+ aa->loop = a->get_loop_mode() != Animation::LOOP_NONE;
+ aa->backward = backward;
+ if (aa->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_audio_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update_audio[cache_update_audio_size++] = aa;
+ aa->accum_pass = accum_pass;
+ }
- nc->audio_start = p_time;
+ HashMap<int, TrackNodeCache::PlayingAudioStreamInfo> &map = aa->playing_streams;
+ // Find stream.
+ int idx = -1;
+ if (p_seeked) {
+ idx = a->track_find_key(i, p_time);
+ // Discard previous stream when seeking.
+ if (map.has(idx)) {
+ aa->audio_stream_playback->stop_stream(map[idx].index);
+ map.erase(idx);
}
-
} else {
- //find stuff to play
List<int> to_play;
if (p_started) {
int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
@@ -875,55 +874,47 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) {
- int idx = to_play.back()->get();
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- } else {
- float start_ofs = a->audio_track_get_key_start_offset(i, idx);
- float end_ofs = a->audio_track_get_key_end_offset(i, idx);
- float len = stream->get_length();
-
- nc->node->call(SNAME("set_stream"), stream);
- nc->node->call(SNAME("play"), start_ofs);
-
- nc->audio_playing = true;
- playing_caches.insert(nc);
- if (len && end_ofs > 0) { //force an end at a time
- nc->audio_len = len - start_ofs - end_ofs;
- } else {
- nc->audio_len = 0;
- }
-
- nc->audio_start = p_time;
- }
- } else if (nc->audio_playing) {
- bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
-
- bool stop = false;
-
- if (!loop) {
- if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
- stop = true;
- }
- } else if (nc->audio_len > 0) {
- float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
+ idx = to_play.back()->get();
+ }
+ }
+ if (idx < 0) {
+ continue;
+ }
- if (len > nc->audio_len) {
- stop = true;
- }
+ // Play stream.
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (stream.is_valid()) {
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
+
+ if (aa->object->call(SNAME("get_stream")) != aa->audio_stream) {
+ aa->object->call(SNAME("set_stream"), aa->audio_stream);
+ aa->audio_stream_playback.unref();
+ if (!playing_audio_stream_players.has(asp)) {
+ playing_audio_stream_players.push_back(asp);
}
+ }
+ if (!aa->object->call(SNAME("is_playing"))) {
+ aa->object->call(SNAME("play"));
+ }
+ if (!aa->object->call(SNAME("has_stream_playback"))) {
+ aa->audio_stream_playback.unref();
+ continue;
+ }
+ if (aa->audio_stream_playback.is_null()) {
+ aa->audio_stream_playback = aa->object->call(SNAME("get_stream_playback"));
+ }
- if (stop) {
- //time to stop
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- }
+ TrackNodeCache::PlayingAudioStreamInfo pasi;
+ pasi.index = aa->audio_stream_playback->play_stream(stream, start_ofs);
+ pasi.start = p_time;
+ if (len && end_ofs > 0) { // Force an end at a time.
+ pasi.len = len - start_ofs - end_ofs;
+ } else {
+ pasi.len = 0;
}
+ map[idx] = pasi;
}
} break;
@@ -1223,6 +1214,53 @@ void AnimationPlayer::_animation_update_transforms() {
ERR_CONTINUE(ba->accum_pass != accum_pass);
ba->object->set_indexed(ba->bezier_property, ba->bezier_accum);
}
+
+ for (int i = 0; i < cache_update_audio_size; i++) {
+ TrackNodeCache::AudioAnim *aa = cache_update_audio[i];
+
+ ERR_CONTINUE(aa->accum_pass != accum_pass);
+
+ // Audio ending process.
+ LocalVector<int> erase_list;
+ for (const KeyValue<int, TrackNodeCache::PlayingAudioStreamInfo> &K : aa->playing_streams) {
+ TrackNodeCache::PlayingAudioStreamInfo pasi = K.value;
+
+ bool stop = false;
+ if (!aa->audio_stream_playback->is_stream_playing(pasi.index)) {
+ stop = true;
+ }
+ if (!aa->loop) {
+ if (!aa->backward) {
+ if (aa->time < pasi.start) {
+ stop = true;
+ }
+ } else if (aa->backward) {
+ if (aa->time > pasi.start) {
+ stop = true;
+ }
+ }
+ }
+ if (pasi.len > 0) {
+ double len = 0.0;
+ if (!aa->backward) {
+ len = pasi.start > aa->time ? (aa->length - pasi.start) + aa->time : aa->time - pasi.start;
+ } else {
+ len = pasi.start < aa->time ? (aa->length - aa->time) + pasi.start : pasi.start - aa->time;
+ }
+ if (len > pasi.len) {
+ stop = true;
+ }
+ }
+ if (stop) {
+ // Time to stop.
+ aa->audio_stream_playback->stop_stream(pasi.index);
+ erase_list.push_back(K.key);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_list.size(); erase_idx++) {
+ aa->playing_streams.erase(erase_list[erase_idx]);
+ }
+ }
}
void AnimationPlayer::_animation_process(double p_delta) {
@@ -1238,6 +1276,7 @@ void AnimationPlayer::_animation_process(double p_delta) {
cache_update_size = 0;
cache_update_prop_size = 0;
cache_update_bezier_size = 0;
+ cache_update_audio_size = 0;
AnimationData *prev_from = playback.current.from;
_animation_process2(p_delta, started);
@@ -1675,6 +1714,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
}
if (get_current_animation() != p_name) {
+ _clear_audio_streams();
_stop_playing_caches(false);
}
@@ -1856,6 +1896,7 @@ void AnimationPlayer::_node_removed(Node *p_node) {
}
void AnimationPlayer::clear_caches() {
+ _clear_audio_streams();
_stop_playing_caches(true);
node_cache_map.clear();
@@ -1867,10 +1908,19 @@ void AnimationPlayer::clear_caches() {
cache_update_size = 0;
cache_update_prop_size = 0;
cache_update_bezier_size = 0;
+ cache_update_audio_size = 0;
emit_signal(SNAME("caches_cleared"));
}
+void AnimationPlayer::_clear_audio_streams() {
+ for (int i = 0; i < playing_audio_stream_players.size(); i++) {
+ playing_audio_stream_players[i]->call(SNAME("stop"));
+ playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>());
+ }
+ playing_audio_stream_players.clear();
+}
+
void AnimationPlayer::set_active(bool p_active) {
if (active == p_active) {
return;
@@ -1950,6 +2000,15 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
return method_call_mode;
}
+void AnimationPlayer::set_audio_max_polyphony(int p_audio_max_polyphony) {
+ ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
+ audio_max_polyphony = p_audio_max_polyphony;
+}
+
+int AnimationPlayer::get_audio_max_polyphony() const {
+ return audio_max_polyphony;
+}
+
void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
movie_quit_on_finish = p_enabled;
}
@@ -1978,6 +2037,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
}
void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
+ _clear_audio_streams();
_stop_playing_caches(p_reset);
Playback &c = playback;
c.blend.clear();
@@ -2198,6 +2258,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
+ ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationPlayer::set_audio_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationPlayer::get_audio_max_polyphony);
+
ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
@@ -2223,6 +2286,7 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 2901c43dcf..b0975fbead 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -37,6 +37,7 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
#include "scene/resources/animation_library.h"
+#include "scene/resources/audio_stream_polyphonic.h"
#ifdef TOOLS_ENABLED
class AnimatedValuesBackup : public RefCounted {
@@ -147,6 +148,26 @@ private:
HashMap<StringName, BezierAnim> bezier_anim;
+ struct PlayingAudioStreamInfo {
+ AudioStreamPlaybackPolyphonic::ID index = -1;
+ double start = 0.0;
+ double len = 0.0;
+ };
+
+ struct AudioAnim {
+ Ref<AudioStreamPolyphonic> audio_stream;
+ Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
+ HashMap<int, PlayingAudioStreamInfo> playing_streams;
+ Object *object = nullptr;
+ uint64_t accum_pass = 0;
+ double length = 0.0;
+ double time = 0.0;
+ bool loop = false;
+ bool backward = false;
+ };
+
+ HashMap<StringName, AudioAnim> audio_anim;
+
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
};
@@ -187,7 +208,10 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
+ TrackNodeCache::AudioAnim *cache_update_audio[NODE_CACHE_UPDATE_MAX];
+ int cache_update_audio_size = 0;
HashSet<TrackNodeCache *> playing_caches;
+ Vector<Node *> playing_audio_stream_players;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
@@ -263,6 +287,7 @@ private:
bool reset_on_save = true;
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
+ int audio_max_polyphony = 32;
bool movie_quit_on_finish = false;
bool processing = false;
bool active = true;
@@ -278,6 +303,7 @@ private:
void _animation_process(double p_delta);
void _node_removed(Node *p_node);
+ void _clear_audio_streams();
void _stop_playing_caches(bool p_reset);
// bind helpers
@@ -377,6 +403,9 @@ public:
void set_method_call_mode(AnimationMethodCallMode p_mode);
AnimationMethodCallMode get_method_call_mode() const;
+ void set_audio_max_polyphony(int p_audio_max_polyphony);
+ int get_audio_max_polyphony() const;
+
void set_movie_quit_on_finish_enabled(bool p_enabled);
bool is_movie_quit_on_finish_enabled() const;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 05b6c72cbd..f630660049 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -486,13 +486,7 @@ void AnimationTree::set_active(bool p_active) {
}
if (!active && is_inside_tree()) {
- for (const TrackCache *E : playing_caches) {
- if (ObjectDB::get_instance(E->object_id)) {
- E->object->call(SNAME("stop"));
- }
- }
-
- playing_caches.clear();
+ _clear_caches();
}
}
@@ -531,6 +525,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
if (!player->has_node(player->get_root())) {
ERR_PRINT("AnimationTree: AnimationPlayer root is invalid.");
set_active(false);
+ _clear_caches();
return false;
}
Node *parent = player->get_node(player->get_root());
@@ -763,6 +758,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_audio->object = child;
track_audio->object_id = track_audio->object->get_instance_id();
+ track_audio->audio_stream.instantiate();
+ track_audio->audio_stream->set_polyphony(audio_max_polyphony);
track = track_audio;
@@ -860,14 +857,32 @@ void AnimationTree::_animation_player_changed() {
}
void AnimationTree::_clear_caches() {
+ _clear_audio_streams();
+ _clear_playing_caches();
for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
memdelete(K.value);
}
- playing_caches.clear();
track_cache.clear();
cache_valid = false;
}
+void AnimationTree::_clear_audio_streams() {
+ for (int i = 0; i < playing_audio_stream_players.size(); i++) {
+ playing_audio_stream_players[i]->call(SNAME("stop"));
+ playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>());
+ }
+ playing_audio_stream_players.clear();
+}
+
+void AnimationTree::_clear_playing_caches() {
+ for (const TrackCache *E : playing_caches) {
+ if (ObjectDB::get_instance(E->object_id)) {
+ E->object->call(SNAME("stop"));
+ }
+ }
+ playing_caches.clear();
+}
+
static void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
// Separate function to use alloca() more efficiently
const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size());
@@ -1007,6 +1022,13 @@ void AnimationTree::_process_graph(double p_delta) {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
t->value = t->init_value;
} break;
+ case Animation::TYPE_AUDIO: {
+ TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
+ for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) {
+ PlayingAudioTrackInfo &track_info = L.value;
+ track_info.volume = 0.0;
+ }
+ } break;
default: {
} break;
}
@@ -1026,8 +1048,8 @@ void AnimationTree::_process_graph(double p_delta) {
bool seeked = as.seeked;
Animation::LoopedFlag looped_flag = as.looped_flag;
bool is_external_seeking = as.is_external_seeking;
+ bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream.
#ifndef _3D_DISABLED
- bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames.
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
@@ -1046,9 +1068,6 @@ void AnimationTree::_process_graph(double p_delta) {
int blend_idx = state.track_map[path];
ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);
real_t blend = (*as.track_blends)[blend_idx] * weight;
- if (Math::is_zero_approx(blend)) {
- continue; // Nothing to blend.
- }
Animation::TrackType ttype = a->track_get_type(i);
if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
@@ -1060,6 +1079,9 @@ void AnimationTree::_process_graph(double p_delta) {
switch (ttype) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
double prev_time = time - delta;
@@ -1151,6 +1173,9 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
double prev_time = time - delta;
@@ -1241,6 +1266,9 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
double prev_time = time - delta;
@@ -1332,6 +1360,9 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_BLEND_SHAPE: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
float value;
@@ -1348,6 +1379,9 @@ void AnimationTree::_process_graph(double p_delta) {
#endif // _3D_DISABLED
} break;
case Animation::TYPE_VALUE: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
@@ -1414,6 +1448,9 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
#endif // TOOLS_ENABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
if (seeked) {
@@ -1435,6 +1472,9 @@ void AnimationTree::_process_graph(double p_delta) {
}
} break;
case Animation::TYPE_BEZIER: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
real_t bezier = a->bezier_track_interpolate(i, time);
@@ -1445,110 +1485,87 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
- if (seeked) {
- int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
- if (idx < 0) {
- continue;
- }
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- } else {
- double start_ofs = a->audio_track_get_key_start_offset(i, idx);
- start_ofs += time - a->track_get_key_time(i, idx);
- double end_ofs = a->audio_track_get_key_end_offset(i, idx);
- double len = stream->get_length();
-
- if (start_ofs > len - end_ofs) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- continue;
- }
-
- t->object->call(SNAME("set_stream"), stream);
- t->object->call(SNAME("play"), start_ofs);
-
- t->playing = true;
- playing_caches.insert(t);
- if (len && end_ofs > 0) { //force an end at a time
- t->len = len - start_ofs - end_ofs;
- } else {
- t->len = 0;
- }
+ Node *asp = Object::cast_to<Node>(t->object);
+ if (!asp) {
+ t->playing_streams.clear();
+ continue;
+ }
- t->start = time;
+ ObjectID oid = a->get_instance_id();
+ if (!t->playing_streams.has(oid)) {
+ t->playing_streams[oid] = PlayingAudioTrackInfo();
+ }
+ // The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that.
+ PlayingAudioTrackInfo &track_info = t->playing_streams[oid];
+ track_info.length = a->get_length();
+ track_info.time = time;
+ track_info.volume += blend;
+ track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE;
+ track_info.backward = backward;
+ track_info.use_blend = a->audio_track_is_use_blend(i);
+
+ HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ // Find stream.
+ int idx = -1;
+ if (seeked) {
+ idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
+ // Discard previous stream when seeking.
+ if (map.has(idx)) {
+ t->audio_stream_playback->stop_stream(map[idx].index);
+ map.erase(idx);
}
-
} else {
- //find stuff to play
List<int> to_play;
a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) {
- int idx = to_play.back()->get();
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- } else {
- double start_ofs = a->audio_track_get_key_start_offset(i, idx);
- double end_ofs = a->audio_track_get_key_end_offset(i, idx);
- double len = stream->get_length();
-
- t->object->call(SNAME("set_stream"), stream);
- t->object->call(SNAME("play"), start_ofs);
-
- t->playing = true;
- playing_caches.insert(t);
- if (len && end_ofs > 0) { //force an end at a time
- t->len = len - start_ofs - end_ofs;
- } else {
- t->len = 0;
- }
+ idx = to_play.back()->get();
+ }
+ }
+ if (idx < 0) {
+ continue;
+ }
- t->start = time;
- }
- } else if (t->playing) {
- bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
-
- bool stop = false;
-
- if (!loop) {
- if (delta > 0) {
- if (time < t->start) {
- stop = true;
- }
- } else if (delta < 0) {
- if (time > t->start) {
- stop = true;
- }
- }
- } else if (t->len > 0) {
- double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
-
- if (len > t->len) {
- stop = true;
- }
+ // Play stream.
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (stream.is_valid()) {
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
+
+ if (t->object->call(SNAME("get_stream")) != t->audio_stream) {
+ t->object->call(SNAME("set_stream"), t->audio_stream);
+ t->audio_stream_playback.unref();
+ if (!playing_audio_stream_players.has(asp)) {
+ playing_audio_stream_players.push_back(asp);
}
+ }
+ if (!t->object->call(SNAME("is_playing"))) {
+ t->object->call(SNAME("play"));
+ }
+ if (!t->object->call(SNAME("has_stream_playback"))) {
+ t->audio_stream_playback.unref();
+ continue;
+ }
+ if (t->audio_stream_playback.is_null()) {
+ t->audio_stream_playback = t->object->call(SNAME("get_stream_playback"));
+ }
- if (stop) {
- //time to stop
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- }
+ PlayingAudioStreamInfo pasi;
+ pasi.index = t->audio_stream_playback->play_stream(stream, start_ofs);
+ pasi.start = time;
+ if (len && end_ofs > 0) { // Force an end at a time.
+ pasi.len = len - start_ofs - end_ofs;
+ } else {
+ pasi.len = 0;
}
+ map[idx] = pasi;
}
- real_t db = Math::linear_to_db(MAX(blend, 0.00001));
- t->object->call(SNAME("set_volume_db"), db);
} break;
case Animation::TYPE_ANIMATION: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);
AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object);
@@ -1694,6 +1711,64 @@ void AnimationTree::_process_graph(double p_delta) {
t->object->set_indexed(t->subpath, t->value);
} break;
+ case Animation::TYPE_AUDIO: {
+ TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
+
+ // Audio ending process.
+ LocalVector<ObjectID> erase_maps;
+ for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) {
+ PlayingAudioTrackInfo &track_info = L.value;
+ float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0);
+ LocalVector<int> erase_streams;
+ HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) {
+ PlayingAudioStreamInfo pasi = M.value;
+
+ bool stop = false;
+ if (!t->audio_stream_playback->is_stream_playing(pasi.index)) {
+ stop = true;
+ }
+ if (!track_info.loop) {
+ if (!track_info.backward) {
+ if (track_info.time < pasi.start) {
+ stop = true;
+ }
+ } else if (track_info.backward) {
+ if (track_info.time > pasi.start) {
+ stop = true;
+ }
+ }
+ }
+ if (pasi.len > 0) {
+ double len = 0.0;
+ if (!track_info.backward) {
+ len = pasi.start > track_info.time ? (track_info.length - pasi.start) + track_info.time : track_info.time - pasi.start;
+ } else {
+ len = pasi.start < track_info.time ? (track_info.length - track_info.time) + pasi.start : pasi.start - track_info.time;
+ }
+ if (len > pasi.len) {
+ stop = true;
+ }
+ }
+ if (stop) {
+ // Time to stop.
+ t->audio_stream_playback->stop_stream(pasi.index);
+ erase_streams.push_back(M.key);
+ } else {
+ t->audio_stream_playback->set_stream_volume(pasi.index, db);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_streams.size(); erase_idx++) {
+ map.erase(erase_streams[erase_idx]);
+ }
+ if (map.size() == 0) {
+ erase_maps.push_back(L.key);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_maps.size(); erase_idx++) {
+ t->playing_streams.erase(erase_maps[erase_idx]);
+ }
+ } break;
default: {
} //the rest don't matter
}
@@ -1819,6 +1894,15 @@ NodePath AnimationTree::get_advance_expression_base_node() const {
return advance_expression_base_node;
}
+void AnimationTree::set_audio_max_polyphony(int p_audio_max_polyphony) {
+ ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
+ audio_max_polyphony = p_audio_max_polyphony;
+}
+
+int AnimationTree::get_audio_max_polyphony() const {
+ return audio_max_polyphony;
+}
+
bool AnimationTree::is_state_invalid() const {
return !state.valid;
}
@@ -2034,6 +2118,9 @@ void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
+ ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationTree::set_audio_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationTree::get_audio_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationTree::get_root_motion_position);
ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationTree::get_root_motion_rotation);
ClassDB::bind_method(D_METHOD("get_root_motion_scale"), &AnimationTree::get_root_motion_scale);
@@ -2052,6 +2139,8 @@ void AnimationTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
+ ADD_GROUP("Audio", "audio_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
ADD_GROUP("Root Motion", "root_motion_");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index ab538feb58..c54493828f 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -35,6 +35,7 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
+#include "scene/resources/audio_stream_polyphonic.h"
class AnimationNodeBlendTree;
class AnimationNodeStartState;
@@ -252,10 +253,28 @@ private:
}
};
- struct TrackCacheAudio : public TrackCache {
- bool playing = false;
+ // Audio stream information for each audio stream placed on the track.
+ struct PlayingAudioStreamInfo {
+ AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic.
double start = 0.0;
double len = 0.0;
+ };
+
+ // Audio track information for mixng and ending.
+ struct PlayingAudioTrackInfo {
+ HashMap<int, PlayingAudioStreamInfo> stream_info;
+ double length = 0.0;
+ double time = 0.0;
+ real_t volume = 0.0;
+ bool loop = false;
+ bool backward = false;
+ bool use_blend = false;
+ };
+
+ struct TrackCacheAudio : public TrackCache {
+ Ref<AudioStreamPolyphonic> audio_stream;
+ Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
+ HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Key is Animation resource ObjectID.
TrackCacheAudio() {
type = Animation::TYPE_AUDIO;
@@ -272,6 +291,7 @@ private:
HashMap<NodePath, TrackCache *> track_cache;
HashSet<TrackCache *> playing_caches;
+ Vector<Node *> playing_audio_stream_players;
Ref<AnimationNode> root;
NodePath advance_expression_base_node = NodePath(String("."));
@@ -279,6 +299,7 @@ private:
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
bool active = false;
NodePath animation_player;
+ int audio_max_polyphony = 32;
AnimationNode::State state;
bool cache_valid = false;
@@ -287,6 +308,8 @@ private:
void _setup_animation_player();
void _animation_player_changed();
void _clear_caches();
+ void _clear_playing_caches();
+ void _clear_audio_streams();
bool _update_caches(AnimationPlayer *player);
void _process_graph(double p_delta);
@@ -348,6 +371,9 @@ public:
void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node);
NodePath get_advance_expression_base_node() const;
+ void set_audio_max_polyphony(int p_audio_max_polyphony);
+ int get_audio_max_polyphony() const;
+
PackedStringArray get_configuration_warnings() const override;
bool is_state_invalid() const;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index d40fc10441..7533a56b59 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -307,6 +307,10 @@ void AudioStreamPlayer::_bus_layout_changed() {
notify_property_list_changed();
}
+bool AudioStreamPlayer::has_stream_playback() {
+ return !stream_playbacks.is_empty();
+}
+
Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
return stream_playbacks[stream_playbacks.size() - 1];
@@ -347,6 +351,7 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer::set_max_polyphony);
ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer::get_max_polyphony);
+ ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer::has_stream_playback);
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 5368391073..d1f6fca2ee 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -107,6 +107,7 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ bool has_stream_playback();
Ref<AudioStreamPlayback> get_stream_playback();
AudioStreamPlayer();
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c977d9d2fb..b084cb5bea 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -3088,6 +3088,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
}
code_completion_options.append_array(completion_options_casei);
+ code_completion_options.append_array(completion_options_substr);
+ code_completion_options.append_array(completion_options_substr_casei);
code_completion_options.append_array(completion_options_subseq);
code_completion_options.append_array(completion_options_subseq_casei);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index af52f6664a..dc23bcb14a 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -920,7 +920,8 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
scaled_points.push_back(points[i] * p_zoom);
}
- p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased);
+ // Thickness below 0.5 doesn't look good on the graph or its minimap.
+ p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased);
}
void GraphEdit::_connections_layer_draw() {
@@ -1088,7 +1089,7 @@ void GraphEdit::_minimap_draw() {
from_color = from_color.lerp(activity_color, E.activity);
to_color = to_color.lerp(activity_color, E.activity);
}
- _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
+ _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
}
// Draw the "camera" viewport.
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 3ceb27b3e6..869d12b4df 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -663,18 +663,18 @@ void Window::set_visible(bool p_visible) {
return;
}
- visible = p_visible;
-
if (!is_inside_tree()) {
+ visible = p_visible;
return;
}
- if (updating_child_controls) {
- _update_child_controls();
- }
-
ERR_FAIL_COND_MSG(get_parent() == nullptr, "Can't change visibility of main window.");
+ visible = p_visible;
+
+ // Stop any queued resizing, as the window will be resized right now.
+ updating_child_controls = false;
+
Viewport *embedder_vp = _get_embedder();
if (!embedder_vp) {
@@ -833,7 +833,7 @@ bool Window::is_visible() const {
}
void Window::_update_window_size() {
- // Force window to respect size limitations of rendering server
+ // Force window to respect size limitations of rendering server.
RenderingServer *rendering_server = RenderingServer::get_singleton();
if (rendering_server) {
Size2i max_window_size = rendering_server->get_maximum_viewport_size();
@@ -1130,6 +1130,7 @@ void Window::_notification(int p_what) {
case NOTIFICATION_READY: {
if (wrap_controls) {
+ // Finish any resizing immediately so it doesn't interfere on stuff overriding _ready().
_update_child_controls();
}
} break;
@@ -1138,9 +1139,7 @@ void Window::_notification(int p_what) {
_invalidate_theme_cache();
_update_theme_item_cache();
- if (embedder) {
- embedder->_sub_window_update(this);
- } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
+ if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
String tr_title = atr(title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
@@ -1152,8 +1151,6 @@ void Window::_notification(int p_what) {
#endif
DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
}
-
- child_controls_changed();
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -1254,8 +1251,13 @@ Vector<Vector2> Window::get_mouse_passthrough_polygon() const {
void Window::set_wrap_controls(bool p_enable) {
wrap_controls = p_enable;
- if (wrap_controls) {
- child_controls_changed();
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (updating_child_controls) {
+ _update_child_controls();
} else {
_update_window_size();
}
@@ -1282,23 +1284,23 @@ Size2 Window::_get_contents_minimum_size() const {
return max;
}
-void Window::_update_child_controls() {
- if (!updating_child_controls) {
+void Window::child_controls_changed() {
+ if (!is_inside_tree() || !visible || updating_child_controls) {
return;
}
- _update_window_size();
-
- updating_child_controls = false;
+ updating_child_controls = true;
+ call_deferred(SNAME("_update_child_controls"));
}
-void Window::child_controls_changed() {
- if (!is_inside_tree() || !visible || updating_child_controls) {
+void Window::_update_child_controls() {
+ if (!updating_child_controls) {
return;
}
- updating_child_controls = true;
- call_deferred(SNAME("_update_child_controls"));
+ _update_window_size();
+
+ updating_child_controls = false;
}
bool Window::_can_consume_input_events() const {
@@ -1647,7 +1649,24 @@ void Window::_invalidate_theme_cache() {
void Window::_update_theme_item_cache() {
// Request an update on the next frame to reflect theme changes.
// Updating without a delay can cause a lot of lag.
- child_controls_changed();
+ if (!wrap_controls) {
+ updating_embedded_window = true;
+ call_deferred(SNAME("_update_embedded_window"));
+ } else {
+ child_controls_changed();
+ }
+}
+
+void Window::_update_embedded_window() {
+ if (!updating_embedded_window) {
+ return;
+ }
+
+ if (embedder) {
+ embedder->_sub_window_update(this);
+ };
+
+ updating_embedded_window = false;
}
void Window::set_theme_type_variation(const StringName &p_theme_type) {
@@ -2201,6 +2220,7 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed);
ClassDB::bind_method(D_METHOD("_update_child_controls"), &Window::_update_child_controls);
+ ClassDB::bind_method(D_METHOD("_update_embedded_window"), &Window::_update_embedded_window);
ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme);
ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme);
diff --git a/scene/main/window.h b/scene/main/window.h
index 18841a4f1a..182caf5f0c 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -116,6 +116,7 @@ private:
bool exclusive = false;
bool wrap_controls = false;
bool updating_child_controls = false;
+ bool updating_embedded_window = false;
bool clamp_to_embedder = false;
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
@@ -123,6 +124,7 @@ private:
bool auto_translate = true;
void _update_child_controls();
+ void _update_embedded_window();
Size2i content_scale_size;
ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 50f3015814..bfbc92a8d4 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -127,6 +127,10 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
}
return true;
+ } else if (what == "use_blend") {
+ if (track_get_type(track) == TYPE_AUDIO) {
+ audio_track_set_use_blend(track, p_value);
+ }
} else if (what == "interp") {
track_set_interpolation_type(track, InterpolationType(p_value.operator int()));
} else if (what == "loop_wrap") {
@@ -528,7 +532,10 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
}
return true;
-
+ } else if (what == "use_blend") {
+ if (track_get_type(track) == TYPE_AUDIO) {
+ r_ret = audio_track_is_use_blend(track);
+ }
} else if (what == "interp") {
r_ret = track_get_interpolation_type(track);
} else if (what == "loop_wrap") {
@@ -834,6 +841,9 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
+ if (track_get_type(i) == TYPE_AUDIO) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/use_blend", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ }
}
}
@@ -3581,6 +3591,27 @@ real_t Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
return at->values[p_key].value.end_offset;
}
+void Animation::audio_track_set_use_blend(int p_track, bool p_enable) {
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND(t->type != TYPE_AUDIO);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ at->use_blend = p_enable;
+ emit_changed();
+}
+
+bool Animation::audio_track_is_use_blend(int p_track) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, false);
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+
+ return at->use_blend;
+}
+
//
int Animation::animation_track_insert_key(int p_track, double p_time, const StringName &p_animation) {
@@ -3813,6 +3844,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "track_idx", "key_idx"), &Animation::audio_track_get_key_stream);
ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset);
ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_set_use_blend", "track_idx", "enable"), &Animation::audio_track_set_use_blend);
+ ClassDB::bind_method(D_METHOD("audio_track_is_use_blend", "track_idx"), &Animation::audio_track_is_use_blend);
ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key);
ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
@@ -4691,6 +4724,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
data_tracks.resize(tracks_to_compress.size());
time_tracks.resize(tracks_to_compress.size());
+ uint32_t needed_min_page_size = base_page_size;
for (uint32_t i = 0; i < data_tracks.size(); i++) {
data_tracks[i].split_tolerance = p_split_tolerance;
if (track_get_type(tracks_to_compress[i]) == TYPE_BLEND_SHAPE) {
@@ -4698,7 +4732,12 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
} else {
data_tracks[i].components = 3;
}
+ needed_min_page_size += data_tracks[i].data.size() + data_tracks[i].get_temp_packet_size();
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ needed_min_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits
}
+ ERR_FAIL_COND_MSG(p_page_size < needed_min_page_size, "Cannot compress with the given page size");
while (true) {
// Begin by finding the keyframe in all tracks with the time closest to the current time
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 00a0761e0a..2c2ddb7095 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -213,6 +213,7 @@ private:
struct AudioTrack : public Track {
Vector<TKey<AudioKey>> values;
+ bool use_blend = true;
AudioTrack() {
type = TYPE_AUDIO;
@@ -447,6 +448,8 @@ public:
Ref<Resource> audio_track_get_key_stream(int p_track, int p_key) const;
real_t audio_track_get_key_start_offset(int p_track, int p_key) const;
real_t audio_track_get_key_end_offset(int p_track, int p_key) const;
+ void audio_track_set_use_blend(int p_track, bool p_enable);
+ bool audio_track_is_use_blend(int p_track) const;
int animation_track_insert_key(int p_track, double p_time, const StringName &p_animation);
void animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation);
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index a09dbd50cf..fd2be9ba22 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -55,6 +55,12 @@ void Shader::set_path(const String &p_path, bool p_take_over) {
RS::get_singleton()->shader_set_path_hint(shader, p_path);
}
+void Shader::set_include_path(const String &p_path) {
+ // Used only if the shader does not have a resource path set,
+ // for example during loading stage or when created by code.
+ include_path = p_path;
+}
+
void Shader::set_code(const String &p_code) {
for (Ref<ShaderInclude> E : include_dependencies) {
E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed));
@@ -80,11 +86,15 @@ void Shader::set_code(const String &p_code) {
HashSet<Ref<ShaderInclude>> new_include_dependencies;
{
+ String path = get_path();
+ if (path.is_empty()) {
+ path = include_path;
+ }
// Preprocessor must run here and not in the server because:
// 1) Need to keep track of include dependencies at resource level
// 2) Server does not do interaction with Resource filetypes, this is a scene level feature.
ShaderPreprocessor preprocessor;
- preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_include_dependencies);
+ preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_include_dependencies);
}
// This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
@@ -231,6 +241,7 @@ Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const Strin
String str;
str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ shader->set_include_path(p_path);
shader->set_code(str);
if (r_error) {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 55608b6c11..ca889940ef 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -56,6 +56,7 @@ private:
Mode mode = MODE_SPATIAL;
HashSet<Ref<ShaderInclude>> include_dependencies;
String code;
+ String include_path;
HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures;
@@ -72,6 +73,7 @@ public:
virtual Mode get_mode() const;
virtual void set_path(const String &p_path, bool p_take_over = false) override;
+ void set_include_path(const String &p_path);
void set_code(const String &p_code);
String get_code() const;
diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp
index cd5e9861f7..68137cbec0 100644
--- a/scene/resources/shader_include.cpp
+++ b/scene/resources/shader_include.cpp
@@ -45,9 +45,14 @@ void ShaderInclude::set_code(const String &p_code) {
}
{
+ String path = get_path();
+ if (path.is_empty()) {
+ path = include_path;
+ }
+
String pp_code;
ShaderPreprocessor preprocessor;
- preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_dependencies);
+ preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_dependencies);
}
// This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
@@ -64,6 +69,10 @@ String ShaderInclude::get_code() const {
return code;
}
+void ShaderInclude::set_include_path(const String &p_path) {
+ include_path = p_path;
+}
+
void ShaderInclude::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_code", "code"), &ShaderInclude::set_code);
ClassDB::bind_method(D_METHOD("get_code"), &ShaderInclude::get_code);
@@ -86,6 +95,7 @@ Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, cons
String str;
str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ shader_inc->set_include_path(p_path);
shader_inc->set_code(str);
if (r_error) {
diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h
index 04f4f5cf84..a8949b327e 100644
--- a/scene/resources/shader_include.h
+++ b/scene/resources/shader_include.h
@@ -42,6 +42,7 @@ class ShaderInclude : public Resource {
private:
String code;
+ String include_path;
HashSet<Ref<ShaderInclude>> dependencies;
void _dependency_changed();
@@ -51,6 +52,8 @@ protected:
public:
void set_code(const String &p_text);
String get_code() const;
+
+ void set_include_path(const String &p_path);
};
class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader {
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index b5a68ef14b..58a638804d 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -991,6 +991,28 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
return navigation_layers[p_layer_index].layers;
}
+void TileSet::set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive.");
+
+ uint32_t _navigation_layers = get_navigation_layer_layers(p_layer_index);
+
+ if (p_value) {
+ _navigation_layers |= 1 << (p_layer_number - 1);
+ } else {
+ _navigation_layers &= ~(1 << (p_layer_number - 1));
+ }
+
+ set_navigation_layer_layers(p_layer_index, _navigation_layers);
+}
+
+bool TileSet::get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive.");
+
+ return get_navigation_layer_layers(p_layer_index) & (1 << (p_layer_number - 1));
+}
+
// Custom data.
int TileSet::get_custom_data_layers_count() const {
return custom_data_layers.size();
@@ -2533,6 +2555,11 @@ void TileSet::_compatibility_conversion() {
bool flip_v = flags & 2;
bool transpose = flags & 4;
+ Transform2D xform;
+ xform = flip_h ? xform.scaled(Size2(-1, 1)) : xform;
+ xform = flip_v ? xform.scaled(Size2(1, -1)) : xform;
+ xform = transpose ? xform.rotated(Math_PI).scaled(Size2(-1, -1)) : xform;
+
int alternative_tile = 0;
if (!atlas_source->has_tile(coords)) {
atlas_source->create_tile(coords);
@@ -2569,14 +2596,26 @@ void TileSet::_compatibility_conversion() {
if (ctd->occluder.is_valid()) {
if (get_occlusion_layers_count() < 1) {
add_occlusion_layer();
+ };
+ Ref<OccluderPolygon2D> occluder = ctd->occluder->duplicate();
+ Vector<Vector2> polygon = ctd->occluder->get_polygon();
+ for (int index = 0; index < polygon.size(); index++) {
+ polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0);
}
- tile_data->set_occluder(0, ctd->occluder);
+ occluder->set_polygon(polygon);
+ tile_data->set_occluder(0, occluder);
}
if (ctd->navigation.is_valid()) {
if (get_navigation_layers_count() < 1) {
add_navigation_layer();
}
- tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
+ Ref<NavigationPolygon> navigation = ctd->navigation->duplicate();
+ Vector<Vector2> vertices = navigation->get_vertices();
+ for (int index = 0; index < vertices.size(); index++) {
+ vertices.write[index] = xform.xform(vertices[index] - ctd->region.get_size() / 2.0);
+ }
+ navigation->set_vertices(vertices);
+ tile_data->set_navigation_polygon(0, navigation);
}
tile_data->set_z_index(ctd->z_index);
@@ -2594,7 +2633,7 @@ void TileSet::_compatibility_conversion() {
if (convex_shape.is_valid()) {
Vector<Vector2> polygon = convex_shape->get_points();
for (int point_index = 0; point_index < polygon.size(); point_index++) {
- polygon.write[point_index] = csd.transform.xform(polygon[point_index]);
+ polygon.write[point_index] = xform.xform(csd.transform.xform(polygon[point_index]) - ctd->region.get_size() / 2.0);
}
tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1);
int index = tile_data->get_collision_polygons_count(0) - 1;
@@ -2605,9 +2644,15 @@ void TileSet::_compatibility_conversion() {
}
}
}
+ // Update the size count.
+ if (!compatibility_size_count.has(ctd->region.get_size())) {
+ compatibility_size_count[ctd->region.get_size()] = 0;
+ }
+ compatibility_size_count[ctd->region.get_size()]++;
} break;
case COMPATIBILITY_TILE_MODE_AUTO_TILE: {
// Not supported. It would need manual conversion.
+ WARN_PRINT_ONCE("Could not convert 3.x autotiles to 4.x. This operation cannot be done automatically, autotiles must be re-created using the terrain system.");
} break;
case COMPATIBILITY_TILE_MODE_ATLAS_TILE: {
atlas_source->set_margins(ctd->region.get_position());
@@ -2624,6 +2669,11 @@ void TileSet::_compatibility_conversion() {
bool flip_v = flags & 2;
bool transpose = flags & 4;
+ Transform2D xform;
+ xform = flip_h ? xform.scaled(Size2(-1, 1)) : xform;
+ xform = flip_v ? xform.scaled(Size2(1, -1)) : xform;
+ xform = transpose ? xform.rotated(Math_PI).scaled(Size2(-1, -1)) : xform;
+
int alternative_tile = 0;
if (!atlas_source->has_tile(coords)) {
atlas_source->create_tile(coords);
@@ -2661,13 +2711,25 @@ void TileSet::_compatibility_conversion() {
if (get_occlusion_layers_count() < 1) {
add_occlusion_layer();
}
- tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
+ Ref<OccluderPolygon2D> occluder = ctd->autotile_occluder_map[coords]->duplicate();
+ Vector<Vector2> polygon = ctd->occluder->get_polygon();
+ for (int index = 0; index < polygon.size(); index++) {
+ polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0);
+ }
+ occluder->set_polygon(polygon);
+ tile_data->set_occluder(0, occluder);
}
if (ctd->autotile_navpoly_map.has(coords)) {
if (get_navigation_layers_count() < 1) {
add_navigation_layer();
}
- tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
+ Ref<NavigationPolygon> navigation = ctd->autotile_navpoly_map[coords]->duplicate();
+ Vector<Vector2> vertices = navigation->get_vertices();
+ for (int index = 0; index < vertices.size(); index++) {
+ vertices.write[index] = xform.xform(vertices[index] - ctd->region.get_size() / 2.0);
+ }
+ navigation->set_vertices(vertices);
+ tile_data->set_navigation_polygon(0, navigation);
}
if (ctd->autotile_priority_map.has(coords)) {
tile_data->set_probability(ctd->autotile_priority_map[coords]);
@@ -2689,7 +2751,7 @@ void TileSet::_compatibility_conversion() {
if (convex_shape.is_valid()) {
Vector<Vector2> polygon = convex_shape->get_points();
for (int point_index = 0; point_index < polygon.size(); point_index++) {
- polygon.write[point_index] = csd.transform.xform(polygon[point_index]);
+ polygon.write[point_index] = xform.xform(csd.transform.xform(polygon[point_index]) - ctd->autotile_tile_size / 2.0);
}
tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1);
int index = tile_data->get_collision_polygons_count(0) - 1;
@@ -2712,6 +2774,12 @@ void TileSet::_compatibility_conversion() {
}
}
}
+
+ // Update the size count.
+ if (!compatibility_size_count.has(ctd->region.get_size())) {
+ compatibility_size_count[ctd->autotile_tile_size] = 0;
+ }
+ compatibility_size_count[ctd->autotile_tile_size] += atlas_size.x * atlas_size.y;
} break;
}
@@ -2728,7 +2796,18 @@ void TileSet::_compatibility_conversion() {
}
}
- // Reset compatibility data
+ // Update the TileSet tile_size according to the most common size found.
+ Vector2i max_size = get_tile_size();
+ int max_count = 0;
+ for (KeyValue<Vector2i, int> kv : compatibility_size_count) {
+ if (kv.value > max_count) {
+ max_size = kv.key;
+ max_count = kv.value;
+ }
+ }
+ set_tile_size(max_size);
+
+ // Reset compatibility data (besides the histogram counts)
for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
memdelete(E.value);
}
@@ -2909,6 +2988,10 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
}
ctd->shapes.push_back(csd);
}
+ } else if (what == "occluder") {
+ ctd->occluder = p_value;
+ } else if (what == "navigation") {
+ ctd->navigation = p_value;
/*
// IGNORED FOR NOW, they seem duplicated data compared to the shapes array
@@ -3387,6 +3470,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer);
ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
+ ClassDB::bind_method(D_METHOD("set_navigation_layer_layer_value", "layer_index", "layer_number", "value"), &TileSet::set_navigation_layer_layer_value);
+ ClassDB::bind_method(D_METHOD("get_navigation_layer_layer_value", "layer_index", "layer_number"), &TileSet::get_navigation_layer_layer_value);
// Custom data
ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
@@ -4282,19 +4367,11 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int
return Rect2(origin, region_size);
}
-Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const {
- ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
- ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords)));
- ERR_FAIL_COND_V(!tile_set, Vector2i());
+bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const {
+ Size2 size = get_tile_texture_region(p_atlas_coords).size;
+ Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size);
- Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2;
- margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
- Vector2i effective_texture_offset = get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_offset();
- if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
- effective_texture_offset = effective_texture_offset.clamp(-margin, margin);
- }
-
- return effective_texture_offset;
+ return rect.has_point(p_position);
}
// Getters for texture and tile region (padded or not)
@@ -5046,7 +5123,7 @@ TileData *TileData::duplicate() {
output->flip_h = flip_h;
output->flip_v = flip_v;
output->transpose = transpose;
- output->tex_offset = tex_offset;
+ output->texture_origin = texture_origin;
output->material = material;
output->modulate = modulate;
output->z_index = z_index;
@@ -5096,13 +5173,13 @@ bool TileData::get_transpose() const {
return transpose;
}
-void TileData::set_texture_offset(Vector2i p_texture_offset) {
- tex_offset = p_texture_offset;
+void TileData::set_texture_origin(Vector2i p_texture_origin) {
+ texture_origin = p_texture_origin;
emit_signal(SNAME("changed"));
}
-Vector2i TileData::get_texture_offset() const {
- return tex_offset;
+Vector2i TileData::get_texture_origin() const {
+ return texture_origin;
}
void TileData::set_material(Ref<Material> p_material) {
@@ -5390,6 +5467,13 @@ Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const {
}
bool TileData::_set(const StringName &p_name, const Variant &p_value) {
+#ifndef DISABLE_DEPRECATED
+ if (p_name == "texture_offset") {
+ texture_origin = p_value;
+ return true;
+ }
+#endif
+
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
@@ -5511,6 +5595,13 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
}
bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
+#ifndef DISABLE_DEPRECATED
+ if (p_name == "texture_offset") {
+ r_ret = texture_origin;
+ return true;
+ }
+#endif
+
Vector<String> components = String(p_name).split("/", true, 2);
if (tile_set) {
@@ -5692,8 +5783,8 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose);
ClassDB::bind_method(D_METHOD("set_material", "material"), &TileData::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &TileData::get_material);
- ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset);
- ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset);
+ ClassDB::bind_method(D_METHOD("set_texture_origin", "texture_origin"), &TileData::set_texture_origin);
+ ClassDB::bind_method(D_METHOD("get_texture_origin"), &TileData::get_texture_origin);
ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate);
ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate);
ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index);
@@ -5746,7 +5837,7 @@ void TileData::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_origin", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_origin", "get_texture_origin");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index");
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index c2ed798f2b..ad25629a1c 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -198,6 +198,7 @@ private:
HashMap<int, CompatibilityTileData *> compatibility_data;
HashMap<int, int> compatibility_tilemap_mapping_tile_modes;
HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping;
+ HashMap<Vector2i, int> compatibility_size_count;
void _compatibility_conversion();
@@ -475,6 +476,8 @@ public:
void remove_navigation_layer(int p_index);
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
+ void set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value);
+ bool get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const;
// Custom data
int get_custom_data_layers_count() const;
@@ -719,7 +722,7 @@ public:
// Helpers.
Vector2i get_atlas_grid_size() const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
- Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
+ bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
// Getters for texture and tile region (padded or not)
Ref<Texture2D> get_runtime_texture() const;
@@ -785,7 +788,7 @@ private:
bool flip_h = false;
bool flip_v = false;
bool transpose = false;
- Vector2i tex_offset;
+ Vector2i texture_origin;
Ref<Material> material = Ref<Material>();
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
@@ -864,8 +867,8 @@ public:
void set_transpose(bool p_transpose);
bool get_transpose() const;
- void set_texture_offset(Vector2i p_texture_offset);
- Vector2i get_texture_offset() const;
+ void set_texture_origin(Vector2i p_texture_origin);
+ Vector2i get_texture_origin() const;
void set_material(Ref<Material> p_material);
Ref<Material> get_material() const;
void set_modulate(Color p_modulate);