summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/AnimatedSprite3D.xml6
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--platform/web/detect.py4
-rw-r--r--platform/web/display_server_web.cpp4
-rw-r--r--platform/web/js/engine/config.js3
-rw-r--r--platform/web/js/libs/audio.worklet.js2
-rw-r--r--platform/web/js/libs/library_godot_audio.js9
-rw-r--r--platform/web/js/libs/library_godot_os.js14
-rw-r--r--platform/web/web_main.cpp26
-rw-r--r--scene/2d/animated_sprite_2d.cpp58
-rw-r--r--scene/3d/sprite_3d.cpp180
-rw-r--r--scene/3d/sprite_3d.h11
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp4
14 files changed, 223 insertions, 106 deletions
diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml
index 0bc5484e3a..58d3ca6ad3 100644
--- a/doc/classes/AnimatedSprite3D.xml
+++ b/doc/classes/AnimatedSprite3D.xml
@@ -13,8 +13,9 @@
<method name="play">
<return type="void" />
<param index="0" name="anim" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="backwards" type="bool" default="false" />
<description>
- Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played.
+ Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation will be played in reverse.
</description>
</method>
<method name="stop">
@@ -37,6 +38,9 @@
<member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false">
If [code]true[/code], the [member animation] is currently playing.
</member>
+ <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
+ The animation speed is multiplied by this value.
+ </member>
</members>
<signals>
<signal name="animation_finished">
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 10babad378..1cff2181af 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1077,10 +1077,12 @@ Error GDScript::load_source_code(const String &p_path) {
}
source = s;
+ path = p_path;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
-#endif
- path = p_path;
+ set_edited(false);
+ set_last_modified_time(FileAccess::get_modified_time(path));
+#endif // TOOLS_ENABLED
return OK;
}
diff --git a/platform/web/detect.py b/platform/web/detect.py
index e055af8400..08f964db92 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -227,3 +227,7 @@ def configure(env):
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
+
+ # This workaround creates a closure that prevents the garbage collector from freeing the WebGL context.
+ # We also only use WebGL2, and changing context version is not widely supported anyway.
+ env.Append(LINKFLAGS=["-s", "GL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"])
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index b36f9d14a4..38ac6b23f4 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -764,10 +764,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
if (wants_webgl2 && !webgl2_init_failed) {
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes);
- //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
- attributes.alpha = true;
+ attributes.alpha = OS::get_singleton()->is_layered_allowed();
attributes.antialias = false;
attributes.majorVersion = 2;
+ attributes.explicitSwapControl = true;
webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes);
if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 9c4b6c2012..41be7b2512 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -317,7 +317,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
if (!(this.canvas instanceof HTMLCanvasElement)) {
const nodes = document.getElementsByTagName('canvas');
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
- this.canvas = nodes[0];
+ const first = nodes[0];
+ this.canvas = /** @type {!HTMLCanvasElement} */ (first);
}
if (!this.canvas) {
throw new Error('No canvas found in page');
diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js
index ea4d8cb221..daf5c9ef12 100644
--- a/platform/web/js/libs/audio.worklet.js
+++ b/platform/web/js/libs/audio.worklet.js
@@ -133,6 +133,8 @@ class GodotProcessor extends AudioWorkletProcessor {
this.running = false;
this.output = null;
this.input = null;
+ this.lock = null;
+ this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 756c1ac595..68e100cca0 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -339,16 +339,21 @@ const GodotAudioWorklet = {
if (GodotAudioWorklet.promise === null) {
return;
}
- GodotAudioWorklet.promise.then(function () {
+ const p = GodotAudioWorklet.promise;
+ p.then(function () {
GodotAudioWorklet.worklet.port.postMessage({
'cmd': 'stop',
'data': null,
});
GodotAudioWorklet.worklet.disconnect();
+ GodotAudioWorklet.worklet.port.onmessage = null;
GodotAudioWorklet.worklet = null;
GodotAudioWorklet.promise = null;
resolve();
- }).catch(function (err) { /* aborted? */ });
+ }).catch(function (err) {
+ // Aborted?
+ GodotRuntime.error(err);
+ });
});
},
},
diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js
index 377eec3234..ce64fb98c0 100644
--- a/platform/web/js/libs/library_godot_os.js
+++ b/platform/web/js/libs/library_godot_os.js
@@ -106,12 +106,14 @@ autoAddDeps(GodotConfig, '$GodotConfig');
mergeInto(LibraryManager.library, GodotConfig);
const GodotFS = {
- $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'],
+ $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'],
$GodotFS__postset: [
'Module["initFS"] = GodotFS.init;',
'Module["copyToFS"] = GodotFS.copy_to_fs;',
].join(''),
$GodotFS: {
+ // ERRNO_CODES works every odd version of emscripten, but this will break too eventually.
+ ENOENT: 44,
_idbfs: false,
_syncing: false,
_mount_points: [],
@@ -138,8 +140,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
@@ -208,8 +211,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp
index 0f4411727a..287fe48c4d 100644
--- a/platform/web/web_main.cpp
+++ b/platform/web/web_main.cpp
@@ -55,6 +55,18 @@ void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}
+void early_cleanup() {
+ emscripten_cancel_main_loop(); // After this, we can exit!
+ int exit_code = OS_Web::get_singleton()->get_exit_code();
+ memdelete(os);
+ os = nullptr;
+ emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
+}
+
+void early_cleanup_sync() {
+ emscripten_set_main_loop(early_cleanup, -1, false);
+}
+
void main_loop_callback() {
uint64_t current_ticks = os->get_ticks_usec();
@@ -87,7 +99,19 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) {
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
- Main::setup(argv[0], argc - 1, &argv[1]);
+ Error err = Main::setup(argv[0], argc - 1, &argv[1]);
+
+ // Proper shutdown in case of setup failure.
+ if (err != OK) {
+ int exit_code = (int)err;
+ if (err == ERR_HELP) {
+ exit_code = 0; // Called with --help.
+ }
+ os->set_exit_code(exit_code);
+ // Will only exit after sync.
+ godot_js_os_finish_async(early_cleanup_sync);
+ return exit_code;
+ }
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index b1b1cb23ed..09255ba834 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -175,33 +175,38 @@ void AnimatedSprite2D::_notification(int p_what) {
if (timeout <= 0) {
timeout = _get_frame_duration();
- int fc = frames->get_frame_count(animation);
- if ((!backwards && frame >= fc - 1) || (backwards && frame <= 0)) {
- if (frames->get_animation_loop(animation)) {
- if (backwards) {
- frame = fc - 1;
- } else {
- frame = 0;
- }
-
- emit_signal(SceneStringNames::get_singleton()->animation_finished);
- } else {
- if (backwards) {
+ int last_frame = frames->get_frame_count(animation) - 1;
+ if (!backwards) {
+ // Forward.
+ if (frame >= last_frame) {
+ if (frames->get_animation_loop(animation)) {
frame = 0;
- } else {
- frame = fc - 1;
- }
-
- if (!is_over) {
- is_over = true;
emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = last_frame;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
}
+ } else {
+ frame++;
}
} else {
- if (backwards) {
- frame--;
+ // Reversed.
+ if (frame <= 0) {
+ if (frames->get_animation_loop(animation)) {
+ frame = last_frame;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = 0;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
} else {
- frame++;
+ frame--;
}
}
@@ -259,14 +264,15 @@ void AnimatedSprite2D::_notification(int p_what) {
void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
if (frames.is_valid()) {
- frames->disconnect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed));
+ frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed));
}
+
frames = p_frames;
if (frames.is_valid()) {
- frames->connect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed));
+ frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed));
}
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
frame = 0;
} else {
set_frame(frame);
@@ -283,7 +289,7 @@ Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const {
}
void AnimatedSprite2D::set_frame(int p_frame) {
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
return;
}
@@ -318,7 +324,7 @@ void AnimatedSprite2D::set_speed_scale(double p_speed_scale) {
speed_scale = MAX(p_speed_scale, 0.0f);
- // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed.
_reset_timeout();
timeout -= elapsed;
}
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 4515277dc3..a4a6c211d7 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -30,7 +30,6 @@
#include "sprite_3d.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
Color SpriteBase3D::_get_color_accum() {
@@ -58,7 +57,7 @@ void SpriteBase3D::_propagate_color_changed() {
}
color_dirty = true;
- _queue_update();
+ _queue_redraw();
for (SpriteBase3D *&E : children) {
E->_propagate_color_changed();
@@ -90,7 +89,7 @@ void SpriteBase3D::_notification(int p_what) {
void SpriteBase3D::set_centered(bool p_center) {
centered = p_center;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_centered() const {
@@ -99,7 +98,7 @@ bool SpriteBase3D::is_centered() const {
void SpriteBase3D::set_offset(const Point2 &p_offset) {
offset = p_offset;
- _queue_update();
+ _queue_redraw();
}
Point2 SpriteBase3D::get_offset() const {
@@ -108,7 +107,7 @@ Point2 SpriteBase3D::get_offset() const {
void SpriteBase3D::set_flip_h(bool p_flip) {
hflip = p_flip;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_flipped_h() const {
@@ -117,7 +116,7 @@ bool SpriteBase3D::is_flipped_h() const {
void SpriteBase3D::set_flip_v(bool p_flip) {
vflip = p_flip;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::is_flipped_v() const {
@@ -127,7 +126,7 @@ bool SpriteBase3D::is_flipped_v() const {
void SpriteBase3D::set_modulate(const Color &p_color) {
modulate = p_color;
_propagate_color_changed();
- _queue_update();
+ _queue_redraw();
}
Color SpriteBase3D::get_modulate() const {
@@ -137,7 +136,7 @@ Color SpriteBase3D::get_modulate() const {
void SpriteBase3D::set_render_priority(int p_priority) {
ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
render_priority = p_priority;
- _queue_update();
+ _queue_redraw();
}
int SpriteBase3D::get_render_priority() const {
@@ -146,7 +145,7 @@ int SpriteBase3D::get_render_priority() const {
void SpriteBase3D::set_pixel_size(real_t p_amount) {
pixel_size = p_amount;
- _queue_update();
+ _queue_redraw();
}
real_t SpriteBase3D::get_pixel_size() const {
@@ -156,7 +155,7 @@ real_t SpriteBase3D::get_pixel_size() const {
void SpriteBase3D::set_axis(Vector3::Axis p_axis) {
ERR_FAIL_INDEX(p_axis, 3);
axis = p_axis;
- _queue_update();
+ _queue_redraw();
}
Vector3::Axis SpriteBase3D::get_axis() const {
@@ -171,7 +170,8 @@ void SpriteBase3D::_im_update() {
//texture->draw_rect_region(ci,dst_rect,src_rect,modulate);
}
-void SpriteBase3D::_queue_update() {
+void SpriteBase3D::_queue_redraw() {
+ // The 3D equivalent of CanvasItem.queue_redraw().
if (pending_update) {
return;
}
@@ -250,7 +250,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
- _queue_update();
+ _queue_redraw();
}
bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const {
@@ -261,7 +261,7 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const {
void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) {
ERR_FAIL_INDEX(p_mode, 3);
alpha_cut = p_mode;
- _queue_update();
+ _queue_redraw();
}
SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const {
@@ -269,9 +269,9 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const {
}
void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) {
- ERR_FAIL_INDEX(p_mode, 3);
+ ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES.
billboard_mode = p_mode;
- _queue_update();
+ _queue_redraw();
}
StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const {
@@ -281,7 +281,7 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const {
void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) {
if (texture_filter != p_filter) {
texture_filter = p_filter;
- _queue_update();
+ _queue_redraw();
}
}
@@ -329,7 +329,6 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
- ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update);
ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
@@ -368,7 +367,7 @@ SpriteBase3D::SpriteBase3D() {
}
material = RenderingServer::get_singleton()->material_create();
- // Set defaults for material, names need to match up those in StandardMaterial3D
+ // Set defaults for material, names need to match up those in StandardMaterial3D.
RS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1));
RS::get_singleton()->material_set_param(material, "specular", 0.5);
RS::get_singleton()->material_set_param(material, "metallic", 0.0);
@@ -394,7 +393,7 @@ SpriteBase3D::SpriteBase3D() {
mesh_colors.resize(4);
mesh_uvs.resize(4);
- // create basic mesh and store format information
+ // Create basic mesh and store format information.
for (int i = 0; i < 4; i++) {
mesh_normals.write[i] = Vector3(0.0, 0.0, 0.0);
mesh_tangents.write[i * 4 + 0] = 0.0;
@@ -554,7 +553,7 @@ void Sprite3D::_draw() {
AABB aabb;
- // Everything except position and UV is compressed
+ // Everything except position and UV is compressed.
uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
@@ -637,13 +636,14 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) {
return;
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update"));
+ texture->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update"));
+ texture->connect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw));
}
- _queue_update();
+
+ _queue_redraw();
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
@@ -657,7 +657,7 @@ void Sprite3D::set_region_enabled(bool p_region) {
}
region = p_region;
- _queue_update();
+ _queue_redraw();
}
bool Sprite3D::is_region_enabled() const {
@@ -668,7 +668,7 @@ void Sprite3D::set_region_rect(const Rect2 &p_region_rect) {
bool changed = region_rect != p_region_rect;
region_rect = p_region_rect;
if (region && changed) {
- _queue_update();
+ _queue_redraw();
}
}
@@ -681,7 +681,7 @@ void Sprite3D::set_frame(int p_frame) {
frame = p_frame;
- _queue_update();
+ _queue_redraw();
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -704,7 +704,7 @@ Vector2i Sprite3D::get_frame_coords() const {
void Sprite3D::set_vframes(int p_amount) {
ERR_FAIL_COND(p_amount < 1);
vframes = p_amount;
- _queue_update();
+ _queue_redraw();
notify_property_list_changed();
}
@@ -715,7 +715,7 @@ int Sprite3D::get_vframes() const {
void Sprite3D::set_hframes(int p_amount) {
ERR_FAIL_COND(p_amount < 1);
hframes = p_amount;
- _queue_update();
+ _queue_redraw();
notify_property_list_changed();
}
@@ -820,9 +820,9 @@ void AnimatedSprite3D::_draw() {
}
Ref<Texture2D> texture = frames->get_frame(animation, frame);
- if (!texture.is_valid()) {
+ if (texture.is_null()) {
set_base(RID());
- return; //no texuture no life
+ return;
}
Size2 tsize = texture->get_size();
if (tsize.x == 0 || tsize.y == 0) {
@@ -917,7 +917,7 @@ void AnimatedSprite3D::_draw() {
AABB aabb;
- // Everything except position and UV is compressed
+ // Everything except position and UV is compressed.
uint8_t *vertex_write_buffer = vertex_buffer.ptrw();
uint8_t *attribute_write_buffer = attribute_buffer.ptrw();
@@ -1056,27 +1056,51 @@ void AnimatedSprite3D::_notification(int p_what) {
double remaining = get_process_delta_time();
while (remaining) {
- double speed = frames->get_animation_speed(animation);
+ double speed = frames->get_animation_speed(animation) * speed_scale;
if (speed == 0) {
return; // Do nothing.
}
if (timeout <= 0) {
- timeout = 1.0 / speed;
-
- int fc = frames->get_frame_count(animation);
- if (frame >= fc - 1) {
- if (frames->get_animation_loop(animation)) {
- frame = 0;
+ timeout = _get_frame_duration();
+
+ int last_frame = frames->get_frame_count(animation) - 1;
+ if (!backwards) {
+ // Forward.
+ if (frame >= last_frame) {
+ if (frames->get_animation_loop(animation)) {
+ frame = 0;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = last_frame;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
} else {
- frame = fc - 1;
+ frame++;
}
- emit_signal(SceneStringNames::get_singleton()->animation_finished);
} else {
- frame++;
+ // Reversed.
+ if (frame <= 0) {
+ if (frames->get_animation_loop(animation)) {
+ frame = last_frame;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ } else {
+ frame = 0;
+ if (!is_over) {
+ is_over = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_finished);
+ }
+ }
+ } else {
+ frame--;
+ }
}
- _queue_update();
+ _queue_redraw();
+
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -1090,14 +1114,15 @@ void AnimatedSprite3D::_notification(int p_what) {
void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
if (frames.is_valid()) {
- frames->disconnect("changed", Callable(this, "_res_changed"));
+ frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed));
}
+
frames = p_frames;
if (frames.is_valid()) {
- frames->connect("changed", Callable(this, "_res_changed"));
+ frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed));
}
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
frame = 0;
} else {
set_frame(frame);
@@ -1105,7 +1130,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
notify_property_list_changed();
_reset_timeout();
- _queue_update();
+ _queue_redraw();
update_configuration_warnings();
}
@@ -1114,7 +1139,7 @@ Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const {
}
void AnimatedSprite3D::set_frame(int p_frame) {
- if (!frames.is_valid()) {
+ if (frames.is_null()) {
return;
}
@@ -1135,7 +1160,8 @@ void AnimatedSprite3D::set_frame(int p_frame) {
frame = p_frame;
_reset_timeout();
- _queue_update();
+ _queue_redraw();
+
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -1143,6 +1169,20 @@ int AnimatedSprite3D::get_frame() const {
return frame;
}
+void AnimatedSprite3D::set_speed_scale(double p_speed_scale) {
+ double elapsed = _get_frame_duration() - timeout;
+
+ speed_scale = MAX(p_speed_scale, 0.0f);
+
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed.
+ _reset_timeout();
+ timeout -= elapsed;
+}
+
+double AnimatedSprite3D::get_speed_scale() const {
+ return speed_scale;
+}
+
Rect2 AnimatedSprite3D::get_item_rect() const {
if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
return Rect2(0, 0, 1, 1);
@@ -1171,7 +1211,8 @@ Rect2 AnimatedSprite3D::get_item_rect() const {
void AnimatedSprite3D::_res_changed() {
set_frame(frame);
- _queue_update();
+
+ _queue_redraw();
}
void AnimatedSprite3D::set_playing(bool p_playing) {
@@ -1187,10 +1228,17 @@ bool AnimatedSprite3D::is_playing() const {
return playing;
}
-void AnimatedSprite3D::play(const StringName &p_animation) {
+void AnimatedSprite3D::play(const StringName &p_animation, const bool p_backwards) {
+ backwards = p_backwards;
+
if (p_animation) {
set_animation(p_animation);
+ if (frames.is_valid() && backwards && get_frame() == 0) {
+ set_frame(frames->get_frame_count(p_animation) - 1);
+ }
}
+
+ is_over = false;
set_playing(true);
}
@@ -1198,24 +1246,28 @@ void AnimatedSprite3D::stop() {
set_playing(false);
}
+double AnimatedSprite3D::_get_frame_duration() {
+ if (frames.is_valid() && frames->has_animation(animation)) {
+ double speed = frames->get_animation_speed(animation) * speed_scale;
+ if (speed > 0) {
+ return 1.0 / speed;
+ }
+ }
+ return 0.0;
+}
+
void AnimatedSprite3D::_reset_timeout() {
if (!playing) {
return;
}
- if (frames.is_valid() && frames->has_animation(animation)) {
- float speed = frames->get_animation_speed(animation);
- if (speed > 0) {
- timeout = 1.0 / speed;
- } else {
- timeout = 0;
- }
- } else {
- timeout = 0;
- }
+ timeout = _get_frame_duration();
+ is_over = false;
}
void AnimatedSprite3D::set_animation(const StringName &p_animation) {
+ ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation));
if (animation == p_animation) {
return;
}
@@ -1224,7 +1276,7 @@ void AnimatedSprite3D::set_animation(const StringName &p_animation) {
_reset_timeout();
set_frame(0);
notify_property_list_changed();
- _queue_update();
+ _queue_redraw();
}
StringName AnimatedSprite3D::get_animation() const {
@@ -1261,12 +1313,15 @@ void AnimatedSprite3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite3D::set_playing);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing);
- ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite3D::play, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop);
ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame);
ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite3D::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite3D::get_speed_scale);
+
ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed);
ADD_SIGNAL(MethodInfo("frame_changed"));
@@ -1275,6 +1330,7 @@ void AnimatedSprite3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing");
}
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 84244a2476..e6a546a76d 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -106,7 +106,7 @@ protected:
uint32_t skin_stride = 0;
uint32_t mesh_surface_format = 0;
- void _queue_update();
+ void _queue_redraw();
public:
void set_centered(bool p_center);
@@ -209,15 +209,19 @@ class AnimatedSprite3D : public SpriteBase3D {
Ref<SpriteFrames> frames;
bool playing = false;
+ bool backwards = false;
StringName animation = "default";
int frame = 0;
+ float speed_scale = 1.0f;
bool centered = false;
+ bool is_over = false;
double timeout = 0.0;
void _res_changed();
+ double _get_frame_duration();
void _reset_timeout();
RID last_shader;
@@ -233,7 +237,7 @@ public:
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
- void play(const StringName &p_animation = StringName());
+ void play(const StringName &p_animation = StringName(), const bool p_backwards = false);
void stop();
void set_playing(bool p_playing);
@@ -245,6 +249,9 @@ public:
void set_frame(int p_frame);
int get_frame() const;
+ void set_speed_scale(double p_speed_scale);
+ double get_speed_scale() const;
+
virtual Rect2 get_item_rect() const override;
virtual TypedArray<String> get_configuration_warnings() const override;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index a0f6e69fd5..984eb12a9c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -124,7 +124,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RenderS
render_buffers = p_render_buffers;
ERR_FAIL_NULL(render_buffers);
- bool msaa_3d = render_buffers->get_msaa_3d();
+ RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d();
if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index 576ec81124..84b65371e0 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -535,7 +535,9 @@ void RenderSceneBuffersRD::ensure_velocity() {
RD::TEXTURE_SAMPLES_8,
};
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, ts[msaa_3d]);
+ RD::TextureSamples texture_samples = ts[msaa_3d];
+
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, texture_samples);
}
create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits);