diff options
-rw-r--r-- | doc/classes/AnimatedSprite3D.xml | 6 | ||||
-rw-r--r-- | modules/gdscript/gdscript.cpp | 6 | ||||
-rw-r--r-- | platform/web/detect.py | 4 | ||||
-rw-r--r-- | platform/web/display_server_web.cpp | 4 | ||||
-rw-r--r-- | platform/web/js/engine/config.js | 3 | ||||
-rw-r--r-- | platform/web/js/libs/audio.worklet.js | 2 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_audio.js | 9 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_os.js | 14 | ||||
-rw-r--r-- | platform/web/web_main.cpp | 26 | ||||
-rw-r--r-- | scene/2d/animated_sprite_2d.cpp | 58 | ||||
-rw-r--r-- | scene/3d/sprite_3d.cpp | 180 | ||||
-rw-r--r-- | scene/3d/sprite_3d.h | 11 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp | 4 |
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="&""" /> + <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); |