mirror of
https://github.com/godotengine/godot.git
synced 2025-10-15 02:49:24 +00:00
Physics Interpolation - Add InterpolatedProperty
And add some basic interpolated properties to Camera.
This commit is contained in:
@@ -552,10 +552,14 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
#define DEV_CHECK_ONCE(m_cond) \
|
||||
if (unlikely(!(m_cond))) { \
|
||||
ERR_PRINT_ONCE("DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
|
||||
} else \
|
||||
#define DEV_CHECK_ONCE(m_cond) \
|
||||
if (true) { \
|
||||
static bool first_print = true; \
|
||||
if (first_print && unlikely(!(m_cond))) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
|
||||
first_print = false; \
|
||||
} \
|
||||
} else \
|
||||
((void)0)
|
||||
#else
|
||||
#define DEV_CHECK_ONCE(m_cond)
|
||||
|
46
core/interpolated_property.cpp
Normal file
46
core/interpolated_property.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* interpolated_property.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "interpolated_property.h"
|
||||
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/math/vector2.h"
|
||||
|
||||
namespace InterpolatedPropertyFuncs {
|
||||
|
||||
float lerp(float p_a, float p_b, float p_fraction) {
|
||||
return Math::lerp(p_a, p_b, p_fraction);
|
||||
}
|
||||
|
||||
Vector2 lerp(const Vector2 &p_a, const Vector2 &p_b, float p_fraction) {
|
||||
return p_a.linear_interpolate(p_b, p_fraction);
|
||||
}
|
||||
|
||||
} //namespace InterpolatedPropertyFuncs
|
109
core/interpolated_property.h
Normal file
109
core/interpolated_property.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/**************************************************************************/
|
||||
/* interpolated_property.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef INTERPOLATED_PROPERTY_H
|
||||
#define INTERPOLATED_PROPERTY_H
|
||||
|
||||
struct Vector2;
|
||||
|
||||
namespace InterpolatedPropertyFuncs {
|
||||
float lerp(float p_a, float p_b, float p_fraction);
|
||||
Vector2 lerp(const Vector2 &p_a, const Vector2 &p_b, float p_fraction);
|
||||
} //namespace InterpolatedPropertyFuncs
|
||||
|
||||
// This class is intended to reduce the boiler plate involved to
|
||||
// support custom properties to be physics interpolated.
|
||||
|
||||
template <class T>
|
||||
class InterpolatedProperty {
|
||||
// Only needs interpolating / updating the servers when
|
||||
// curr and prev are different.
|
||||
bool _needs_interpolating = false;
|
||||
T _interpolated;
|
||||
T curr;
|
||||
T prev;
|
||||
|
||||
public:
|
||||
// FTI depends on the constant flow between current values
|
||||
// (on the current tick) and stored previous values (on the previous tick).
|
||||
// These should be updated both on each tick, and also on resets.
|
||||
void pump() {
|
||||
prev = curr;
|
||||
_needs_interpolating = false;
|
||||
}
|
||||
void reset() { pump(); }
|
||||
|
||||
void set_interpolated_value(const T &p_val) {
|
||||
_interpolated = p_val;
|
||||
}
|
||||
const T &interpolated() const { return _interpolated; }
|
||||
bool needs_interpolating() const { return _needs_interpolating; }
|
||||
|
||||
bool interpolate(float p_interpolation_fraction) {
|
||||
if (_needs_interpolating) {
|
||||
_interpolated = InterpolatedPropertyFuncs::lerp(prev, curr, p_interpolation_fraction);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
return curr;
|
||||
}
|
||||
|
||||
bool operator==(const T &p_o) const {
|
||||
return p_o == curr;
|
||||
}
|
||||
|
||||
bool operator!=(const T &p_o) const {
|
||||
return p_o != curr;
|
||||
}
|
||||
|
||||
InterpolatedProperty &operator=(T p_val) {
|
||||
curr = p_val;
|
||||
_interpolated = p_val;
|
||||
_needs_interpolating = true;
|
||||
return *this;
|
||||
}
|
||||
InterpolatedProperty(T p_val) {
|
||||
curr = p_val;
|
||||
_interpolated = p_val;
|
||||
pump();
|
||||
}
|
||||
InterpolatedProperty() {
|
||||
// Ensure either the constructor is run,
|
||||
// or the memory is zeroed if using a fundamental type.
|
||||
_interpolated = T{};
|
||||
curr = T{};
|
||||
prev = T{};
|
||||
}
|
||||
};
|
||||
|
||||
#endif // INTERPOLATED_PROPERTY_H
|
@@ -45,11 +45,63 @@ void Camera::_request_camera_update() {
|
||||
_update_camera();
|
||||
}
|
||||
|
||||
void Camera::fti_update_servers() {
|
||||
void Camera::fti_pump_property() {
|
||||
switch (mode) {
|
||||
default:
|
||||
break;
|
||||
case PROJECTION_PERSPECTIVE: {
|
||||
fov.pump();
|
||||
} break;
|
||||
case PROJECTION_ORTHOGONAL: {
|
||||
size.pump();
|
||||
} break;
|
||||
case PROJECTION_FRUSTUM: {
|
||||
size.pump();
|
||||
frustum_offset.pump();
|
||||
} break;
|
||||
}
|
||||
near.pump();
|
||||
far.pump();
|
||||
|
||||
Spatial::fti_pump_property();
|
||||
}
|
||||
|
||||
void Camera::fti_update_servers_property() {
|
||||
if (camera.is_valid()) {
|
||||
float f = Engine::get_singleton()->get_physics_interpolation_fraction();
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
break;
|
||||
case PROJECTION_PERSPECTIVE: {
|
||||
// If there have been changes due to interpolation, update the servers.
|
||||
if (fov.interpolate(f) || near.interpolate(f) || far.interpolate(f)) {
|
||||
VisualServer::get_singleton()->camera_set_perspective(camera, fov.interpolated(), near.interpolated(), far.interpolated());
|
||||
}
|
||||
} break;
|
||||
case PROJECTION_ORTHOGONAL: {
|
||||
if (size.interpolate(f) || near.interpolate(f) || far.interpolate(f)) {
|
||||
VisualServer::get_singleton()->camera_set_orthogonal(camera, size.interpolated(), near.interpolated(), far.interpolated());
|
||||
}
|
||||
} break;
|
||||
case PROJECTION_FRUSTUM: {
|
||||
if (size.interpolate(f) || frustum_offset.interpolate(f) || near.interpolate(f) || far.interpolate(f)) {
|
||||
VisualServer::get_singleton()->camera_set_frustum(camera, size.interpolated(), frustum_offset.interpolated(), near.interpolated(), far.interpolated());
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Spatial::fti_update_servers_property();
|
||||
}
|
||||
|
||||
void Camera::fti_update_servers_xform() {
|
||||
if (camera.is_valid()) {
|
||||
Transform tr = _get_adjusted_camera_transform(_get_cached_global_transform_interpolated());
|
||||
VisualServer::get_singleton()->camera_set_transform(camera, tr);
|
||||
}
|
||||
|
||||
Spatial::fti_update_servers_xform();
|
||||
}
|
||||
|
||||
void Camera::_update_camera_mode() {
|
||||
@@ -57,7 +109,6 @@ void Camera::_update_camera_mode() {
|
||||
switch (mode) {
|
||||
case PROJECTION_PERSPECTIVE: {
|
||||
set_perspective(fov, near, far);
|
||||
|
||||
} break;
|
||||
case PROJECTION_ORTHOGONAL: {
|
||||
set_orthogonal(size, near, far);
|
||||
@@ -66,6 +117,8 @@ void Camera::_update_camera_mode() {
|
||||
set_frustum(size, frustum_offset, near, far);
|
||||
} break;
|
||||
}
|
||||
|
||||
fti_notify_node_changed(false);
|
||||
}
|
||||
|
||||
void Camera::_validate_property(PropertyInfo &p_property) const {
|
||||
@@ -812,11 +865,11 @@ ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const {
|
||||
return process_mode;
|
||||
}
|
||||
|
||||
void ClippedCamera::fti_pump() {
|
||||
void ClippedCamera::fti_pump_xform() {
|
||||
_interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr;
|
||||
|
||||
// Must call the base class.
|
||||
Spatial::fti_pump();
|
||||
Camera::fti_pump_xform();
|
||||
}
|
||||
|
||||
void ClippedCamera::_physics_interpolated_changed() {
|
||||
@@ -833,9 +886,9 @@ Transform ClippedCamera::_get_adjusted_camera_transform(const Transform &p_xform
|
||||
return t;
|
||||
}
|
||||
|
||||
void ClippedCamera::fti_update_servers() {
|
||||
void ClippedCamera::fti_update_servers_xform() {
|
||||
clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev;
|
||||
Camera::fti_update_servers();
|
||||
Camera::fti_update_servers_xform();
|
||||
}
|
||||
|
||||
void ClippedCamera::_notification(int p_what) {
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "core/interpolated_property.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/3d/spatial_velocity_tracker.h"
|
||||
#include "scene/main/viewport.h"
|
||||
@@ -66,10 +67,12 @@ private:
|
||||
|
||||
Projection mode;
|
||||
|
||||
float fov;
|
||||
float size;
|
||||
Vector2 frustum_offset;
|
||||
float near, far;
|
||||
InterpolatedProperty<float> fov;
|
||||
InterpolatedProperty<float> near;
|
||||
InterpolatedProperty<float> far;
|
||||
|
||||
InterpolatedProperty<float> size;
|
||||
InterpolatedProperty<Vector2> frustum_offset;
|
||||
float v_offset;
|
||||
float h_offset;
|
||||
KeepAspect keep_aspect;
|
||||
@@ -109,7 +112,10 @@ protected:
|
||||
void _update_camera();
|
||||
virtual void _request_camera_update();
|
||||
void _update_camera_mode();
|
||||
virtual void fti_update_servers();
|
||||
|
||||
virtual void fti_pump_property();
|
||||
virtual void fti_update_servers_property();
|
||||
virtual void fti_update_servers_xform();
|
||||
|
||||
void _notification(int p_what);
|
||||
virtual void _validate_property(PropertyInfo &p_property) const;
|
||||
@@ -230,9 +236,9 @@ private:
|
||||
|
||||
protected:
|
||||
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
|
||||
virtual void fti_pump();
|
||||
virtual void fti_pump_xform();
|
||||
virtual void fti_update_servers_xform();
|
||||
virtual void _physics_interpolated_changed();
|
||||
virtual void fti_update_servers();
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
void _notification(int p_what);
|
||||
|
@@ -181,13 +181,13 @@ void Spatial::_notification(int p_what) {
|
||||
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
// Always reset FTI when entering tree.
|
||||
fti_pump();
|
||||
fti_pump_xform();
|
||||
|
||||
// No need to interpolate as we are doing a reset.
|
||||
data.global_transform_interpolated = get_global_transform();
|
||||
|
||||
// Make sure servers are up to date.
|
||||
fti_update_servers();
|
||||
fti_update_servers_xform();
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
@@ -266,7 +266,14 @@ void Spatial::_notification(int p_what) {
|
||||
if (data.client_physics_interpolation_data) {
|
||||
data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr;
|
||||
}
|
||||
data.local_transform_prev = data.local_transform;
|
||||
|
||||
// In most cases, nodes derived from Spatial will have to
|
||||
// already have reset code available for SceneTreeFTI,
|
||||
// so it makes sense for them to reuse this method
|
||||
// rather than respond individually to NOTIFICATION_RESET_PHYSICS_INTERPOLATION,
|
||||
// unless they need to perform specific tasks (like changing process modes).
|
||||
fti_pump_xform();
|
||||
fti_pump_property();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PAUSED: {
|
||||
@@ -302,7 +309,7 @@ void Spatial::set_global_rotation(const Vector3 &p_euler_rad) {
|
||||
set_global_transform(transform);
|
||||
}
|
||||
|
||||
void Spatial::fti_pump() {
|
||||
void Spatial::fti_pump_xform() {
|
||||
if (data.dirty & DIRTY_LOCAL) {
|
||||
_update_local_transform();
|
||||
}
|
||||
@@ -310,9 +317,9 @@ void Spatial::fti_pump() {
|
||||
data.local_transform_prev = data.local_transform;
|
||||
}
|
||||
|
||||
void Spatial::fti_notify_node_changed() {
|
||||
void Spatial::fti_notify_node_changed(bool p_transform_changed) {
|
||||
if (is_inside_tree()) {
|
||||
get_tree()->get_scene_tree_fti().spatial_notify_set_transform(*this);
|
||||
get_tree()->get_scene_tree_fti().spatial_notify_changed(*this, p_transform_changed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1107,8 +1114,10 @@ Spatial::Spatial() :
|
||||
data.vi_visible = true;
|
||||
data.merging_allowed = true;
|
||||
|
||||
data.fti_on_frame_list = false;
|
||||
data.fti_on_tick_list = false;
|
||||
data.fti_on_frame_xform_list = false;
|
||||
data.fti_on_frame_property_list = false;
|
||||
data.fti_on_tick_xform_list = false;
|
||||
data.fti_on_tick_property_list = false;
|
||||
data.fti_global_xform_interp_set = false;
|
||||
|
||||
data.merging_mode = MERGING_MODE_INHERIT;
|
||||
|
@@ -123,8 +123,10 @@ private:
|
||||
bool disable_scale : 1;
|
||||
|
||||
// Scene tree interpolation
|
||||
bool fti_on_frame_list : 1;
|
||||
bool fti_on_tick_list : 1;
|
||||
bool fti_on_frame_xform_list : 1;
|
||||
bool fti_on_frame_property_list : 1;
|
||||
bool fti_on_tick_xform_list : 1;
|
||||
bool fti_on_tick_property_list : 1;
|
||||
bool fti_global_xform_interp_set : 1;
|
||||
|
||||
bool merging_allowed : 1;
|
||||
@@ -166,18 +168,23 @@ protected:
|
||||
// Calling this announces to the FTI system that a node has been moved,
|
||||
// or requires an update in terms of interpolation
|
||||
// (e.g. changing Camera zoom even if position hasn't changed).
|
||||
void fti_notify_node_changed();
|
||||
void fti_notify_node_changed(bool p_transform_changed = true);
|
||||
|
||||
// Opportunity after FTI to update the servers
|
||||
// with global_transform_interpolated,
|
||||
// and any custom interpolated data in derived classes.
|
||||
// Make sure to call the parent class fti_update_servers(),
|
||||
// so all data is updated to the servers.
|
||||
virtual void fti_update_servers() {}
|
||||
virtual void fti_update_servers_xform() {}
|
||||
virtual void fti_update_servers_property() {}
|
||||
|
||||
// Pump the FTI data, also gives a chance for inherited classes
|
||||
// to pump custom data, but they *must* call the base class here too.
|
||||
virtual void fti_pump();
|
||||
// This is the opportunity for classes to move current values for
|
||||
// transforms and properties to stored previous values,
|
||||
// and this should take place both on ticks, and during resets.
|
||||
virtual void fti_pump_xform();
|
||||
virtual void fti_pump_property() {}
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
@@ -82,7 +82,7 @@ void VisualInstance::set_instance_use_identity_transform(bool p_enable) {
|
||||
}
|
||||
}
|
||||
|
||||
void VisualInstance::fti_update_servers() {
|
||||
void VisualInstance::fti_update_servers_xform() {
|
||||
if (!_is_using_identity_transform()) {
|
||||
VisualServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated());
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ protected:
|
||||
void _update_visibility();
|
||||
virtual void _refresh_portal_mode();
|
||||
void set_instance_use_identity_transform(bool p_enable);
|
||||
virtual void fti_update_servers();
|
||||
virtual void fti_update_servers_xform();
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
@@ -38,12 +38,16 @@
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/3d/visual_instance.h"
|
||||
|
||||
// Uncomment this to enable some slow extra DEV_ENABLED
|
||||
// checks to ensure there aren't more than one object added to the lists.
|
||||
// #define GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
|
||||
void SceneTreeFTI::_reset_flags(Node *p_node) {
|
||||
Spatial *s = Object::cast_to<Spatial>(p_node);
|
||||
|
||||
if (s) {
|
||||
s->data.fti_on_frame_list = false;
|
||||
s->data.fti_on_tick_list = false;
|
||||
s->data.fti_on_frame_xform_list = false;
|
||||
s->data.fti_on_tick_xform_list = false;
|
||||
|
||||
// In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION
|
||||
// will reset this, but this should help cover hidden nodes.
|
||||
@@ -60,8 +64,8 @@ void SceneTreeFTI::set_enabled(Node *p_root, bool p_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.spatial_tick_list[0].clear();
|
||||
data.spatial_tick_list[1].clear();
|
||||
data.tick_xform_list[0].clear();
|
||||
data.tick_xform_list[1].clear();
|
||||
|
||||
// Spatial flags must be reset.
|
||||
if (p_root) {
|
||||
@@ -79,57 +83,125 @@ void SceneTreeFTI::tick_update() {
|
||||
uint32_t curr_mirror = data.mirror;
|
||||
uint32_t prev_mirror = curr_mirror ? 0 : 1;
|
||||
|
||||
LocalVector<Spatial *> &curr = data.spatial_tick_list[curr_mirror];
|
||||
LocalVector<Spatial *> &prev = data.spatial_tick_list[prev_mirror];
|
||||
LocalVector<Spatial *> &curr = data.tick_xform_list[curr_mirror];
|
||||
LocalVector<Spatial *> &prev = data.tick_xform_list[prev_mirror];
|
||||
|
||||
// First detect on the previous list but not on this tick list.
|
||||
for (uint32_t n = 0; n < prev.size(); n++) {
|
||||
Spatial *s = prev[n];
|
||||
if (!s->data.fti_on_tick_list) {
|
||||
if (!s->data.fti_on_tick_xform_list) {
|
||||
// Needs a reset so jittering will stop.
|
||||
s->fti_pump();
|
||||
s->fti_pump_xform();
|
||||
|
||||
// This may not get updated so set it to the same as global xform.
|
||||
// TODO: double check this is the best value.
|
||||
s->data.global_transform_interpolated = s->get_global_transform();
|
||||
|
||||
// Remove from interpolation list.
|
||||
if (s->data.fti_on_frame_list) {
|
||||
s->data.fti_on_frame_list = false;
|
||||
if (s->data.fti_on_frame_xform_list) {
|
||||
s->data.fti_on_frame_xform_list = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalVector<Spatial *> &curr_prop = data.tick_property_list[curr_mirror];
|
||||
LocalVector<Spatial *> &prev_prop = data.tick_property_list[prev_mirror];
|
||||
|
||||
// Detect on the previous property list but not on this tick list.
|
||||
for (uint32_t n = 0; n < prev_prop.size(); n++) {
|
||||
Spatial *s = prev_prop[n];
|
||||
|
||||
if (!s->data.fti_on_tick_property_list) {
|
||||
// Needs a reset so jittering will stop.
|
||||
s->fti_pump_xform();
|
||||
|
||||
// Remove from interpolation list.
|
||||
if (s->data.fti_on_frame_property_list) {
|
||||
s->data.fti_on_frame_property_list = false;
|
||||
data.frame_property_list.erase_unordered(s);
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(s) == -1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pump all on the property list that are NOT on the tick list.
|
||||
for (uint32_t n = 0; n < curr_prop.size(); n++) {
|
||||
Spatial *s = curr_prop[n];
|
||||
|
||||
// Reset, needs to be marked each tick.
|
||||
s->data.fti_on_tick_property_list = false;
|
||||
s->fti_pump_property();
|
||||
}
|
||||
|
||||
// Now pump all on the current list.
|
||||
for (uint32_t n = 0; n < curr.size(); n++) {
|
||||
Spatial *s = curr[n];
|
||||
|
||||
// Reset, needs to be marked each tick.
|
||||
s->data.fti_on_tick_list = false;
|
||||
s->data.fti_on_tick_xform_list = false;
|
||||
|
||||
// Pump.
|
||||
s->fti_pump();
|
||||
s->fti_pump_xform();
|
||||
}
|
||||
|
||||
// Clear previous list and flip.
|
||||
prev.clear();
|
||||
prev_prop.clear();
|
||||
|
||||
data.mirror = prev_mirror;
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_spatial_notify_set_transform(Spatial &r_spatial) {
|
||||
// This may be checked by the calling routine already,
|
||||
// but needs to be double checked for custom SceneTrees.
|
||||
if (!data.enabled || !r_spatial.is_physics_interpolated()) {
|
||||
void SceneTreeFTI::_spatial_notify_set_property(Spatial &r_spatial) {
|
||||
if (!r_spatial.is_physics_interpolated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!r_spatial.data.fti_on_tick_list) {
|
||||
r_spatial.data.fti_on_tick_list = true;
|
||||
data.spatial_tick_list[data.mirror].push_back(&r_spatial);
|
||||
DEV_CHECK_ONCE(data.enabled);
|
||||
|
||||
// Note that a Spatial can be on BOTH the transform list and the property list.
|
||||
if (!r_spatial.data.fti_on_tick_property_list) {
|
||||
r_spatial.data.fti_on_tick_property_list = true;
|
||||
|
||||
// Should only appear once in the property list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.tick_property_list[data.mirror].find(&r_spatial) == -1);
|
||||
#endif
|
||||
data.tick_property_list[data.mirror].push_back(&r_spatial);
|
||||
}
|
||||
|
||||
if (!r_spatial.data.fti_on_frame_list) {
|
||||
r_spatial.data.fti_on_frame_list = true;
|
||||
if (!r_spatial.data.fti_on_frame_property_list) {
|
||||
r_spatial.data.fti_on_frame_property_list = true;
|
||||
|
||||
// Should only appear once in the property frame list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(&r_spatial) == -1);
|
||||
#endif
|
||||
data.frame_property_list.push_back(&r_spatial);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_spatial_notify_set_xform(Spatial &r_spatial) {
|
||||
if (!r_spatial.is_physics_interpolated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEV_CHECK_ONCE(data.enabled);
|
||||
|
||||
if (!r_spatial.data.fti_on_tick_xform_list) {
|
||||
r_spatial.data.fti_on_tick_xform_list = true;
|
||||
|
||||
// Should only appear once in the xform list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[data.mirror].find(&r_spatial) == -1);
|
||||
#endif
|
||||
data.tick_xform_list[data.mirror].push_back(&r_spatial);
|
||||
}
|
||||
|
||||
if (!r_spatial.data.fti_on_frame_xform_list) {
|
||||
r_spatial.data.fti_on_frame_xform_list = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,14 +210,33 @@ void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_spatial->data.fti_on_frame_list) {
|
||||
p_spatial->data.fti_on_frame_list = false;
|
||||
ERR_FAIL_NULL(p_spatial);
|
||||
|
||||
if (p_spatial->data.fti_on_frame_xform_list) {
|
||||
p_spatial->data.fti_on_frame_xform_list = false;
|
||||
}
|
||||
|
||||
// This can potentially be optimized for large scenes with large churn,
|
||||
// as it will be doing a linear search through the lists.
|
||||
data.spatial_tick_list[0].erase_unordered(p_spatial);
|
||||
data.spatial_tick_list[1].erase_unordered(p_spatial);
|
||||
data.tick_xform_list[0].erase_unordered(p_spatial);
|
||||
data.tick_xform_list[1].erase_unordered(p_spatial);
|
||||
|
||||
data.tick_property_list[0].erase_unordered(p_spatial);
|
||||
data.tick_property_list[1].erase_unordered(p_spatial);
|
||||
|
||||
data.frame_property_list.erase_unordered(p_spatial);
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
// There should only be one occurrence on the lists.
|
||||
// Check this in DEV_ENABLED builds.
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[0].find(p_spatial) == -1);
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[1].find(p_spatial) == -1);
|
||||
|
||||
DEV_CHECK_ONCE(data.tick_property_list[0].find(p_spatial) == -1);
|
||||
DEV_CHECK_ONCE(data.tick_property_list[1].find(p_spatial) == -1);
|
||||
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(p_spatial) == -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform, int p_depth) {
|
||||
@@ -176,7 +267,7 @@ void SceneTreeFTI::_update_dirty_spatials(Node *p_node, uint32_t p_current_frame
|
||||
if (!p_active) {
|
||||
if (data.frame_start) {
|
||||
// On the frame start, activate whenever we hit something that requests interpolation.
|
||||
if (s->data.fti_on_frame_list) {
|
||||
if (s->data.fti_on_frame_xform_list) {
|
||||
p_active = true;
|
||||
}
|
||||
} else {
|
||||
@@ -236,7 +327,7 @@ void SceneTreeFTI::_update_dirty_spatials(Node *p_node, uint32_t p_current_frame
|
||||
}
|
||||
|
||||
// Upload to VisualServer the interpolated global xform.
|
||||
s->fti_update_servers();
|
||||
s->fti_update_servers_xform();
|
||||
|
||||
} // if active.
|
||||
|
||||
@@ -283,6 +374,15 @@ void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) {
|
||||
print_line("Took " + itos(after - before) + " usec " + (data.frame_start ? "start" : "end"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the properties once off at the end of the frame.
|
||||
// No need for two passes for properties.
|
||||
if (!p_frame_start) {
|
||||
for (uint32_t n = 0; n < data.frame_property_list.size(); n++) {
|
||||
Spatial *s = data.frame_property_list[n];
|
||||
s->fti_update_servers_property();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ndef _3D_DISABLED
|
||||
|
@@ -47,7 +47,7 @@ public:
|
||||
void set_enabled(Node *p_root, bool p_enabled) {}
|
||||
bool is_enabled() const { return false; }
|
||||
|
||||
void spatial_notify_set_transform(Spatial &r_spatial) {}
|
||||
void spatial_notify_changed(Spatial &r_spatial, bool p_transform_changed) {}
|
||||
void spatial_notify_delete(Spatial *p_spatial) {}
|
||||
};
|
||||
#else
|
||||
@@ -66,7 +66,13 @@ public:
|
||||
class SceneTreeFTI {
|
||||
struct Data {
|
||||
// Prev / Curr lists of spatials having local xforms pumped.
|
||||
LocalVector<Spatial *> spatial_tick_list[2];
|
||||
LocalVector<Spatial *> tick_xform_list[2];
|
||||
|
||||
// Prev / Curr lists of spatials having actively interpolated properties.
|
||||
LocalVector<Spatial *> tick_property_list[2];
|
||||
|
||||
LocalVector<Spatial *> frame_property_list;
|
||||
|
||||
uint32_t mirror = 0;
|
||||
|
||||
bool enabled = false;
|
||||
@@ -82,15 +88,20 @@ class SceneTreeFTI {
|
||||
|
||||
void _update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform = nullptr, int p_depth = 0);
|
||||
void _reset_flags(Node *p_node);
|
||||
void _spatial_notify_set_transform(Spatial &r_spatial);
|
||||
void _spatial_notify_set_xform(Spatial &r_spatial);
|
||||
void _spatial_notify_set_property(Spatial &r_spatial);
|
||||
|
||||
public:
|
||||
// Hottest function, allow inlining the data.enabled check.
|
||||
void spatial_notify_set_transform(Spatial &r_spatial) {
|
||||
void spatial_notify_changed(Spatial &r_spatial, bool p_transform_changed) {
|
||||
if (!data.enabled) {
|
||||
return;
|
||||
}
|
||||
_spatial_notify_set_transform(r_spatial);
|
||||
if (p_transform_changed) {
|
||||
_spatial_notify_set_xform(r_spatial);
|
||||
} else {
|
||||
_spatial_notify_set_property(r_spatial);
|
||||
}
|
||||
}
|
||||
|
||||
void spatial_notify_delete(Spatial *p_spatial);
|
||||
|
Reference in New Issue
Block a user