diff --git a/core/error_macros.cpp b/core/error_macros.cpp index 4ebb248b270f..a96a1beed33d 100644 --- a/core/error_macros.cpp +++ b/core/error_macros.cpp @@ -34,6 +34,10 @@ #include "core/ustring.h" #include "os/os.h" +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +#include "scene/main/node.h" +#endif + static ErrorHandlerList *error_handler_list = nullptr; void add_error_handler(ErrorHandlerList *p_handler) { @@ -117,3 +121,50 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li void _err_flush_stdout() { fflush(stdout); } + +// Prevent error spam by limiting the warnings to a certain frequency. +void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) { +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + const uint32_t warn_max = 2048; + const uint32_t warn_timeout_seconds = 15; + + static uint32_t warn_count = warn_max; + static uint32_t warn_timeout = warn_timeout_seconds; + + uint32_t time_now = UINT32_MAX; + + if (warn_count) { + warn_count--; + } + + if (!warn_count) { + time_now = OS::get_singleton()->get_ticks_msec() / 1000; + } + + if ((warn_count == 0) && (time_now >= warn_timeout)) { + warn_count = warn_max; + warn_timeout = time_now + warn_timeout_seconds; + + if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + // UINT64_MAX means unused. + if (p_id == UINT64_MAX) { + _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", ERR_HANDLER_WARNING); + } else { + String node_name; + if (p_id != 0) { + if (ObjectDB::get_instance(p_id)) { + Node *node = Object::cast_to(ObjectDB::get_instance(p_id)); + if (node && node->is_inside_tree()) { + node_name = "\"" + String(node->get_path()) + "\""; + } else { + node_name = "\"unknown\""; + } + } + } + + _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", ERR_HANDLER_WARNING); + } + } + } +#endif +} diff --git a/core/error_macros.h b/core/error_macros.h index 30156acbf991..1bcb61672ddc 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -31,6 +31,7 @@ #ifndef ERROR_MACROS_H #define ERROR_MACROS_H +#include "core/object_id.h" #include "core/safe_refcount.h" #include "core/typedefs.h" @@ -86,6 +87,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); void _err_flush_stdout(); +void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string); + #ifndef _STR #define _STR(m_x) #m_x #define _MKSTR(m_x) _STR(m_x) @@ -558,4 +561,14 @@ void _err_flush_stdout(); #define DEV_CHECK_ONCE(m_cond) #endif +/** + * Physics Interpolation warnings. + * These are spam protection warnings. + */ +#define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \ + _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string) + +#define PHYSICS_INTERPOLATION_WARNING(m_string) \ + _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, UINT64_MAX, m_string) + #endif // ERROR_MACROS_H diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 97c566bc5522..db206cf9f687 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -24,6 +24,7 @@ When using physics interpolation, there will be circumstances in which you want to know the interpolated (displayed) transform of a node rather than the standard transform (which may only be accurate to the most recent physics tick). This is particularly important for frame-based operations that take place in [method Node._process], rather than [method Node._physics_process]. Examples include [Camera]s focusing on a node, or finding where to fire lasers from on a frame rather than physics tick. + [b]Note:[/b] This function creates an interpolation pump on the [Spatial] the first time it is called, which can respond to physics interpolation resets. If you get problems with "streaking" when initially following a [Spatial], be sure to call [method get_global_transform_interpolated] at least once [i]before[/i] resetting the [Spatial] physics interpolation. diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index ae7503e2ebca..a1cd08256019 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -45,14 +45,6 @@ Once finished with your RID, you will want to free the RID using the VisualServer's [method free_rid] static method. - - - - - Prevents physics interpolation for the current physics tick. - This is useful when moving a [Camera] to a new location, to give an instantaneous change rather than interpolation from the previous location. - - @@ -80,14 +72,6 @@ Sets camera to use frustum projection. This mode allows adjusting the [code]offset[/code] argument to create "tilted frustum" effects. - - - - - - Turns on and off physics interpolation for the [Camera]. - - diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index cb02b42241af..1046e68d3658 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -6274,9 +6274,15 @@ void SpatialEditor::_init_grid() { bool orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; - PoolVector grid_colors[3]; - PoolVector grid_points[3]; - PoolVector grid_normals[3]; + static LocalVector grid_colors[3]; + static LocalVector grid_points[3]; + static LocalVector grid_normals[3]; + + for (uint32_t n = 0; n < 3; n++) { + grid_colors[n].clear(); + grid_points[n].clear(); + grid_normals[n].clear(); + } Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color"); Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color"); @@ -6402,9 +6408,9 @@ void SpatialEditor::_init_grid() { grid[c] = RID_PRIME(VisualServer::get_singleton()->mesh_create()); Array d; d.resize(VS::ARRAY_MAX); - d[VisualServer::ARRAY_VERTEX] = grid_points[c]; - d[VisualServer::ARRAY_COLOR] = grid_colors[c]; - d[VisualServer::ARRAY_NORMAL] = grid_normals[c]; + d[VisualServer::ARRAY_VERTEX] = (PoolVector)grid_points[c]; + d[VisualServer::ARRAY_COLOR] = (PoolVector)grid_colors[c]; + d[VisualServer::ARRAY_NORMAL] = (PoolVector)grid_normals[c]; VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[c], VisualServer::PRIMITIVE_LINES, d); VisualServer::get_singleton()->mesh_surface_set_material(grid[c], 0, grid_mat[c]->get_rid()); grid_instance[c] = VisualServer::get_singleton()->instance_create2(grid[c], get_tree()->get_root()->get_world()->get_scenario()); diff --git a/main/main.cpp b/main/main.cpp index e53b9a2462ef..ed0ca5ca6ba4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2367,14 +2367,14 @@ bool Main::iteration() { uint64_t physics_begin = OS::get_singleton()->get_ticks_usec(); - PhysicsServer::get_singleton()->flush_queries(); - // Prepare the fixed timestep interpolated nodes - // BEFORE they are updated by the physics 2D, + // BEFORE they are updated by the physics, // otherwise the current and previous transforms // may be the same, and no interpolation takes place. OS::get_singleton()->get_main_loop()->iteration_prepare(); + PhysicsServer::get_singleton()->flush_queries(); + Physics2DServer::get_singleton()->sync(); Physics2DServer::get_singleton()->flush_queries(); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 7dbb6b9380ab..629cbb229283 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1267,7 +1267,7 @@ void CPUParticles2D::_notification(int p_what) { } } } - if (p_what == NOTIFICATION_RESET_PHYSICS_INTERPOLATION) { + if (p_what == NOTIFICATION_RESET_PHYSICS_INTERPOLATION && is_inside_tree()) { // Make sure current is up to date with any pending global transform changes. _interpolation_data.global_xform_curr = get_global_transform_const(); _interpolation_data.global_xform_prev = _interpolation_data.global_xform_curr; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index ebbe0775dd3f..5e3f9b6ead5a 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -29,6 +29,8 @@ /**************************************************************************/ #include "skeleton_2d.h" +#include "core/engine.h" +#include "core/math/transform_interpolator.h" void Bone2D::_order_changed_in_parent() { if (skeleton) { @@ -270,20 +272,76 @@ Bone2D *Skeleton2D::get_bone(int p_idx) { return bones[p_idx].bone; } -void Skeleton2D::_notification(int p_what) { - if (p_what == NOTIFICATION_READY) { - if (bone_setup_dirty) { - _update_bone_setup(); - } - if (transform_dirty) { - _update_transform(); - } +void Skeleton2D::_update_process_mode() { + bool process = is_physics_interpolated_and_enabled() && is_visible_in_tree(); + + set_process_internal(process); + set_physics_process_internal(process); +} - request_ready(); +void Skeleton2D::_ensure_update_interpolation_data() { + 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; } +} - if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { - VS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); +void Skeleton2D::_physics_interpolated_changed() { + _update_process_mode(); +} + +void Skeleton2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + if (bone_setup_dirty) { + _update_bone_setup(); + } + if (transform_dirty) { + _update_transform(); + } + + request_ready(); + } break; + case NOTIFICATION_ENTER_TREE: { + _update_process_mode(); + + if (is_physics_interpolated_and_enabled()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_physics_interpolated_and_enabled()) { + _ensure_update_interpolation_data(); + if (Engine::get_singleton()->is_in_physics_frame()) { + _interpolation_data.xform_curr = get_global_transform(); + } + } else { + VS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); + } + } break; + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + _ensure_update_interpolation_data(); + _interpolation_data.xform_curr = get_global_transform(); + } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + Transform2D res; + TransformInterpolator::interpolate_transform_2d(_interpolation_data.xform_prev, _interpolation_data.xform_curr, res, Engine::get_singleton()->get_physics_interpolation_fraction()); + VS::get_singleton()->skeleton_set_base_transform_2d(skeleton, res); + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + _update_process_mode(); + } break; } } diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 22773b8e4cb4..31e1e6c85c4c 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -101,10 +101,21 @@ class Skeleton2D : public Node2D { RID skeleton; + void _update_process_mode(); + void _ensure_update_interpolation_data(); + + struct InterpolationData { + Transform2D xform_curr; + Transform2D xform_prev; + uint32_t last_update_physics_tick = UINT32_MAX; + } _interpolation_data; + protected: void _notification(int p_what); static void _bind_methods(); + virtual void _physics_interpolated_changed(); + public: int get_bone_count() const; Bone2D *get_bone(int p_idx); diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 61fa3e21b678..62463c6ef6f8 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -33,8 +33,11 @@ #include "collision_object.h" #include "core/engine.h" #include "core/math/camera_matrix.h" +#include "core/math/transform_interpolator.h" #include "scene/resources/material.h" #include "scene/resources/surface_tool.h" +#include "servers/visual/visual_server_constants.h" + void Camera::_update_audio_listener_state() { } @@ -79,7 +82,16 @@ void Camera::_update_camera() { return; } - VisualServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + if (!is_physics_interpolated_and_enabled()) { + VisualServer::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); + VisualServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } // here goes listener stuff /* @@ -99,7 +111,61 @@ void Camera::_update_camera() { } void Camera::_physics_interpolated_changed() { - VisualServer::get_singleton()->camera_set_interpolated(camera, is_physics_interpolated()); + _update_process_mode(); +} + +void Camera::_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 Camera::_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(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction()); + + Transform &tr = id.camera_xform_interpolated; + tr = _get_adjusted_camera_transform(id.xform_interpolated); + } +} + +void Camera::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 Camera::_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 Camera::_notification(int p_what) { @@ -116,15 +182,48 @@ void Camera::_notification(int p_what) { viewport->_camera_set(this); } } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(); + +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated Camera: " + 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 + + VisualServer::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 Camera 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_physics_interpolated()) { - VisualServer::get_singleton()->camera_reset_physics_interpolation(camera); + if (is_inside_tree()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; } } break; case NOTIFICATION_EXIT_WORLD: { @@ -148,22 +247,33 @@ void Camera::_notification(int p_what) { if (viewport) { viewport->find_world()->_register_camera(this); } + _update_process_mode(); } break; case NOTIFICATION_LOST_CURRENT: { if (viewport) { viewport->find_world()->_remove_camera(this); } + _update_process_mode(); } break; } } -Transform Camera::get_camera_transform() const { - Transform tr = get_global_transform().orthonormalized(); +Transform Camera::_get_adjusted_camera_transform(const Transform &p_xform) const { + Transform tr = p_xform.orthonormalized(); tr.origin += tr.basis.get_axis(1) * v_offset; tr.origin += tr.basis.get_axis(0) * h_offset; return tr; } +Transform Camera::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()); +} + void Camera::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) { return; @@ -365,6 +475,10 @@ Point2 Camera::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; @@ -692,25 +806,48 @@ float ClippedCamera::get_margin() const { return margin; } void ClippedCamera::set_process_mode(ProcessMode p_mode) { + if (is_physics_interpolated_and_enabled() && p_mode == CLIP_PROCESS_IDLE) { + p_mode = CLIP_PROCESS_PHYSICS; + WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode."); + } + if (process_mode == p_mode) { return; } process_mode = p_mode; - set_process_internal(process_mode == CLIP_PROCESS_IDLE); - set_physics_process_internal(process_mode == CLIP_PROCESS_PHYSICS); + + set_desired_process_modes(process_mode == CLIP_PROCESS_IDLE, process_mode == CLIP_PROCESS_PHYSICS); } ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const { return process_mode; } -Transform ClippedCamera::get_camera_transform() const { - Transform t = Camera::get_camera_transform(); +void ClippedCamera::physics_interpolation_flip_data() { + _interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr; +} + +void ClippedCamera::_physics_interpolated_changed() { + // Switch process mode to physics if we are turning on interpolation. + // Idle process mode doesn't work well with physics interpolation. + set_process_mode(get_process_mode()); + + Camera::_physics_interpolated_changed(); +} + +Transform ClippedCamera::_get_adjusted_camera_transform(const Transform &p_xform) const { + Transform t = Camera::_get_adjusted_camera_transform(p_xform); t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset; return t; } void ClippedCamera::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + if (p_what == NOTIFICATION_ENTER_TREE) { + // Switch process mode to physics if we are turning on interpolation. + // Idle process mode doesn't work well with physics interpolation. + set_process_mode(get_process_mode()); + } + + if (((p_what == NOTIFICATION_INTERNAL_PROCESS) && process_mode == CLIP_PROCESS_IDLE) || ((p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) && process_mode == CLIP_PROCESS_PHYSICS)) { Spatial *parent = Object::cast_to(get_parent()); if (!parent) { return; @@ -732,7 +869,7 @@ void ClippedCamera::_notification(int p_what) { Vector3 ray_from = parent_plane.project(cam_pos); - clip_offset = 0; //reset by defau;t + _interpolation_data.clip_offset_curr = 0; // Reset by default. { //check if points changed Vector local_points = get_near_plane_points(); @@ -758,15 +895,29 @@ void ClippedCamera::_notification(int p_what) { float closest_safe = 1.0f, closest_unsafe = 1.0f; if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) { - clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe); + _interpolation_data.clip_offset_curr = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe); + } + + // Default to use the current value + // (in the case of non-interpolated). + if (!is_physics_interpolated_and_enabled()) { + clip_offset = _interpolation_data.clip_offset_curr; } _update_camera(); } + if (is_physics_interpolated_and_enabled() && (p_what == NOTIFICATION_INTERNAL_PROCESS)) { + clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev; + } + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { update_gizmo(); } + + if (p_what == NOTIFICATION_RESET_PHYSICS_INTERPOLATION) { + _interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr; + } } void ClippedCamera::set_collision_mask(uint32_t p_mask) { @@ -881,9 +1032,11 @@ void ClippedCamera::_bind_methods() { } ClippedCamera::ClippedCamera() { margin = 0; - clip_offset = 0; - process_mode = CLIP_PROCESS_PHYSICS; - set_physics_process_internal(true); + + // Force initializing to physics (prevent noop check). + process_mode = CLIP_PROCESS_IDLE; + set_process_mode(CLIP_PROCESS_PHYSICS); + collision_mask = 1; set_notify_local_transform(Engine::get_singleton()->is_editor_hint()); points.resize(5); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 4200c92bb7a1..e090185eeaee 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -61,6 +61,7 @@ class Camera : public Spatial { private: bool force_change; bool current; + Viewport *viewport; Projection mode; @@ -90,13 +91,44 @@ class Camera : public Spatial { Ref velocity_tracker; bool affect_lod = true; + /////////////////////////////////////////////////////// + // 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 Cameras, + // 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 { + Transform xform_curr; + Transform xform_prev; + Transform xform_interpolated; + Transform 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(); + virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const; + /////////////////////////////////////////////////////// + void _update_camera(); virtual void _request_camera_update(); void _update_camera_mode(); - virtual void _physics_interpolated_changed(); - void _notification(int p_what); virtual void _validate_property(PropertyInfo &p_property) const; @@ -135,7 +167,7 @@ class Camera : public Spatial { void set_znear(float p_znear); void set_frustum_offset(Vector2 p_offset); - virtual Transform get_camera_transform() const; + Transform get_camera_transform() const; virtual Vector3 project_ray_normal(const Point2 &p_pos) const; virtual Vector3 project_ray_origin(const Point2 &p_pos) const; @@ -195,7 +227,9 @@ class ClippedCamera : public Camera { ProcessMode process_mode; RID pyramid_shape; float margin; - float clip_offset; + + float clip_offset = 0; + uint32_t collision_mask; bool clip_to_areas; bool clip_to_bodies; @@ -204,10 +238,21 @@ class ClippedCamera : public Camera { Vector points; + /////////////////////////////////////////////////////// + // INTERPOLATION FUNCTIONS + struct InterpolatedData { + float clip_offset_curr = 0; + float clip_offset_prev = 0; + } _interpolation_data; + protected: + virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const; + virtual void physics_interpolation_flip_data(); + virtual void _physics_interpolated_changed(); + /////////////////////////////////////////////////////// + void _notification(int p_what); static void _bind_methods(); - virtual Transform get_camera_transform() const; public: void set_clip_to_areas(bool p_clip); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 6f0e7ee0dd91..146097ea8503 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -900,14 +900,6 @@ void CPUParticles::_particles_process(float p_delta) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; } - - // Teleport if starting a new particle, so - // we don't get a streak from the old position - // to this new start. - if (_interpolated) { - p.copy_to(particles_prev[i]); - } - } else if (!p.active) { continue; } else if (p.time > p.lifetime) { @@ -1012,6 +1004,13 @@ void CPUParticles::_particles_process(float p_delta) { } p.transform.origin += p.velocity * local_delta; + + // Teleport if starting a new particle, so + // we don't get a streak from the old position + // to this new start. + if (restart && _interpolated) { + p.copy_to(particles_prev[i]); + } } } diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 4bb5b74c8d45..cac9673bfa06 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -359,7 +359,7 @@ void Spatial::_disable_client_physics_interpolation() { } Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fraction) { - ERR_FAIL_NULL_V(is_inside_tree(), Transform()); + ERR_FAIL_COND_V(!is_inside_tree(), Transform()); // set in motion the mechanisms for client side interpolation if not already active if (!_is_physics_interpolated_client_side()) { @@ -407,7 +407,14 @@ Transform Spatial::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 (Engine::get_singleton()->is_in_physics_frame() || !is_physics_interpolated_and_enabled()) { + 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(); } diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 71dd5cf468db..cf80a6742dd7 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -99,12 +99,11 @@ void VisualInstance::_notification(int p_what) { case NOTIFICATION_TRANSFORM_CHANGED: { if (_is_vi_visible() || is_physics_interpolated_and_enabled()) { if (!_is_using_identity_transform()) { - Transform gt = get_global_transform(); - VisualServer::get_singleton()->instance_set_transform(instance, gt); + VisualServer::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()) { + if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) { if (_is_vi_visible()) { _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); } @@ -114,7 +113,14 @@ void VisualInstance::_notification(int p_what) { } } break; case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { - if (_is_vi_visible() && is_physics_interpolated()) { + if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) { + // We must ensure the VisualServer 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()) { + VisualServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + VisualServer::get_singleton()->instance_reset_physics_interpolation(instance); } } break; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 1ae0d3d812db..2cee99eac3a8 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -101,6 +101,12 @@ void Node::_notification(int p_notification) { get_tree()->node_count++; orphan_node_count--; + // Allow physics interpolated nodes to automatically reset when added to the tree + // (this is to save the user doing this manually each time). + if (get_tree()->is_physics_interpolation_enabled()) { + _set_physics_interpolation_reset_requested(true); + } + } break; case NOTIFICATION_EXIT_TREE: { ERR_FAIL_COND(!get_viewport()); @@ -221,14 +227,14 @@ void Node::_propagate_physics_interpolated(bool p_interpolated) { data.blocked--; } -void Node::_propagate_physics_interpolation_reset_requested() { +void Node::_propagate_physics_interpolation_reset_requested(bool p_requested) { if (is_physics_interpolated()) { - data.physics_interpolation_reset_requested = true; + data.physics_interpolation_reset_requested = p_requested; } data.blocked++; for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_physics_interpolation_reset_requested(); + data.children[i]->_propagate_physics_interpolation_reset_requested(p_requested); } data.blocked--; } @@ -885,15 +891,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); + } } float Node::get_physics_process_delta_time() const { @@ -1297,12 +1311,6 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { //recognize children created in this node constructor p_child->data.parent_owned = data.in_constructor; add_child_notify(p_child); - - // Allow physics interpolated nodes to automatically reset when added to the tree - // (this is to save the user doing this manually each time) - if (is_inside_tree() && get_tree()->is_physics_interpolation_enabled()) { - p_child->_propagate_physics_interpolation_reset_requested(); - } } void Node::add_child(Node *p_child, bool p_force_readable_name) { diff --git a/scene/main/node.h b/scene/main/node.h index 85268df14c5d..4ffd585ec26f 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -209,7 +209,7 @@ class Node : public Object { void _propagate_exit_tree(); void _propagate_after_exit_branch(bool p_exiting_tree); void _propagate_physics_interpolated(bool p_interpolated); - void _propagate_physics_interpolation_reset_requested(); + void _propagate_physics_interpolation_reset_requested(bool p_requested); void _print_stray_nodes(); void _propagate_pause_owner(Node *p_owner); void _propagate_groups_dirty(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 48e9c0a4d060..3c1809c3586b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -555,7 +555,15 @@ void SceneTree::client_physics_interpolation_remove_spatial(SelfList *p 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(); VisualServer::get_singleton()->tick(); + + // 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(); } } @@ -572,11 +580,6 @@ bool SceneTree::iteration(float p_time) { current_frame++; - // Any objects performing client physics interpolation - // should be given an opportunity to keep their previous transforms - // up to take before each new physics tick. - _client_physics_interpolation.physics_process(); - flush_transform_notifications(); if (MainLoop::iteration(p_time)) { diff --git a/servers/visual/rasterizer.cpp b/servers/visual/rasterizer.cpp index ac31b61786da..14fd50db583f 100644 --- a/servers/visual/rasterizer.cpp +++ b/servers/visual/rasterizer.cpp @@ -361,11 +361,7 @@ void RasterizerStorage::multimesh_instance_set_transform(RID p_multimesh, int p_ #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) if (!Engine::get_singleton()->is_in_physics_frame()) { - static int32_t warn_count = 0; - warn_count++; - if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign)."); - } + PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process"); } #endif return; @@ -521,11 +517,7 @@ void RasterizerStorage::multimesh_set_as_bulk_array_interpolated(RID p_multimesh _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) if (!Engine::get_singleton()->is_in_physics_frame()) { - static int32_t warn_count = 0; - warn_count++; - if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign)."); - } + PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process"); } #endif } @@ -541,11 +533,7 @@ void RasterizerStorage::multimesh_set_as_bulk_array(RID p_multimesh, const PoolV _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) if (!Engine::get_singleton()->is_in_physics_frame()) { - static int32_t warn_count = 0; - warn_count++; - if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign)."); - } + PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process"); } #endif return; diff --git a/servers/visual/visual_server_constants.h b/servers/visual/visual_server_constants.h index 82b3bcb8b688..3520972e23cf 100644 --- a/servers/visual/visual_server_constants.h +++ b/servers/visual/visual_server_constants.h @@ -53,6 +53,9 @@ // This is expensive. // #define VISUAL_SERVER_CANVAS_CHECK_BOUNDS +// Uncomment this define to produce debugging output for physics interpolation. +// #define VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + #endif // DEV_ENABLED #endif // VISUAL_SERVER_CONSTANTS_H diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index f57c7b80d51c..589446ce4c33 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -461,8 +461,6 @@ class VisualServerRaster : public VisualServer { BIND4(camera_set_orthogonal, RID, float, float, float) BIND5(camera_set_frustum, RID, float, Vector2, float, float) BIND2(camera_set_transform, RID, const Transform &) - BIND2(camera_set_interpolated, RID, bool) - BIND1(camera_reset_physics_interpolation, RID) BIND2(camera_set_cull_mask, RID, uint32_t) BIND2(camera_set_environment, RID, RID) BIND2(camera_set_use_vertical_aspect, RID, bool) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index fba46e02b22d..bdea223d5cee 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -40,16 +40,6 @@ /* CAMERA API */ -Transform VisualServerScene::Camera::get_transform_interpolated() const { - if (!interpolated) { - return transform; - } - - Transform final; - TransformInterpolator::interpolate_transform_via_method(transform_prev, transform, final, Engine::get_singleton()->get_physics_interpolation_fraction(), interpolation_method); - return final; -} - RID VisualServerScene::camera_create() { Camera *camera = memnew(Camera); return camera_owner.make_rid(camera); @@ -83,59 +73,11 @@ void VisualServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p camera->zfar = p_z_far; } -void VisualServerScene::camera_reset_physics_interpolation(RID p_camera) { - Camera *camera = camera_owner.get(p_camera); - ERR_FAIL_COND(!camera); - - if (_interpolation_data.interpolation_enabled && camera->interpolated) { - _interpolation_data.camera_teleport_list.push_back(p_camera); - } -} - -void VisualServerScene::camera_set_interpolated(RID p_camera, bool p_interpolated) { - Camera *camera = camera_owner.get(p_camera); - ERR_FAIL_COND(!camera); - camera->interpolated = p_interpolated; -} - void VisualServerScene::camera_set_transform(RID p_camera, const Transform &p_transform) { Camera *camera = camera_owner.get(p_camera); ERR_FAIL_COND(!camera); camera->transform = p_transform.orthonormalized(); - - if (_interpolation_data.interpolation_enabled) { - if (camera->interpolated) { - if (!camera->on_interpolate_transform_list) { - _interpolation_data.camera_transform_update_list_curr->push_back(p_camera); - camera->on_interpolate_transform_list = true; - } - - // decide on the interpolation method .. slerp if possible - camera->interpolation_method = TransformInterpolator::find_method(camera->transform_prev.basis, camera->transform.basis); - -#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) - if (!Engine::get_singleton()->is_in_physics_frame()) { - // Effectively a WARN_PRINT_ONCE but after a certain number of occurrences. - static int32_t warn_count = -256; - if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - WARN_PRINT("[Physics interpolation] Camera interpolation is being triggered from outside physics process, this might lead to issues (possibly benign)."); - } - warn_count++; - } -#endif - } else { -#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) - if (Engine::get_singleton()->is_in_physics_frame()) { - static int32_t warn_count = -256; - if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - WARN_PRINT("[Physics interpolation] Non-interpolated Camera is being triggered from physics process, this might lead to issues (possibly benign)."); - } - warn_count++; - } -#endif - } - } } void VisualServerScene::camera_set_cull_mask(RID p_camera, uint32_t p_layers) { @@ -828,7 +770,13 @@ void VisualServerScene::instance_reset_physics_interpolation(RID p_instance) { ERR_FAIL_COND(!instance); if (_interpolation_data.interpolation_enabled && instance->interpolated) { - _interpolation_data.instance_teleport_list.push_back(p_instance); + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames())); + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif } } @@ -842,6 +790,10 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform & Instance *instance = instance_owner.get(p_instance); ERR_FAIL_COND(!instance); +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + if (!(_interpolation_data.interpolation_enabled && instance->interpolated) || !instance->scenario) { if (instance->transform == p_transform) { return; //must be checked to avoid worst evil @@ -865,24 +817,7 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform & #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) if ((_interpolation_data.interpolation_enabled && !instance->interpolated) && (Engine::get_singleton()->is_in_physics_frame())) { - static int32_t warn_count = 0; - warn_count++; - if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - String node_name; - ObjectID id = instance->object_id; - if (id != 0) { - if (ObjectDB::get_instance(id)) { - Node *node = Object::cast_to(ObjectDB::get_instance(id)); - if (node && node->is_inside_tree()) { - node_name = "\"" + String(node->get_path()) + "\""; - } else { - node_name = "\"unknown\""; - } - } - } - - WARN_PRINT("[Physics interpolation] Non-interpolated Instance is being triggered from physics process, this might lead to issues: " + node_name + " (possibly benign)."); - } + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Non-interpolated triggered from physics process"); } #endif @@ -918,6 +853,10 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform & instance->transform_curr = p_transform; +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif + // keep checksums up to date instance->transform_checksum_curr = new_checksum; @@ -950,41 +889,11 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform & #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) if (!Engine::get_singleton()->is_in_physics_frame()) { - static int32_t warn_count = 0; - warn_count++; - if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - String node_name; - ObjectID id = instance->object_id; - if (id != 0) { - if (ObjectDB::get_instance(id)) { - Node *node = Object::cast_to(ObjectDB::get_instance(id)); - if (node && node->is_inside_tree()) { - node_name = "\"" + String(node->get_path()) + "\""; - } else { - node_name = "\"unknown\""; - } - } - } - - WARN_PRINT("[Physics interpolation] Instance interpolation is being triggered from outside physics process, this might lead to issues: " + node_name + " (possibly benign)."); - } + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Interpolated triggered from outside physics process"); } #endif } -void VisualServerScene::InterpolationData::notify_free_camera(RID p_rid, Camera &r_camera) { - r_camera.on_interpolate_transform_list = false; - - if (!interpolation_enabled) { - return; - } - - // if the camera was on any of the lists, remove - camera_transform_update_list_curr->erase_multiple_unordered(p_rid); - camera_transform_update_list_prev->erase_multiple_unordered(p_rid); - camera_teleport_list.erase_multiple_unordered(p_rid); -} - void VisualServerScene::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) { r_instance.on_interpolate_list = false; r_instance.on_interpolate_transform_list = false; @@ -997,10 +906,13 @@ void VisualServerScene::InterpolationData::notify_free_instance(RID p_rid, Insta instance_interpolate_update_list.erase_multiple_unordered(p_rid); instance_transform_update_list_curr->erase_multiple_unordered(p_rid); instance_transform_update_list_prev->erase_multiple_unordered(p_rid); - instance_teleport_list.erase_multiple_unordered(p_rid); } void VisualServerScene::update_interpolation_tick(bool p_process) { +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + // update interpolation in storage VSG::storage->update_interpolation_tick(p_process); @@ -1057,64 +969,12 @@ void VisualServerScene::update_interpolation_tick(bool p_process) { // prepare for the next iteration _interpolation_data.instance_transform_update_list_curr->clear(); - - // CAMERAS - // detect any that were on the previous transform list that are no longer active, - for (unsigned int n = 0; n < _interpolation_data.camera_transform_update_list_prev->size(); n++) { - const RID &rid = (*_interpolation_data.camera_transform_update_list_prev)[n]; - Camera *camera = camera_owner.getornull(rid); - - // no longer active? (either the instance deleted or no longer being transformed) - if (camera && !camera->on_interpolate_transform_list) { - camera->transform = camera->transform_prev; - } - } - - // cameras , swap any current with previous - for (unsigned int n = 0; n < _interpolation_data.camera_transform_update_list_curr->size(); n++) { - const RID &rid = (*_interpolation_data.camera_transform_update_list_curr)[n]; - Camera *camera = camera_owner.getornull(rid); - if (camera) { - camera->transform_prev = camera->transform; - camera->on_interpolate_transform_list = false; - } - } - - // we maintain a mirror list for the transform updates, so we can detect when an instance - // is no longer being transformed, and remove it from the interpolate list - SWAP(_interpolation_data.camera_transform_update_list_curr, _interpolation_data.camera_transform_update_list_prev); - - // prepare for the next iteration - _interpolation_data.camera_transform_update_list_curr->clear(); } void VisualServerScene::update_interpolation_frame(bool p_process) { // update interpolation in storage VSG::storage->update_interpolation_frame(p_process); - // teleported instances - for (unsigned int n = 0; n < _interpolation_data.instance_teleport_list.size(); n++) { - const RID &rid = _interpolation_data.instance_teleport_list[n]; - Instance *instance = instance_owner.getornull(rid); - if (instance) { - instance->transform_prev = instance->transform_curr; - instance->transform_checksum_prev = instance->transform_checksum_curr; - } - } - - _interpolation_data.instance_teleport_list.clear(); - - // camera teleports - for (unsigned int n = 0; n < _interpolation_data.camera_teleport_list.size(); n++) { - const RID &rid = _interpolation_data.camera_teleport_list[n]; - Camera *camera = camera_owner.getornull(rid); - if (camera) { - camera->transform_prev = camera->transform; - } - } - - _interpolation_data.camera_teleport_list.clear(); - if (p_process) { real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); @@ -1124,6 +984,10 @@ void VisualServerScene::update_interpolation_frame(bool p_process) { if (instance) { TransformInterpolator::interpolate_transform_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method); +#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + // make sure AABBs are constantly up to date through the interpolation _instance_queue_update(instance, true); } @@ -2930,10 +2794,8 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view } break; } - Transform camera_transform = _interpolation_data.interpolation_enabled ? camera->get_transform_interpolated() : camera->transform; - - _prepare_scene(camera_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint); - _render_scene(camera_transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1); + _prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint); + _render_scene(camera->transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1); #endif } @@ -4615,8 +4477,6 @@ void VisualServerScene::update_dirty_instances() { bool VisualServerScene::free(RID p_rid) { if (camera_owner.owns(p_rid)) { Camera *camera = camera_owner.get(p_rid); - _interpolation_data.notify_free_camera(p_rid, *camera); - camera_owner.free(p_rid); memdelete(camera); } else if (scenario_owner.owns(p_rid)) { diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index cf1f7c932c25..56b6ceb5b60b 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -81,20 +81,12 @@ class VisualServerScene { uint32_t visible_layers; RID env; - // transform_prev is only used when using fixed timestep interpolation Transform transform; - Transform transform_prev; - - bool interpolated : 1; - bool on_interpolate_transform_list : 1; bool vaspect : 1; - TransformInterpolator::Method interpolation_method : 3; int32_t previous_room_id_hint; - Transform get_transform_interpolated() const; - Camera() { visible_layers = 0xFFFFFFFF; fov = 70; @@ -105,9 +97,6 @@ class VisualServerScene { offset = Vector2(); vaspect = false; previous_room_id_hint = -1; - interpolated = true; - on_interpolate_transform_list = false; - interpolation_method = TransformInterpolator::INTERP_LERP; } }; @@ -118,8 +107,6 @@ class VisualServerScene { virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far); virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far); virtual void camera_set_transform(RID p_camera, const Transform &p_transform); - virtual void camera_set_interpolated(RID p_camera, bool p_interpolated); - virtual void camera_reset_physics_interpolation(RID p_camera); virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); virtual void camera_set_environment(RID p_camera, RID p_env); virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); @@ -410,18 +397,11 @@ class VisualServerScene { virtual void set_physics_interpolation_enabled(bool p_enabled); struct InterpolationData { - void notify_free_camera(RID p_rid, Camera &r_camera); void notify_free_instance(RID p_rid, Instance &r_instance); LocalVector instance_interpolate_update_list; LocalVector instance_transform_update_lists[2]; LocalVector *instance_transform_update_list_curr = &instance_transform_update_lists[0]; LocalVector *instance_transform_update_list_prev = &instance_transform_update_lists[1]; - LocalVector instance_teleport_list; - - LocalVector camera_transform_update_lists[2]; - LocalVector *camera_transform_update_list_curr = &camera_transform_update_lists[0]; - LocalVector *camera_transform_update_list_prev = &camera_transform_update_lists[1]; - LocalVector camera_teleport_list; bool interpolation_enabled = false; } _interpolation_data; diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 549a267a4a11..e934962594d3 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -375,8 +375,6 @@ class VisualServerWrapMT : public VisualServer { FUNC4(camera_set_orthogonal, RID, float, float, float) FUNC5(camera_set_frustum, RID, float, Vector2, float, float) FUNC2(camera_set_transform, RID, const Transform &) - FUNC2(camera_set_interpolated, RID, bool) - FUNC1(camera_reset_physics_interpolation, RID) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 903121505942..a9399e32b0c9 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2095,8 +2095,6 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("camera_set_orthogonal", "camera", "size", "z_near", "z_far"), &VisualServer::camera_set_orthogonal); ClassDB::bind_method(D_METHOD("camera_set_frustum", "camera", "size", "offset", "z_near", "z_far"), &VisualServer::camera_set_frustum); ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &VisualServer::camera_set_transform); - ClassDB::bind_method(D_METHOD("camera_set_interpolated", "camera", "interpolated"), &VisualServer::camera_set_interpolated); - ClassDB::bind_method(D_METHOD("camera_reset_physics_interpolation", "camera"), &VisualServer::camera_reset_physics_interpolation); ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &VisualServer::camera_set_cull_mask); ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &VisualServer::camera_set_environment); ClassDB::bind_method(D_METHOD("camera_set_use_vertical_aspect", "camera", "enable"), &VisualServer::camera_set_use_vertical_aspect); diff --git a/servers/visual_server.h b/servers/visual_server.h index 02822bf57069..71fbaa03da8c 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -634,8 +634,6 @@ class VisualServer : public Object { virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0; virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0; virtual void camera_set_transform(RID p_camera, const Transform &p_transform) = 0; - virtual void camera_set_interpolated(RID p_camera, bool p_interpolated) = 0; - virtual void camera_reset_physics_interpolation(RID p_camera) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0;