diff options
Diffstat (limited to 'scene')
48 files changed, 964 insertions, 226 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 8e91dce425..89a0479de3 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -242,7 +242,7 @@ void AudioStreamPlayer2D::seek(float p_seconds) { void AudioStreamPlayer2D::stop() { setplay.set(-1); - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer2D::is_playing() const { @@ -430,7 +430,7 @@ void AudioStreamPlayer2D::_bind_methods() { } AudioStreamPlayer2D::AudioStreamPlayer2D() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), callable_mp(this, &AudioStreamPlayer2D::stop), true)); cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength"); set_hide_clip_children(true); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 1d3f1ceada..bfbdb49f22 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -688,6 +688,7 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->particles_set_speed_scale(particles, 0); } set_process_internal(true); + set_physics_process_internal(true); previous_position = get_global_position(); } break; @@ -711,15 +712,6 @@ void GPUParticles2D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PROCESS: { - const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) / - get_process_delta_time(); - - if (velocity != previous_velocity) { - RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); - previous_velocity = velocity; - } - previous_position = get_global_position(); - if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { @@ -739,6 +731,19 @@ void GPUParticles2D::_notification(int p_what) { } } } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Update velocity in physics process, so that velocity calculations remain correct + // if the physics tick rate is lower than the rendered framerate (especially without physics interpolation). + const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) / + get_physics_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } + previous_position = get_global_position(); + } break; } } diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 04ba550888..111f5a7b78 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -41,6 +41,9 @@ void NavigationLink2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional); ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional); @@ -106,12 +109,7 @@ bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const { void NavigationLink2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (enabled) { - NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); - } - current_global_transform = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + _link_enter_navigation_map(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -120,36 +118,15 @@ void NavigationLink2D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); - if (is_inside_tree()) { - Transform2D new_global_transform = get_global_transform(); - if (current_global_transform != new_global_transform) { - current_global_transform = new_global_transform; - NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); - queue_redraw(); - } - } + _link_update_transform(); } break; case NOTIFICATION_EXIT_TREE: { - NavigationServer2D::get_singleton()->link_set_map(link, RID()); + _link_exit_navigation_map(); } break; case NOTIFICATION_DRAW: { #ifdef DEBUG_ENABLED - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) { - Color color; - if (enabled) { - color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); - } else { - color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); - } - - real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - - draw_line(get_start_position(), get_end_position(), color); - draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); - draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); - } + _update_debug_mesh(); #endif // DEBUG_ENABLED } break; } @@ -188,15 +165,32 @@ void NavigationLink2D::set_enabled(bool p_enabled) { enabled = p_enabled; - NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); + NavigationServer2D::get_singleton()->link_set_enabled(link, enabled); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } +void NavigationLink2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer2D::get_singleton()->link_set_map(link, map_override); +} + +RID NavigationLink2D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_2d()->get_navigation_map(); + } + return RID(); +} + void NavigationLink2D::set_bidirectional(bool p_bidirectional) { if (bidirectional == p_bidirectional) { return; @@ -255,9 +249,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) { update_configuration_warnings(); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } @@ -277,9 +269,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) { update_configuration_warnings(); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } @@ -347,6 +337,69 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const { return warnings; } +void NavigationLink2D::_link_enter_navigation_map() { + if (!is_inside_tree()) { + return; + } + + if (map_override.is_valid()) { + NavigationServer2D::get_singleton()->link_set_map(link, map_override); + } else { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + } + + current_global_transform = get_global_transform(); + + NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + NavigationServer2D::get_singleton()->link_set_enabled(link, enabled); + + queue_redraw(); +} + +void NavigationLink2D::_link_exit_navigation_map() { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); +} + +void NavigationLink2D::_link_update_transform() { + if (!is_inside_tree()) { + return; + } + + Transform2D new_global_transform = get_global_transform(); + if (current_global_transform != new_global_transform) { + current_global_transform = new_global_transform; + NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + queue_redraw(); + } +} + +#ifdef DEBUG_ENABLED +void NavigationLink2D::_update_debug_mesh() { + if (!is_inside_tree()) { + return; + } + + if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) { + return; + } + + Color color; + if (enabled) { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); + } else { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); + } + + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + draw_line(get_start_position(), get_end_position(), color); + draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); +} +#endif // DEBUG_ENABLED + NavigationLink2D::NavigationLink2D() { link = NavigationServer2D::get_singleton()->link_create(); diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 2929691c04..c724096607 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -38,6 +38,7 @@ class NavigationLink2D : public Node2D { bool enabled = true; RID link; + RID map_override; bool bidirectional = true; uint32_t navigation_layers = 1; Vector2 end_position; @@ -47,6 +48,10 @@ class NavigationLink2D : public Node2D { Transform2D current_global_transform; +#ifdef DEBUG_ENABLED + void _update_debug_mesh(); +#endif // DEBUG_ENABLED + protected: static void _bind_methods(); void _notification(int p_what); @@ -66,6 +71,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_bidirectional(bool p_bidirectional); bool is_bidirectional() const { return bidirectional; } @@ -97,6 +105,11 @@ public: NavigationLink2D(); ~NavigationLink2D(); + +private: + void _link_enter_navigation_map(); + void _link_exit_navigation_map(); + void _link_update_transform(); }; #endif // NAVIGATION_LINK_2D_H diff --git a/scene/2d/physics/shape_cast_2d.cpp b/scene/2d/physics/shape_cast_2d.cpp index 00be84b622..b92978bcad 100644 --- a/scene/2d/physics/shape_cast_2d.cpp +++ b/scene/2d/physics/shape_cast_2d.cpp @@ -382,7 +382,7 @@ bool ShapeCast2D::is_collide_with_bodies_enabled() const { return collide_with_bodies; } -Array ShapeCast2D::_get_collision_result() const { +Array ShapeCast2D::get_collision_result() const { Array ret; for (int i = 0; i < result.size(); ++i) { @@ -464,7 +464,7 @@ void ShapeCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast2D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast2D::is_collide_with_bodies_enabled); - ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast2D::_get_collision_result); + ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast2D::get_collision_result); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape"); @@ -473,7 +473,7 @@ void ShapeCast2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:px"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result"); ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); diff --git a/scene/2d/physics/shape_cast_2d.h b/scene/2d/physics/shape_cast_2d.h index 6b8fd5b798..d866dd4edb 100644 --- a/scene/2d/physics/shape_cast_2d.h +++ b/scene/2d/physics/shape_cast_2d.h @@ -60,7 +60,6 @@ class ShapeCast2D : public Node2D { real_t collision_safe_fraction = 1.0; real_t collision_unsafe_fraction = 1.0; - Array _get_collision_result() const; void _shape_changed(); protected: @@ -102,6 +101,7 @@ public: void force_shapecast_update(); bool is_colliding() const; + Array get_collision_result() const; int get_collision_count() const; Object *get_collider(int p_idx) const; RID get_collider_rid(int p_idx) const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 4266060466..42f7a75c0a 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -682,7 +682,7 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_bones", "_get_bones"); ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count"); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c12b95314e..b10f2097da 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -532,6 +532,18 @@ TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, boo } } +bool TileMap::is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_H; +} + +bool TileMap::is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_V; +} + +bool TileMap::is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_TRANSPOSE; +} + Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array); } @@ -926,6 +938,10 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_h, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_v, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_transposed", "layer", "coords", "use_proxies"), &TileMap::is_cell_transposed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); ClassDB::bind_method(D_METHOD("get_layer_for_body_rid", "body"), &TileMap::get_layer_for_body_rid); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 690102f730..142dc1193f 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -167,6 +167,10 @@ public: // Helper method to make accessing the data easier. TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + // Patterns. Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern); diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 437790bb99..7b125a6895 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -1773,6 +1773,10 @@ void TileMapLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapLayer::get_cell_alternative_tile); ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &TileMapLayer::get_cell_tile_data); + ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "coords"), &TileMapLayer::is_cell_flipped_h); + ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "coords"), &TileMapLayer::is_cell_flipped_v); + ClassDB::bind_method(D_METHOD("is_cell_transposed", "coords"), &TileMapLayer::is_cell_transposed); + ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapLayer::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::get_used_cells_by_id, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMapLayer::get_used_rect); @@ -2490,6 +2494,18 @@ Rect2i TileMapLayer::get_used_rect() const { return used_rect_cache; } +bool TileMapLayer::is_cell_flipped_h(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_H; +} + +bool TileMapLayer::is_cell_flipped_v(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_V; +} + +bool TileMapLayer::is_cell_transposed(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_TRANSPOSE; +} + Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) { ERR_FAIL_COND_V(tile_set.is_null(), nullptr); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index c71f13d7be..1a6d182094 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -438,6 +438,10 @@ public: TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const; Rect2i get_used_rect() const; + bool is_cell_flipped_h(const Vector2i &p_coords) const; + bool is_cell_flipped_v(const Vector2i &p_coords) const; + bool is_cell_transposed(const Vector2i &p_coords) const; + // Patterns. Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array); void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6888462876..4d3f494ccf 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -562,7 +562,7 @@ void AudioStreamPlayer3D::seek(float p_seconds) { void AudioStreamPlayer3D::stop() { setplay.set(-1); - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer3D::is_playing() const { @@ -862,7 +862,7 @@ void AudioStreamPlayer3D::_bind_methods() { } AudioStreamPlayer3D::AudioStreamPlayer3D() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), callable_mp(this, &AudioStreamPlayer3D::stop), true)); velocity_tracker.instantiate(); set_disable_scale(true); cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength"); diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 8515aacba7..c70fa3ca2e 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,9 @@ #include "camera_3d.h" #include "core/math/projection.h" +#include "core/math/transform_interpolator.h" #include "scene/main/viewport.h" +#include "servers/rendering/rendering_server_constants.h" void Camera3D::_update_audio_listener_state() { } @@ -88,7 +90,16 @@ void Camera3D::_update_camera() { return; } - RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + if (!is_physics_interpolated_and_enabled()) { + RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + } else { + // Ideally we shouldn't be moving a physics interpolated camera within a frame, + // because it will break smooth interpolation, but it may occur on e.g. level load. + if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } if (is_part_of_edited_scene() || !is_current()) { return; @@ -97,6 +108,64 @@ void Camera3D::_update_camera() { get_viewport()->_camera_3d_transform_changed_notify(); } +void Camera3D::_physics_interpolated_changed() { + _update_process_mode(); +} + +void Camera3D::_physics_interpolation_ensure_data_flipped() { + // The curr -> previous update can either occur + // on the INTERNAL_PHYSICS_PROCESS OR + // on NOTIFICATION_TRANSFORM_CHANGED, + // if NOTIFICATION_TRANSFORM_CHANGED takes place + // earlier than INTERNAL_PHYSICS_PROCESS on a tick. + // This is to ensure that the data keeps flowing, but the new data + // doesn't overwrite before prev has been set. + + // Keep the data flowing. + uint64_t tick = Engine::get_singleton()->get_physics_frames(); + if (_interpolation_data.last_update_physics_tick != tick) { + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + _interpolation_data.last_update_physics_tick = tick; + physics_interpolation_flip_data(); + } +} + +void Camera3D::_physics_interpolation_ensure_transform_calculated(bool p_force) const { + DEV_CHECK_ONCE(!Engine::get_singleton()->is_in_physics_frame()); + + InterpolationData &id = _interpolation_data; + uint64_t frame = Engine::get_singleton()->get_frames_drawn(); + + if (id.last_update_frame != frame || p_force) { + id.last_update_frame = frame; + + TransformInterpolator::interpolate_transform_3d(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction()); + + Transform3D &tr = id.camera_xform_interpolated; + tr = _get_adjusted_camera_transform(id.xform_interpolated); + } +} + +void Camera3D::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) { + _desired_process_internal = p_process_internal; + _desired_physics_process_internal = p_physics_process_internal; + _update_process_mode(); +} + +void Camera3D::_update_process_mode() { + bool process = _desired_process_internal; + bool physics_process = _desired_physics_process_internal; + + if (is_physics_interpolated_and_enabled()) { + if (is_current()) { + process = true; + physics_process = true; + } + } + set_process_internal(process); + set_physics_process_internal(physics_process); +} + void Camera3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { @@ -118,11 +187,58 @@ void Camera3D::_notification(int p_what) { #endif } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(); + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated Camera3D: " + rtos(_interpolation_data.xform_interpolated.origin.x) + "\t( prev " + rtos(_interpolation_data.xform_prev.origin.x) + ", curr " + rtos(_interpolation_data.xform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera3D triggered from outside physics process"); + } +#endif + } _request_camera_update(); if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { velocity_tracker->update_position(get_global_transform().origin); } + // Allow auto-reset when first adding to the tree, as a convenience. + if (_is_physics_interpolation_reset_requested() && is_inside_tree()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + _set_physics_interpolation_reset_requested(false); + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (is_inside_tree()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } + } break; + + case NOTIFICATION_PAUSED: { + if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } } break; case NOTIFICATION_EXIT_WORLD: { @@ -151,23 +267,34 @@ void Camera3D::_notification(int p_what) { if (viewport) { viewport->find_world_3d()->_register_camera(this); } + _update_process_mode(); } break; case NOTIFICATION_LOST_CURRENT: { if (viewport) { viewport->find_world_3d()->_remove_camera(this); } + _update_process_mode(); } break; } } -Transform3D Camera3D::get_camera_transform() const { - Transform3D tr = get_global_transform().orthonormalized(); +Transform3D Camera3D::_get_adjusted_camera_transform(const Transform3D &p_xform) const { + Transform3D tr = p_xform.orthonormalized(); tr.origin += tr.basis.get_column(1) * v_offset; tr.origin += tr.basis.get_column(0) * h_offset; return tr; } +Transform3D Camera3D::get_camera_transform() const { + if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) { + _physics_interpolation_ensure_transform_calculated(); + return _interpolation_data.camera_xform_interpolated; + } + + return _get_adjusted_camera_transform(get_global_transform()); +} + Projection Camera3D::_get_camera_projection(real_t p_near) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; Projection cm; @@ -379,6 +506,11 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Plane p(get_camera_transform().xform_inv(p_pos), 1.0); p = cm.xform4(p); + + // Prevent divide by zero. + // TODO: Investigate, this was causing NaNs. + ERR_FAIL_COND_V(p.d == 0, Point2()); + p.normal /= p.d; Point2 res; diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index dbf2ffc1dd..3e9f940ad6 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -98,7 +98,39 @@ private: RID pyramid_shape; Vector<Vector3> pyramid_shape_points; + /////////////////////////////////////////////////////// + // INTERPOLATION FUNCTIONS + void _physics_interpolation_ensure_transform_calculated(bool p_force = false) const; + void _physics_interpolation_ensure_data_flipped(); + + // These can be set by derived Camera3Ds, if they wish to do processing + // (while still allowing physics interpolation to function). + bool _desired_process_internal = false; + bool _desired_physics_process_internal = false; + + mutable struct InterpolationData { + Transform3D xform_curr; + Transform3D xform_prev; + Transform3D xform_interpolated; + Transform3D camera_xform_interpolated; // After modification according to camera type. + uint32_t last_update_physics_tick = 0; + uint32_t last_update_frame = UINT32_MAX; + } _interpolation_data; + + void _update_process_mode(); + protected: + // Use from derived classes to set process modes instead of setting directly. + // This is because physics interpolation may need to request process modes additionally. + void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal); + + // Opportunity for derived classes to interpolate extra attributes. + virtual void physics_interpolation_flip_data() {} + + virtual void _physics_interpolated_changed() override; + virtual Transform3D _get_adjusted_camera_transform(const Transform3D &p_xform) const; + /////////////////////////////////////////////////////// + void _update_camera(); virtual void _request_camera_update(); void _update_camera_mode(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 3771b385e5..2cef607d29 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -459,14 +459,6 @@ void GPUParticles3D::_notification(int p_what) { // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update. case NOTIFICATION_INTERNAL_PROCESS: { - const Vector3 velocity = (get_global_position() - previous_position) / get_process_delta_time(); - - if (velocity != previous_velocity) { - RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); - previous_velocity = velocity; - } - previous_position = get_global_position(); - if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { @@ -487,8 +479,21 @@ void GPUParticles3D::_notification(int p_what) { } } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Update velocity in physics process, so that velocity calculations remain correct + // if the physics tick rate is lower than the rendered framerate (especially without physics interpolation). + const Vector3 velocity = (get_global_position() - previous_position) / get_physics_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } + previous_position = get_global_position(); + } break; + case NOTIFICATION_ENTER_TREE: { set_process_internal(false); + set_physics_process_internal(false); if (sub_emitter != NodePath()) { _attach_sub_emitter(); } @@ -499,6 +504,7 @@ void GPUParticles3D::_notification(int p_what) { } previous_position = get_global_transform().origin; set_process_internal(true); + set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 038a78609f..3f8b0dfb8e 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -709,7 +709,7 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f const Vector3 *pp = probe_positions.ptr(); bool exists = false; for (int j = 0; j < ppcount; j++) { - if (pp[j].is_equal_approx(real_pos)) { + if (pp[j].distance_to(real_pos) < (p_cell_size * 0.5f)) { exists = true; break; } @@ -1072,6 +1072,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa if (env.is_valid()) { environment_image = RS::get_singleton()->environment_bake_panorama(env->get_rid(), true, Size2i(128, 64)); + environment_transform = Basis::from_euler(env->get_sky_rotation()).inverse(); } } } break; diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index dc776ebea2..bebba9a6c0 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -152,6 +152,9 @@ void NavigationLink3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink3D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional); ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional); @@ -217,16 +220,7 @@ bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const { void NavigationLink3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (enabled) { - NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); - } - current_global_transform = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); - -#ifdef DEBUG_ENABLED - _update_debug_mesh(); -#endif // DEBUG_ENABLED + _link_enter_navigation_map(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -235,30 +229,11 @@ void NavigationLink3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); - if (is_inside_tree()) { - Transform3D new_global_transform = get_global_transform(); - if (current_global_transform != new_global_transform) { - current_global_transform = new_global_transform; - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform); - } -#endif // DEBUG_ENABLED - } - } + _link_update_transform(); } break; case NOTIFICATION_EXIT_TREE: { - NavigationServer3D::get_singleton()->link_set_map(link, RID()); - -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_scenario(debug_instance, RID()); - RS::get_singleton()->instance_set_visible(debug_instance, false); - } -#endif // DEBUG_ENABLED + _link_exit_navigation_map(); } break; } } @@ -320,6 +295,25 @@ void NavigationLink3D::set_enabled(bool p_enabled) { update_gizmos(); } +void NavigationLink3D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer3D::get_singleton()->link_set_map(link, map_override); +} + +RID NavigationLink3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_3d()->get_navigation_map(); + } + return RID(); +} + void NavigationLink3D::set_bidirectional(bool p_bidirectional) { if (bidirectional == p_bidirectional) { return; @@ -467,3 +461,53 @@ PackedStringArray NavigationLink3D::get_configuration_warnings() const { return warnings; } + +void NavigationLink3D::_link_enter_navigation_map() { + if (!is_inside_tree()) { + return; + } + + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->link_set_map(link, map_override); + } else { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + } + + current_global_transform = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); + +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_exit_navigation_map() { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_update_transform() { + if (!is_inside_tree()) { + return; + } + + Transform3D new_global_transform = get_global_transform(); + if (current_global_transform != new_global_transform) { + current_global_transform = new_global_transform; + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED + } +} diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1867082811..e894761f40 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -38,6 +38,7 @@ class NavigationLink3D : public Node3D { bool enabled = true; RID link; + RID map_override; bool bidirectional = true; uint32_t navigation_layers = 1; Vector3 end_position; @@ -72,6 +73,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_bidirectional(bool p_bidirectional); bool is_bidirectional() const { return bidirectional; } @@ -100,6 +104,11 @@ public: real_t get_travel_cost() const { return travel_cost; } PackedStringArray get_configuration_warnings() const override; + +private: + void _link_enter_navigation_map(); + void _link_exit_navigation_map(); + void _link_update_transform(); }; #endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 2e08afb30d..86ce8a881a 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -30,6 +30,7 @@ #include "node_3d.h" +#include "core/math/transform_interpolator.h" #include "scene/3d/visual_instance_3d.h" #include "scene/main/viewport.h" #include "scene/property_utils.h" @@ -176,6 +177,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; _update_visibility_parent(true); + _disable_client_physics_interpolation(); } break; case NOTIFICATION_ENTER_WORLD: { @@ -226,6 +228,12 @@ void Node3D::_notification(int p_what) { } #endif } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (data.client_physics_interpolation_data) { + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + } + } break; } } @@ -341,6 +349,119 @@ Transform3D Node3D::get_transform() const { return data.local_transform; } +// Return false to timeout and remove from the client interpolation list. +bool Node3D::update_client_physics_interpolation_data() { + if (!is_inside_tree() || !_is_physics_interpolated_client_side()) { + return false; + } + + ERR_FAIL_NULL_V(data.client_physics_interpolation_data, false); + ClientPhysicsInterpolationData &pid = *data.client_physics_interpolation_data; + + uint64_t tick = Engine::get_singleton()->get_physics_frames(); + + // Has this update been done already this tick? + // (For instance, get_global_transform_interpolated() could be called multiple times.) + if (pid.current_physics_tick != tick) { + // Timeout? + if (tick >= pid.timeout_physics_tick) { + return false; + } + + if (pid.current_physics_tick == (tick - 1)) { + // Normal interpolation situation, there is a continuous flow of data + // from one tick to the next... + pid.global_xform_prev = pid.global_xform_curr; + } else { + // There has been a gap, we cannot sensibly offer interpolation over + // a multitick gap, so we will teleport. + pid.global_xform_prev = get_global_transform(); + } + pid.current_physics_tick = tick; + } + + pid.global_xform_curr = get_global_transform(); + return true; +} + +void Node3D::_disable_client_physics_interpolation() { + // Disable any current client side interpolation. + // (This can always restart as normal if you later re-attach the node to the SceneTree.) + if (data.client_physics_interpolation_data) { + memdelete(data.client_physics_interpolation_data); + data.client_physics_interpolation_data = nullptr; + + SceneTree *tree = get_tree(); + if (tree && _client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_remove_node_3d(&_client_physics_interpolation_node_3d_list); + } + } + _set_physics_interpolated_client_side(false); +} + +Transform3D Node3D::_get_global_transform_interpolated(real_t p_interpolation_fraction) { + ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); + + // Set in motion the mechanisms for client side interpolation if not already active. + if (!_is_physics_interpolated_client_side()) { + _set_physics_interpolated_client_side(true); + + ERR_FAIL_COND_V(data.client_physics_interpolation_data != nullptr, Transform3D()); + data.client_physics_interpolation_data = memnew(ClientPhysicsInterpolationData); + data.client_physics_interpolation_data->global_xform_curr = get_global_transform(); + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + data.client_physics_interpolation_data->current_physics_tick = Engine::get_singleton()->get_physics_frames(); + } + + // Storing the last tick we requested client interpolation allows us to timeout + // and remove client interpolated nodes from the list to save processing. + // We use some arbitrary timeout here, but this could potentially be user defined. + + // Note: This timeout has to be larger than the number of ticks in a frame, otherwise the interpolated + // data will stop flowing before the next frame is drawn. This should only be relevant at high tick rates. + // We could alternatively do this by frames rather than ticks and avoid this problem, but then the behavior + // would be machine dependent. + data.client_physics_interpolation_data->timeout_physics_tick = Engine::get_singleton()->get_physics_frames() + 256; + + // Make sure data is up to date. + update_client_physics_interpolation_data(); + + // Interpolate the current data. + const Transform3D &xform_curr = data.client_physics_interpolation_data->global_xform_curr; + const Transform3D &xform_prev = data.client_physics_interpolation_data->global_xform_prev; + + Transform3D res; + TransformInterpolator::interpolate_transform_3d(xform_prev, xform_curr, res, p_interpolation_fraction); + + SceneTree *tree = get_tree(); + + // This should not happen, as is_inside_tree() is checked earlier. + ERR_FAIL_NULL_V(tree, res); + if (!_client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_add_node_3d(&_client_physics_interpolation_node_3d_list); + } + + return res; +} + +Transform3D Node3D::get_global_transform_interpolated() { + // Pass through if physics interpolation is switched off. + // This is a convenience, as it allows you to easy turn off interpolation + // without changing any code. + if (!is_physics_interpolated_and_enabled()) { + return get_global_transform(); + } + + // If we are in the physics frame, the interpolated global transform is meaningless. + // However, there is an exception, we may want to use this as a means of starting off the client + // interpolation pump if not already started (when _is_physics_interpolated_client_side() is false). + if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) { + return get_global_transform(); + } + + return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction()); +} + Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); @@ -1140,6 +1261,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); + ClassDB::bind_method(D_METHOD("get_global_transform_interpolated"), &Node3D::get_global_transform_interpolated); ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node3D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node3D::get_global_position); ClassDB::bind_method(D_METHOD("set_global_basis", "basis"), &Node3D::set_global_basis); @@ -1236,4 +1358,27 @@ void Node3D::_bind_methods() { } Node3D::Node3D() : - xform_change(this) {} + xform_change(this), _client_physics_interpolation_node_3d_list(this) { + // Default member initializer for bitfield is a C++20 extension, so: + + data.top_level = false; + data.inside_world = false; + + data.ignore_notification = false; + data.notify_local_transform = false; + data.notify_transform = false; + + data.visible = true; + data.disable_scale = false; + data.vi_visible = true; + +#ifdef TOOLS_ENABLED + data.gizmos_disabled = false; + data.gizmos_dirty = false; + data.transform_gizmo_visible = true; +#endif +} + +Node3D::~Node3D() { + _disable_client_physics_interpolation(); +} diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index c1667221df..217ee28cf1 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -85,7 +85,15 @@ private: DIRTY_GLOBAL_TRANSFORM = 4 }; + struct ClientPhysicsInterpolationData { + Transform3D global_xform_curr; + Transform3D global_xform_prev; + uint64_t current_physics_tick = 0; + uint64_t timeout_physics_tick = 0; + }; + mutable SelfList<Node> xform_change; + SelfList<Node3D> _client_physics_interpolation_node_3d_list; // This Data struct is to avoid namespace pollution in derived classes. @@ -101,8 +109,19 @@ private: Viewport *viewport = nullptr; - bool top_level = false; - bool inside_world = false; + bool top_level : 1; + bool inside_world : 1; + + // This is cached, and only currently kept up to date in visual instances. + // This is set if a visual instance is (a) in the tree AND (b) visible via is_visible_in_tree() call. + bool vi_visible : 1; + + bool ignore_notification : 1; + bool notify_local_transform : 1; + bool notify_transform : 1; + + bool visible : 1; + bool disable_scale : 1; RID visibility_parent; @@ -110,18 +129,13 @@ private: List<Node3D *> children; List<Node3D *>::Element *C = nullptr; - bool ignore_notification = false; - bool notify_local_transform = false; - bool notify_transform = false; - - bool visible = true; - bool disable_scale = false; + ClientPhysicsInterpolationData *client_physics_interpolation_data = nullptr; #ifdef TOOLS_ENABLED Vector<Ref<Node3DGizmo>> gizmos; - bool gizmos_disabled = false; - bool gizmos_dirty = false; - bool transform_gizmo_visible = true; + bool gizmos_disabled : 1; + bool gizmos_dirty : 1; + bool transform_gizmo_visible : 1; #endif } data; @@ -150,6 +164,11 @@ protected: _FORCE_INLINE_ void _update_local_transform() const; _FORCE_INLINE_ void _update_rotation_and_scale() const; + void _set_vi_visible(bool p_visible) { data.vi_visible = p_visible; } + bool _is_vi_visible() const { return data.vi_visible; } + Transform3D _get_global_transform_interpolated(real_t p_interpolation_fraction); + void _disable_client_physics_interpolation(); + void _notification(int p_what); static void _bind_methods(); @@ -208,6 +227,9 @@ public: Quaternion get_quaternion() const; Transform3D get_global_transform() const; + Transform3D get_global_transform_interpolated(); + bool update_client_physics_interpolation_data(); + #ifdef TOOLS_ENABLED virtual Transform3D get_global_gizmo_transform() const; virtual Transform3D get_local_gizmo_transform() const; @@ -279,6 +301,7 @@ public: NodePath get_visibility_parent() const; Node3D(); + ~Node3D(); }; VARIANT_ENUM_CAST(Node3D::RotationEditMode) diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 150771545b..6982df12f6 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -129,9 +129,6 @@ void Occluder3D::_notification(int p_what) { } } -void Occluder3D::_bind_methods() { -} - Occluder3D::Occluder3D() { occluder = RS::get_singleton()->occluder_create(); } diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 91445710b3..62e9478527 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -49,7 +49,6 @@ protected: void _update(); virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0; - static void _bind_methods(); void _notification(int p_what); public: diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp index ada238c7f2..8ad651fdf5 100644 --- a/scene/3d/physics/shape_cast_3d.cpp +++ b/scene/3d/physics/shape_cast_3d.cpp @@ -157,7 +157,7 @@ void ShapeCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled); - ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result); + ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast3D::get_collision_result); ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color); ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color); @@ -169,7 +169,7 @@ void ShapeCast3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result"); ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); @@ -475,7 +475,7 @@ bool ShapeCast3D::is_collide_with_bodies_enabled() const { return collide_with_bodies; } -Array ShapeCast3D::_get_collision_result() const { +Array ShapeCast3D::get_collision_result() const { Array ret; for (int i = 0; i < result.size(); ++i) { diff --git a/scene/3d/physics/shape_cast_3d.h b/scene/3d/physics/shape_cast_3d.h index 19b73e3f72..9fc5e71670 100644 --- a/scene/3d/physics/shape_cast_3d.h +++ b/scene/3d/physics/shape_cast_3d.h @@ -73,8 +73,6 @@ class ShapeCast3D : public Node3D { real_t collision_safe_fraction = 1.0; real_t collision_unsafe_fraction = 1.0; - Array _get_collision_result() const; - RID debug_instance; Ref<ArrayMesh> debug_mesh; @@ -123,6 +121,7 @@ public: Ref<StandardMaterial3D> get_debug_material(); + Array get_collision_result() const; int get_collision_count() const; Object *get_collider(int p_idx) const; RID get_collider_rid(int p_idx) const; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 0d6316ee35..2476e7d5cd 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -503,7 +503,11 @@ Transform3D SkeletonIK3D::_get_target_transform() { Node3D *target_node_override = cast_to<Node3D>(target_node_override_ref.get_validated_object()); if (target_node_override && target_node_override->is_inside_tree()) { - return target_node_override->get_global_transform(); + // Make sure to use the interpolated transform as target. + // When physics interpolation is off this will pass through to get_global_transform(). + // When using interpolation, ensure that the target matches the interpolated visual position + // of the target when updating the IK each frame. + return target_node_override->get_global_transform_interpolated(); } else { return target; } diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp index 9851214194..d5c603112e 100644 --- a/scene/3d/skeleton_modifier_3d.cpp +++ b/scene/3d/skeleton_modifier_3d.cpp @@ -37,7 +37,7 @@ void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const { PackedStringArray SkeletonModifier3D::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); if (skeleton_id.is_null()) { - warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton.")); + warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D.")); } return warnings; } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index f02cd9b700..4fe5dd2385 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -218,7 +218,13 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String if ("spatial_attachment_path" == p_what) { PinnedPoint *w = pinned_points.ptrw(); - callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + + if (is_inside_tree()) { + callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + } else { + pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } } else if ("offset" == p_what) { PinnedPoint *w = pinned_points.ptrw(); w[p_item].offset = p_value; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index f14ae3a285..79a01450dd 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -30,6 +30,8 @@ #include "visual_instance_3d.h" +#include "core/config/project_settings.h" + AABB VisualInstance3D::get_aabb() const { AABB ret; GDVIRTUAL_CALL(_get_aabb, ret); @@ -41,7 +43,38 @@ void VisualInstance3D::_update_visibility() { return; } - RS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree()); + bool already_visible = _is_vi_visible(); + bool visible = is_visible_in_tree(); + _set_vi_visible(visible); + + // If making visible, make sure the rendering server is up to date with the transform. + if (visible && !already_visible) { + if (!_is_using_identity_transform()) { + Transform3D gt = get_global_transform(); + RS::get_singleton()->instance_set_transform(instance, gt); + } + } + + RS::get_singleton()->instance_set_visible(instance, visible); +} + +void VisualInstance3D::_physics_interpolated_changed() { + RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated()); +} + +void VisualInstance3D::set_instance_use_identity_transform(bool p_enable) { + // Prevent sending instance transforms when using global coordinates. + _set_use_identity_transform(p_enable); + + if (is_inside_tree()) { + if (p_enable) { + // Want to make sure instance is using identity transform. + RS::get_singleton()->instance_set_transform(instance, Transform3D()); + } else { + // Want to make sure instance is up to date. + RS::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + } } void VisualInstance3D::_notification(int p_what) { @@ -53,13 +86,52 @@ void VisualInstance3D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - Transform3D gt = get_global_transform(); - RenderingServer::get_singleton()->instance_set_transform(instance, gt); + if (_is_vi_visible() || is_physics_interpolated_and_enabled()) { + if (!_is_using_identity_transform()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + + // For instance when first adding to the tree, when the previous transform is + // unset, to prevent streaking from the origin. + if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) { + if (_is_vi_visible()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + } + _set_physics_interpolation_reset_requested(false); + } + } + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) { + // We must ensure the RenderingServer transform is up to date before resetting. + // This is because NOTIFICATION_TRANSFORM_CHANGED is deferred, + // and cannot be relied to be called in order before NOTIFICATION_RESET_PHYSICS_INTERPOLATION. + if (!_is_using_identity_transform()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + + RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance); + } +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + else if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + + String node_name = is_inside_tree() ? String(get_path()) : String(get_name()); + if (!_is_vi_visible()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with unhidden nodes: \"" + node_name + "\"."); + } + if (!is_physics_interpolated()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with interpolated nodes: \"" + node_name + "\"."); + } + } +#endif + } break; case NOTIFICATION_EXIT_WORLD: { RenderingServer::get_singleton()->instance_set_scenario(instance, RID()); RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID()); + _set_vi_visible(false); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 59ede26ac1..9b02c928b7 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -45,6 +45,9 @@ class VisualInstance3D : public Node3D { protected: void _update_visibility(); + virtual void _physics_interpolated_changed() override; + void set_instance_use_identity_transform(bool p_enable); + void _notification(int p_what); static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index e4baae1afb..cdc85d2b2d 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -754,9 +754,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::Pl return nti; } -void AnimationNodeAdd2::_bind_methods() { -} - AnimationNodeAdd2::AnimationNodeAdd2() { add_input("in"); add_input("add"); @@ -800,9 +797,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::Pl return nti; } -void AnimationNodeAdd3::_bind_methods() { -} - AnimationNodeAdd3::AnimationNodeAdd3() { add_input("-add"); add_input("in"); @@ -845,9 +839,6 @@ bool AnimationNodeBlend2::has_filter() const { return true; } -void AnimationNodeBlend2::_bind_methods() { -} - AnimationNodeBlend2::AnimationNodeBlend2() { add_input("in"); add_input("blend"); @@ -887,9 +878,6 @@ AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer:: return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough. } -void AnimationNodeBlend3::_bind_methods() { -} - AnimationNodeBlend3::AnimationNodeBlend3() { add_input("-blend"); add_input("in"); @@ -932,9 +920,6 @@ AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::Pl return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); } -void AnimationNodeSub2::_bind_methods() { -} - AnimationNodeSub2::AnimationNodeSub2() { add_input("in"); add_input("sub"); @@ -972,9 +957,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); } -void AnimationNodeTimeScale::_bind_methods() { -} - AnimationNodeTimeScale::AnimationNodeTimeScale() { add_input("in"); } @@ -1014,9 +996,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); } -void AnimationNodeTimeSeek::_bind_methods() { -} - AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index c7ef7ed624..2add35d009 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -200,9 +200,6 @@ class AnimationNodeAdd2 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -220,9 +217,6 @@ class AnimationNodeAdd3 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -240,9 +234,6 @@ class AnimationNodeBlend2 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -259,9 +250,6 @@ class AnimationNodeBlend3 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -277,9 +265,6 @@ class AnimationNodeSub2 : public AnimationNodeSync { StringName sub_amount = PNAME("sub_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -297,9 +282,6 @@ class AnimationNodeTimeScale : public AnimationNode { StringName scale = PNAME("scale"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -316,9 +298,6 @@ class AnimationNodeTimeSeek : public AnimationNode { StringName seek_pos_request = PNAME("seek_request"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index e90c1aa245..183c4af950 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -112,7 +112,7 @@ void AudioStreamPlayer::seek(float p_seconds) { } void AudioStreamPlayer::stop() { - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer::is_playing() const { @@ -283,7 +283,7 @@ void AudioStreamPlayer::_bind_methods() { } AudioStreamPlayer::AudioStreamPlayer() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), false)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), callable_mp(this, &AudioStreamPlayer::stop), false)); } AudioStreamPlayer::~AudioStreamPlayer() { diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp index 36c14e03d5..206408e3a7 100644 --- a/scene/audio/audio_stream_player_internal.cpp +++ b/scene/audio/audio_stream_player_internal.cpp @@ -132,7 +132,7 @@ Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() { } ERR_FAIL_COND_V_MSG(!node->is_inside_tree(), stream_playback, "Playback can only happen when a node is inside the scene tree"); if (stream->is_monophonic() && is_playing()) { - stop(); + stop_callable.call(); } stream_playback = stream->instantiate_playback(); ERR_FAIL_COND_V_MSG(stream_playback.is_null(), stream_playback, "Failed to instantiate playback."); @@ -242,7 +242,7 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) { if (stream.is_valid()) { stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters)); } - stop(); + stop_callable.call(); stream = p_stream; _update_stream_parameters(); if (stream.is_valid()) { @@ -253,12 +253,12 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) { void AudioStreamPlayerInternal::seek(float p_seconds) { if (is_playing()) { - stop(); + stop_callable.call(); play_callable.call(p_seconds); } } -void AudioStreamPlayerInternal::stop() { +void AudioStreamPlayerInternal::stop_basic() { for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { AudioServer::get_singleton()->stop_playback_stream(playback); } @@ -289,7 +289,7 @@ void AudioStreamPlayerInternal::set_playing(bool p_enable) { if (p_enable) { play_callable.call(0.0); } else { - stop(); + stop_callable.call(); } } @@ -339,9 +339,10 @@ StringName AudioStreamPlayerInternal::get_bus() const { return SceneStringName(Master); } -AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical) { +AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical) { node = p_node; play_callable = p_play_callable; + stop_callable = p_stop_callable; physical = p_physical; bus = SceneStringName(Master); diff --git a/scene/audio/audio_stream_player_internal.h b/scene/audio/audio_stream_player_internal.h index ec4489067e..7d8faeba06 100644 --- a/scene/audio/audio_stream_player_internal.h +++ b/scene/audio/audio_stream_player_internal.h @@ -53,6 +53,7 @@ private: Node *node = nullptr; Callable play_callable; + Callable stop_callable; bool physical = false; AudioServer::PlaybackType playback_type = AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT; @@ -94,7 +95,7 @@ public: Ref<AudioStreamPlayback> play_basic(); void seek(float p_seconds); - void stop(); + void stop_basic(); bool is_playing() const; float get_playback_position(); @@ -110,7 +111,7 @@ public: void set_playback_type(AudioServer::PlaybackType p_playback_type); AudioServer::PlaybackType get_playback_type() const; - AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical); + AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical); }; #endif // AUDIO_STREAM_PLAYER_INTERNAL_H diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1da3668ebe..e9fe78e162 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1448,6 +1448,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V bool line_clicked = false; float text_rect_begin = 0.0; int char_pos = -1; + bool char_clicked = false; Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); @@ -1578,6 +1579,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { + if (!p_meta) { + char_pos = rtl ? TS->shaped_text_get_range(rid).y : TS->shaped_text_get_range(rid).x; + } if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) { if (p_meta) { int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x); @@ -1592,6 +1596,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V obj_rect.position.y += baseline_y; if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } break; } @@ -1602,18 +1607,21 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size); if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } } else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) { // Hex code box. Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index); if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } } } } else { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); char_pos = TS->shaped_text_closest_character_pos(rid, char_pos); + char_clicked = true; } } line_clicked = true; @@ -1621,7 +1629,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } // If table hit was detected, and line hit is in the table bounds use table hit. - if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) { + if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || !char_clicked)) { if (r_click_frame != nullptr) { *r_click_frame = table_click_frame; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0396f3ab4a..5c46abc732 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -138,6 +138,12 @@ void Node::_notification(int p_notification) { get_tree()->nodes_in_tree_count++; orphan_node_count--; + + // Allow physics interpolated nodes to automatically reset when added to the tree + // (this is to save the user from doing this manually each time). + if (get_tree()->is_physics_interpolation_enabled()) { + _set_physics_interpolation_reset_requested(true); + } } break; case NOTIFICATION_EXIT_TREE: { @@ -437,6 +443,18 @@ void Node::_propagate_physics_interpolated(bool p_interpolated) { data.blocked--; } +void Node::_propagate_physics_interpolation_reset_requested(bool p_requested) { + if (is_physics_interpolated()) { + data.physics_interpolation_reset_requested = p_requested; + } + + data.blocked++; + for (KeyValue<StringName, Node *> &K : data.children) { + K.value->_propagate_physics_interpolation_reset_requested(p_requested); + } + data.blocked--; +} + void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index)."); ERR_FAIL_NULL(p_child); @@ -890,15 +908,23 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) { } // If swapping from interpolated to non-interpolated, use this as an extra means to cause a reset. - if (is_physics_interpolated() && !interpolate) { - reset_physics_interpolation(); + if (is_physics_interpolated() && !interpolate && is_inside_tree()) { + propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); } _propagate_physics_interpolated(interpolate); } void Node::reset_physics_interpolation() { - propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + if (is_inside_tree()) { + propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + + // If `reset_physics_interpolation()` is called explicitly by the user + // (e.g. from scripts) then we prevent deferred auto-resets taking place. + // The user is trusted to call reset in the right order, and auto-reset + // will interfere with their control of prev / curr, so should be turned off. + _propagate_physics_interpolation_reset_requested(false); + } } bool Node::_is_enabled() const { @@ -3825,6 +3851,9 @@ Node::Node() { data.unhandled_key_input = false; data.physics_interpolated = true; + data.physics_interpolation_reset_requested = false; + data.physics_interpolated_client_side = false; + data.use_identity_transform = false; data.parent_owned = false; data.in_constructor = true; diff --git a/scene/main/node.h b/scene/main/node.h index ee195ddef9..2f6372dad5 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -225,6 +225,21 @@ private: // is switched on. bool physics_interpolated : 1; + // We can auto-reset physics interpolation when e.g. adding a node for the first time. + bool physics_interpolation_reset_requested : 1; + + // Most nodes need not be interpolated in the scene tree, physics interpolation + // is normally only needed in the RenderingServer. However if we need to read the + // interpolated transform of a node in the SceneTree, it is necessary to duplicate + // the interpolation logic client side, in order to prevent stalling the RenderingServer + // by reading back. + bool physics_interpolated_client_side : 1; + + // For certain nodes (e.g. CPU particles in global mode) + // it can be useful to not send the instance transform to the + // RenderingServer, and specify the mesh in world space. + bool use_identity_transform : 1; + bool parent_owned : 1; bool in_constructor : 1; bool use_placeholder : 1; @@ -263,6 +278,7 @@ private: void _propagate_exit_tree(); void _propagate_after_exit_tree(); void _propagate_physics_interpolated(bool p_interpolated); + void _propagate_physics_interpolation_reset_requested(bool p_requested); void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); void _propagate_groups_dirty(); Array _get_node_and_resource(const NodePath &p_path); @@ -334,6 +350,15 @@ protected: void _set_owner_nocheck(Node *p_owner); void _set_name_nocheck(const StringName &p_name); + void _set_physics_interpolated_client_side(bool p_enable) { data.physics_interpolated_client_side = p_enable; } + bool _is_physics_interpolated_client_side() const { return data.physics_interpolated_client_side; } + + void _set_physics_interpolation_reset_requested(bool p_enable) { data.physics_interpolation_reset_requested = p_enable; } + bool _is_physics_interpolation_reset_requested() const { return data.physics_interpolation_reset_requested; } + + void _set_use_identity_transform(bool p_enable) { data.use_identity_transform = p_enable; } + bool _is_using_identity_transform() const { return data.use_identity_transform; } + //call from SceneTree void _call_input(const Ref<InputEvent> &p_event); void _call_shortcut_input(const Ref<InputEvent> &p_event); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index ced6d9aaa6..f0c9e8a866 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -59,6 +59,7 @@ #include "servers/navigation_server_3d.h" #include "servers/physics_server_2d.h" #ifndef _3D_DISABLED +#include "scene/3d/node_3d.h" #include "scene/resources/3d/world_3d.h" #include "servers/physics_server_3d.h" #endif // _3D_DISABLED @@ -118,6 +119,29 @@ void SceneTreeTimer::release_connections() { SceneTreeTimer::SceneTreeTimer() {} +#ifndef _3D_DISABLED +// This should be called once per physics tick, to make sure the transform previous and current +// is kept up to date on the few Node3Ds that are using client side physics interpolation. +void SceneTree::ClientPhysicsInterpolation::physics_process() { + for (SelfList<Node3D> *E = _node_3d_list.first(); E;) { + Node3D *node_3d = E->self(); + + SelfList<Node3D> *current = E; + + // Get the next element here BEFORE we potentially delete one. + E = E->next(); + + // This will return false if the Node3D has timed out .. + // i.e. if get_global_transform_interpolated() has not been called + // for a few seconds, we can delete from the list to keep processing + // to a minimum. + if (!node_3d->update_client_physics_interpolation_data()) { + _node_3d_list.remove(current); + } + } +} +#endif + void SceneTree::tree_changed() { emit_signal(tree_changed_name); } @@ -466,9 +490,31 @@ bool SceneTree::is_physics_interpolation_enabled() const { return _physics_interpolation_enabled; } +#ifndef _3D_DISABLED +void SceneTree::client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem) { + // This ensures that _update_physics_interpolation_data() will be called at least once every + // physics tick, to ensure the previous and current transforms are kept up to date. + _client_physics_interpolation._node_3d_list.add(p_elem); +} + +void SceneTree::client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem) { + _client_physics_interpolation._node_3d_list.remove(p_elem); +} +#endif + void SceneTree::iteration_prepare() { if (_physics_interpolation_enabled) { + // Make sure any pending transforms from the last tick / frame + // are flushed before pumping the interpolation prev and currents. + flush_transform_notifications(); RenderingServer::get_singleton()->tick(); + +#ifndef _3D_DISABLED + // Any objects performing client physics interpolation + // should be given an opportunity to keep their previous transforms + // up to date before each new physics tick. + _client_physics_interpolation.physics_process(); +#endif } } @@ -503,6 +549,14 @@ bool SceneTree::physics_process(double p_time) { return _quit; } +void SceneTree::iteration_end() { + // When physics interpolation is active, we want all pending transforms + // to be flushed to the RenderingServer before finishing a physics tick. + if (_physics_interpolation_enabled) { + flush_transform_notifications(); + } +} + bool SceneTree::process(double p_time) { if (MainLoop::process(p_time)) { _quit = true; @@ -570,6 +624,10 @@ bool SceneTree::process(double p_time) { #endif // _3D_DISABLED #endif // TOOLS_ENABLED + if (_physics_interpolation_enabled) { + RenderingServer::get_singleton()->pre_draw(true); + } + return _quit; } @@ -1761,6 +1819,13 @@ SceneTree::SceneTree() { set_physics_interpolation_enabled(GLOBAL_DEF("physics/common/physics_interpolation", false)); + // Always disable jitter fix if physics interpolation is enabled - + // Jitter fix will interfere with interpolation, and is not necessary + // when interpolation is active. + if (is_physics_interpolation_enabled()) { + Engine::get_singleton()->set_physics_jitter_fix(0); + } + // Initialize network state. set_multiplayer(MultiplayerAPI::create_default_interface()); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 6f0a61ec51..7e44541105 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -41,6 +41,9 @@ class PackedScene; class Node; +#ifndef _3D_DISABLED +class Node3D; +#endif class Window; class Material; class Mesh; @@ -120,6 +123,13 @@ private: bool changed = false; }; +#ifndef _3D_DISABLED + struct ClientPhysicsInterpolation { + SelfList<Node3D>::List _node_3d_list; + void physics_process(); + } _client_physics_interpolation; +#endif + Window *root = nullptr; double physics_process_time = 0.0; @@ -315,6 +325,7 @@ public: virtual void iteration_prepare() override; virtual bool physics_process(double p_time) override; + virtual void iteration_end() override; virtual bool process(double p_time) override; virtual void finalize() override; @@ -423,6 +434,11 @@ public: void set_physics_interpolation_enabled(bool p_enabled); bool is_physics_interpolation_enabled() const; +#ifndef _3D_DISABLED + void client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem); + void client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem); +#endif + SceneTree(); ~SceneTree(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1302e3c53e..c85fda2371 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1511,6 +1511,7 @@ void Viewport::_gui_show_tooltip() { r.size *= win_scale; vr = window->get_usable_parent_rect(); } + r.size = r.size.ceil(); r.size = r.size.min(panel->get_max_size()); if (r.size.x + r.position.x > vr.size.x + vr.position.x) { @@ -5024,6 +5025,13 @@ Viewport::Viewport() { #endif // _3D_DISABLED set_sdf_oversize(sdf_oversize); // Set to server. + + // Physics interpolation mode for viewports is a special case. + // Typically viewports will be housed within Controls, + // and Controls default to PHYSICS_INTERPOLATION_MODE_OFF. + // Viewports can thus inherit physics interpolation OFF, which is unexpected. + // Setting to ON allows each viewport to have a fresh interpolation state. + set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_ON); } Viewport::~Viewport() { diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index d124577d25..6a799e90ce 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -5275,11 +5275,26 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int 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); + TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile); + if (tile_data->get_transpose()) { + size = Size2(size.y, size.x); + } + Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size); return rect.has_point(p_position); } +bool TileSetAtlasSource::is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const { + Size2 size = get_tile_texture_region(p_atlas_coords).size; + TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile); + if (tile_data->get_transpose()) { + size = Size2(size.y, size.x); + } + Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size); + + return p_rect.intersection(rect) == p_rect; +} + int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) { return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE); } diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h index e083fa45b9..51df972c8d 100644 --- a/scene/resources/2d/tile_set.h +++ b/scene/resources/2d/tile_set.h @@ -763,6 +763,7 @@ public: Vector2i get_atlas_grid_size() const; Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const; + bool is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const; static int alternative_no_transform(int p_alternative_id); diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp index f912d2650d..91531699b4 100644 --- a/scene/resources/3d/importer_mesh.cpp +++ b/scene/resources/3d/importer_mesh.cpp @@ -269,7 +269,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma } \ write_array[vert_idx] = transformed_vert; -void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array) { +void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array, bool p_raycast_normals) { if (!SurfaceTool::simplify_scale_func) { return; } @@ -432,6 +432,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli unsigned int index_target = 12; // Start with the smallest target, 4 triangles unsigned int last_index_count = 0; + // Only used for normal raycasting int split_vertex_count = vertex_count; LocalVector<Vector3> split_vertex_normals; LocalVector<int> split_vertex_indices; @@ -441,7 +442,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli RandomPCG pcg; pcg.seed(123456789); // Keep seed constant across imports - Ref<StaticRaycaster> raycaster = StaticRaycaster::create(); + Ref<StaticRaycaster> raycaster = p_raycast_normals ? StaticRaycaster::create() : Ref<StaticRaycaster>(); if (raycaster.is_valid()) { raycaster->add_mesh(vertices, indices, 0); raycaster->commit(); @@ -488,19 +489,22 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } new_indices.resize(new_index_count); - - LocalVector<LocalVector<int>> vertex_corners; - vertex_corners.resize(vertex_count); { int *ptrw = new_indices.ptrw(); for (unsigned int j = 0; j < new_index_count; j++) { - const int &remapped = vertex_inverse_remap[ptrw[j]]; - vertex_corners[remapped].push_back(j); - ptrw[j] = remapped; + ptrw[j] = vertex_inverse_remap[ptrw[j]]; } } if (raycaster.is_valid()) { + LocalVector<LocalVector<int>> vertex_corners; + vertex_corners.resize(vertex_count); + + int *ptrw = new_indices.ptrw(); + for (unsigned int j = 0; j < new_index_count; j++) { + vertex_corners[ptrw[j]].push_back(j); + } + float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); const float ray_bias = 0.05; float ray_length = ray_bias + mesh_error * scale * 3.0f; @@ -671,7 +675,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } - surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + if (raycaster.is_valid()) { + surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + } + surfaces.write[i].lods.sort_custom<Surface::LODComparator>(); for (int j = 0; j < surfaces.write[i].lods.size(); j++) { @@ -682,6 +689,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } +void ImporterMesh::_generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array) { + generate_lods(p_normal_merge_angle, p_normal_split_angle, p_skin_pose_transform_array); +} + bool ImporterMesh::has_mesh() const { return mesh.is_valid(); } @@ -1367,7 +1378,7 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); - ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::generate_lods); + ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::_generate_lods_bind); ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h index ff8683449b..777f936030 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -86,6 +86,8 @@ protected: void _set_data(const Dictionary &p_data); Dictionary _get_data() const; + void _generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array); + static void _bind_methods(); public: @@ -112,7 +114,7 @@ public: void set_surface_material(int p_surface, const Ref<Material> &p_material); - void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array); + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array, bool p_raycast_normals = false); void create_shadow_mesh(); Ref<ImporterMesh> get_shadow_mesh() const; diff --git a/scene/resources/3d/primitive_meshes.cpp b/scene/resources/3d/primitive_meshes.cpp index ee772f960a..128822d2cb 100644 --- a/scene/resources/3d/primitive_meshes.cpp +++ b/scene/resources/3d/primitive_meshes.cpp @@ -324,22 +324,43 @@ Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const { } float PrimitiveMesh::get_lightmap_texel_size() const { - float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"); + return texel_size; +} - if (texel_size <= 0.0) { - texel_size = 0.2; +void PrimitiveMesh::_on_settings_changed() { + float new_texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size")); + if (new_texel_size <= 0.0) { + new_texel_size = 0.2; + } + if (texel_size == new_texel_size) { + return; } - return texel_size; + texel_size = new_texel_size; + _update_lightmap_size(); + request_update(); } PrimitiveMesh::PrimitiveMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); mesh = RenderingServer::get_singleton()->mesh_create(); + + ERR_FAIL_NULL(ProjectSettings::get_singleton()); + texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size")); + if (texel_size <= 0.0) { + texel_size = 0.2; + } + ProjectSettings *project_settings = ProjectSettings::get_singleton(); + project_settings->connect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed)); } PrimitiveMesh::~PrimitiveMesh() { ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); + + ERR_FAIL_NULL(ProjectSettings::get_singleton()); + ProjectSettings *project_settings = ProjectSettings::get_singleton(); + project_settings->disconnect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed)); } /** @@ -350,7 +371,6 @@ void CapsuleMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend @@ -365,7 +385,6 @@ void CapsuleMesh::_update_lightmap_size() { void CapsuleMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding); @@ -613,7 +632,6 @@ void BoxMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float width = (size.x + size.z) / texel_size; @@ -632,7 +650,6 @@ void BoxMesh::_create_mesh_array(Array &p_arr) const { // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale // accordingly. bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding); @@ -937,7 +954,6 @@ void CylinderMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float top_circumference = top_radius * Math_PI * 2.0; @@ -957,7 +973,6 @@ void CylinderMesh::_update_lightmap_size() { void CylinderMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding); @@ -1244,7 +1259,6 @@ void PlaneMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding); @@ -1416,7 +1430,6 @@ void PrismMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); // left_to_right does not effect the surface area of the prism so we ignore that. @@ -1440,7 +1453,6 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { // Only used if we calculate UV2 bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; float horizontal_total = size.x + size.z + 2.0 * _uv2_padding; @@ -1762,7 +1774,6 @@ void SphereMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float _width = radius * Math_TAU; @@ -1776,7 +1787,6 @@ void SphereMesh::_update_lightmap_size() { void SphereMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding); @@ -1950,7 +1960,6 @@ void TorusMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float min_radius = inner_radius; @@ -2000,7 +2009,6 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { // Only used if we calculate UV2 bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; float horizontal_total = max_radius * Math_TAU + _uv2_padding; diff --git a/scene/resources/3d/primitive_meshes.h b/scene/resources/3d/primitive_meshes.h index 4d2d0760b3..fc2489923a 100644 --- a/scene/resources/3d/primitive_meshes.h +++ b/scene/resources/3d/primitive_meshes.h @@ -67,6 +67,9 @@ protected: // assume primitive triangles as the type, correct for all but one and it will change this :) Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES; + // Copy of our texel_size project setting. + float texel_size = 0.2; + static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const {} @@ -76,6 +79,8 @@ protected: float get_lightmap_texel_size() const; virtual void _update_lightmap_size(){}; + void _on_settings_changed(); + public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 104187775d..6b65ea4cfb 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -1482,8 +1482,8 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi switch (block_type) { case 1: /* info */ { ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, "Invalid BMFont info block size."); - base_size = f->get_16(); - if (base_size <= 0) { + base_size = ABS(static_cast<int16_t>(f->get_16())); + if (base_size == 0) { base_size = 16; } uint8_t flags = f->get_8(); @@ -1776,7 +1776,10 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi if (type == "info") { if (keys.has("size")) { - base_size = keys["size"].to_int(); + base_size = ABS(keys["size"].to_int()); + if (base_size == 0) { + base_size = 16; + } } if (keys.has("outline")) { outline = keys["outline"].to_int(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4bedcb1820..1fa52b9c73 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -412,7 +412,6 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t } VisualShaderNode::Category VisualShaderNode::get_category() const { - WARN_PRINT(get_caption() + " is missing a category."); return CATEGORY_NONE; } @@ -1825,7 +1824,7 @@ void VisualShader::reset_state() { void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { //mode - p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles,Sky,Fog")); //render modes HashMap<String, String> blend_mode_enums; @@ -3004,9 +3003,9 @@ VisualShader::VisualShader() { /////////////////////////////////////////////////////////// const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { - // Node3D + // Spatial - // Node3D, Vertex + // Spatial, Vertex { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, @@ -3043,7 +3042,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom2", "CUSTOM2" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom3", "CUSTOM3" }, - // Node3D, Fragment + // Spatial, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, @@ -3075,7 +3074,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_UINT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, - // Node3D, Light + // Spatial, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, @@ -3883,9 +3882,9 @@ VisualShaderNodeParameterRef::VisualShaderNodeParameterRef() { const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { //////////////////////////////////////////////////////////////////////// - // Node3D. + // Spatial. //////////////////////////////////////////////////////////////////////// - // Node3D, Vertex. + // Spatial, Vertex. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, @@ -3900,7 +3899,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Projection Matrix", "PROJECTION_MATRIX" }, //////////////////////////////////////////////////////////////////////// - // Node3D, Fragment. + // Spatial, Fragment. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, @@ -3932,7 +3931,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Depth", "DEPTH" }, //////////////////////////////////////////////////////////////////////// - // Node3D, Light. + // Spatial, Light. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" }, |