|
@@ -181,6 +181,7 @@ void Spatial::_notification(int p_what) {
|
|
data.parent = nullptr;
|
|
data.parent = nullptr;
|
|
data.C = nullptr;
|
|
data.C = nullptr;
|
|
data.toplevel_active = false;
|
|
data.toplevel_active = false;
|
|
|
|
+ _disable_client_physics_interpolation();
|
|
} break;
|
|
} break;
|
|
case NOTIFICATION_ENTER_WORLD: {
|
|
case NOTIFICATION_ENTER_WORLD: {
|
|
data.inside_world = true;
|
|
data.inside_world = true;
|
|
@@ -238,8 +239,8 @@ void Spatial::_notification(int p_what) {
|
|
#endif
|
|
#endif
|
|
} break;
|
|
} break;
|
|
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
|
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
|
- if (data.physics_interpolation_data) {
|
|
|
|
- data.physics_interpolation_data->global_xform_prev = data.physics_interpolation_data->global_xform_curr;
|
|
|
|
|
|
+ if (data.client_physics_interpolation_data) {
|
|
|
|
+ data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr;
|
|
}
|
|
}
|
|
} break;
|
|
} break;
|
|
|
|
|
|
@@ -275,20 +276,53 @@ Transform Spatial::get_transform() const {
|
|
return data.local_transform;
|
|
return data.local_transform;
|
|
}
|
|
}
|
|
|
|
|
|
-void Spatial::_update_physics_interpolation_data() {
|
|
|
|
- if (!_is_physics_interpolated_client_side()) {
|
|
|
|
- return;
|
|
|
|
|
|
+// Return false to timeout and remove from the client interpolation list.
|
|
|
|
+bool Spatial::update_client_physics_interpolation_data() {
|
|
|
|
+ if (!is_inside_tree() || !_is_physics_interpolated_client_side()) {
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- ERR_FAIL_NULL(data.physics_interpolation_data);
|
|
|
|
- PhysicsInterpolationData &pid = *data.physics_interpolation_data;
|
|
|
|
|
|
+ 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();
|
|
uint64_t tick = Engine::get_singleton()->get_physics_frames();
|
|
- if (tick != pid.current_physics_tick) {
|
|
|
|
- pid.global_xform_prev = pid.global_xform_curr;
|
|
|
|
|
|
+
|
|
|
|
+ // 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.current_physics_tick = tick;
|
|
}
|
|
}
|
|
|
|
|
|
pid.global_xform_curr = get_global_transform();
|
|
pid.global_xform_curr = get_global_transform();
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Spatial::_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_spatials_list.in_list()) {
|
|
|
|
+ tree->client_physics_interpolation_remove_spatial(&_client_physics_interpolation_spatials_list);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ _set_physics_interpolated_client_side(false);
|
|
}
|
|
}
|
|
|
|
|
|
Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fraction) {
|
|
Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fraction) {
|
|
@@ -298,22 +332,41 @@ Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fra
|
|
if (!_is_physics_interpolated_client_side()) {
|
|
if (!_is_physics_interpolated_client_side()) {
|
|
_set_physics_interpolated_client_side(true);
|
|
_set_physics_interpolated_client_side(true);
|
|
|
|
|
|
- ERR_FAIL_COND_V(data.physics_interpolation_data, Transform());
|
|
|
|
- data.physics_interpolation_data = memnew(PhysicsInterpolationData);
|
|
|
|
- data.physics_interpolation_data->global_xform_curr = get_global_transform();
|
|
|
|
- data.physics_interpolation_data->global_xform_prev = data.physics_interpolation_data->global_xform_curr;
|
|
|
|
- data.physics_interpolation_data->current_physics_tick = Engine::get_singleton()->get_physics_frames();
|
|
|
|
|
|
+ ERR_FAIL_COND_V(data.client_physics_interpolation_data, Transform());
|
|
|
|
+ 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 behaviour
|
|
|
|
+ // 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
|
|
// make sure data is up to date
|
|
- _update_physics_interpolation_data();
|
|
|
|
|
|
+ update_client_physics_interpolation_data();
|
|
|
|
|
|
// interpolate the current data
|
|
// interpolate the current data
|
|
- const Transform &xform_curr = data.physics_interpolation_data->global_xform_curr;
|
|
|
|
- const Transform &xform_prev = data.physics_interpolation_data->global_xform_prev;
|
|
|
|
|
|
+ const Transform &xform_curr = data.client_physics_interpolation_data->global_xform_curr;
|
|
|
|
+ const Transform &xform_prev = data.client_physics_interpolation_data->global_xform_prev;
|
|
|
|
|
|
Transform res;
|
|
Transform res;
|
|
TransformInterpolator::interpolate_transform(xform_prev, xform_curr, res, p_interpolation_fraction);
|
|
TransformInterpolator::interpolate_transform(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_spatials_list.in_list()) {
|
|
|
|
+ tree->client_physics_interpolation_add_spatial(&_client_physics_interpolation_spatials_list);
|
|
|
|
+ }
|
|
|
|
+
|
|
return res;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -321,7 +374,7 @@ Transform Spatial::get_global_transform_interpolated() {
|
|
// Pass through if physics interpolation is switched off.
|
|
// Pass through if physics interpolation is switched off.
|
|
// This is a convenience, as it allows you to easy turn off interpolation
|
|
// This is a convenience, as it allows you to easy turn off interpolation
|
|
// without changing any code.
|
|
// without changing any code.
|
|
- if (!is_physics_interpolated_and_enabled()) {
|
|
|
|
|
|
+ if (Engine::get_singleton()->is_in_physics_frame() || !is_physics_interpolated_and_enabled()) {
|
|
return get_global_transform();
|
|
return get_global_transform();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -872,7 +925,7 @@ void Spatial::_bind_methods() {
|
|
}
|
|
}
|
|
|
|
|
|
Spatial::Spatial() :
|
|
Spatial::Spatial() :
|
|
- xform_change(this) {
|
|
|
|
|
|
+ xform_change(this), _client_physics_interpolation_spatials_list(this) {
|
|
data.dirty = DIRTY_NONE;
|
|
data.dirty = DIRTY_NONE;
|
|
data.children_lock = 0;
|
|
data.children_lock = 0;
|
|
|
|
|
|
@@ -886,7 +939,7 @@ Spatial::Spatial() :
|
|
data.disable_scale = false;
|
|
data.disable_scale = false;
|
|
data.vi_visible = true;
|
|
data.vi_visible = true;
|
|
|
|
|
|
- data.physics_interpolation_data = nullptr;
|
|
|
|
|
|
+ data.client_physics_interpolation_data = nullptr;
|
|
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
#ifdef TOOLS_ENABLED
|
|
data.gizmo_disabled = false;
|
|
data.gizmo_disabled = false;
|
|
@@ -899,8 +952,5 @@ Spatial::Spatial() :
|
|
}
|
|
}
|
|
|
|
|
|
Spatial::~Spatial() {
|
|
Spatial::~Spatial() {
|
|
- if (data.physics_interpolation_data) {
|
|
|
|
- memdelete(data.physics_interpolation_data);
|
|
|
|
- data.physics_interpolation_data = nullptr;
|
|
|
|
- }
|
|
|
|
|
|
+ _disable_client_physics_interpolation();
|
|
}
|
|
}
|