Merge branch 'Relintai:master' into Relintai-master

This commit is contained in:
Nguyen Truong An 2025-04-01 11:15:48 +02:00 committed by GitHub
commit 48d17312e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 950 additions and 493 deletions

View File

@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
## [Master]
- Backported everything up to and including https://github.com/godotengine/godot/commit/815484b595250f7dca4652f35f55cc269d2bc472 merge commit: https://github.com/godotengine/godot/commit/5a50868b602cd77f0ffffa0405e0abdb400d7abb
- Backported everything up to and including https://github.com/godotengine/godot/commit/31935d6d636ed463f607b3bfeadb604404ec53ca merge commit: https://github.com/godotengine/godot/commit/157f8805c215adc84ac26e14009e02b916336699
## [4.5.0]

View File

@ -194,6 +194,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
for (int i = 0; i < custom_feature_array.size(); i++) {
custom_features.insert(custom_feature_array[i]);
}
_version++;
return true;
}
@ -207,6 +208,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
}
_version++;
return true;
}
bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {

View File

@ -35,6 +35,7 @@
#include "core/containers/rb_set.h"
#include "core/object/object.h"
#include "core/os/thread_safe.h"
#include "core/typedefs.h"
// Querying ProjectSettings is usually done at startup.
// Additionally, in order to keep track of changes to ProjectSettings,
@ -67,6 +68,10 @@ class ProjectSettings : public Object {
int _dirty_this_frame = 2;
// Starting version from 1 ensures that all callers can reset their tested version to 0,
// and will always detect the initial project settings as a "change".
uint32_t _version = 1;
public:
typedef RBMap<String, Variant> CustomMap;
static const String PROJECT_DATA_DIR_NAME_SUFFIX;
@ -198,6 +203,10 @@ public:
// There is therefore the potential for a change to be missed. Persisting the counter
// for two frames avoids this, at the cost of a frame delay.
bool has_changes() const { return _dirty_this_frame == 1; }
// Testing a version allows fast cached GET_GLOBAL macros.
uint32_t get_version() const { return _version; }
void update();
ProjectSettings();
@ -215,4 +224,19 @@ Variant _GLOBAL_DEF_ALIAS(const String &p_var, const String &p_old_name, const V
#define GLOBAL_DEF_ALIAS_RST(m_var, m_old_name, m_value) _GLOBAL_DEF(m_var, m_old_name, m_value, true)
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
#endif
#define GLOBAL_CACHED(m_name, m_type, m_setting_name) \
static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_CACHED must use a trivial type that allows static lifetime."); \
static m_type m_name; \
{ \
static uint32_t local_version = 0; \
static Mutex local_mutex; \
uint32_t new_version = ProjectSettings::get_singleton()->get_version(); \
if (local_version != new_version) { \
local_mutex.lock(); \
local_version = new_version; \
m_name = ProjectSettings::get_singleton()->get(m_setting_name); \
local_mutex.unlock(); \
} \
}
#endif // PROJECT_SETTINGS_H

View File

@ -37,6 +37,7 @@
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/os/safe_refcount.h"
#include "core/typedefs.h"
template <class T>
class Vector;
@ -161,19 +162,26 @@ public:
T *p = ptrw();
int len = size();
for (int i = p_index; i < len - 1; i++) {
p[i] = p[i + 1];
p[i] = MOVE_VAR(p[i + 1]);
};
resize(len - 1);
}
Error insert(int p_pos, const T &p_val) {
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
resize(size() + 1);
for (int i = (size() - 1); i > p_pos; i--) {
set(i, get(i - 1));
int new_size = size() + 1;
ERR_FAIL_INDEX_V(p_pos, new_size , ERR_INVALID_PARAMETER);
Error err = resize(new_size);
ERR_FAIL_COND_V(err, err);
T *p = ptrw();
for (int i = new_size - 1; i > p_pos; i--) {
p[i] = MOVE_VAR(p[i - 1]);
}
set(p_pos, p_val);
p[p_pos] = p_val;
return OK;
}

View File

@ -35,6 +35,7 @@
#include "core/containers/sort_array.h"
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/typedefs.h"
/**
* Generic Templatized Linked List Implementation.
@ -445,6 +446,18 @@ public:
}
}
void operator=(List &&p_list) {
if (unlikely(this == &p_list)) {
return;
}
clear();
_data = p_list._data;
p_list._data = nullptr;
}
T &operator[](int p_index) {
CRASH_BAD_INDEX(p_index, size());
@ -686,6 +699,11 @@ public:
}
}
List(List &&p_list) {
_data = p_list._data;
p_list._data = nullptr;
}
List() {
_data = nullptr;
};

View File

@ -37,6 +37,7 @@
#include "core/containers/vector.h"
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/typedefs.h"
template <class T, class U = uint32_t, bool force_trivial = false>
class LocalVector {
@ -66,9 +67,9 @@ public:
}
if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) {
memnew_placement(&data[count++], T(p_elem));
memnew_placement(&data[count++], T(MOVE_VAR(p_elem)));
} else {
data[count++] = p_elem;
data[count++] = MOVE_VAR(p_elem);
}
}
@ -76,7 +77,7 @@ public:
ERR_FAIL_UNSIGNED_INDEX(p_index, count);
count--;
for (U i = p_index; i < count; i++) {
data[i] = data[i + 1];
data[i] = MOVE_VAR(data[i + 1]);
}
if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) {
data[count].~T();
@ -105,6 +106,15 @@ public:
return false;
}
bool erase_unordered(const T &p_val) {
int64_t idx = find(p_val);
if (idx >= 0) {
remove_unordered(idx);
return true;
}
return false;
}
U erase_multiple_unordered(const T &p_val) {
U from = 0;
U count = 0;
@ -193,13 +203,13 @@ public:
void insert(U p_pos, T p_val) {
ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1);
if (p_pos == count) {
push_back(p_val);
push_back(MOVE_VAR(p_val));
} else {
resize(count + 1);
for (U i = count - 1; i > p_pos; i--) {
data[i] = data[i - 1];
data[i] = MOVE_VAR(data[i - 1]);
}
data[p_pos] = p_val;
data[p_pos] = MOVE_VAR(p_val);
}
}
@ -285,6 +295,16 @@ public:
}
}
LocalVector(LocalVector &&p_from) {
data = p_from.data;
count = p_from.count;
capacity = p_from.capacity;
p_from.data = nullptr;
p_from.count = 0;
p_from.capacity = 0;
}
inline LocalVector &operator=(const LocalVector &p_from) {
resize(p_from.size());
for (U i = 0; i < p_from.count; i++) {
@ -292,6 +312,23 @@ public:
}
return *this;
}
inline void operator=(LocalVector &&p_from) {
if (unlikely(this == &p_from)) {
return;
}
reset();
data = p_from.data;
count = p_from.count;
capacity = p_from.capacity;
p_from.data = nullptr;
p_from.count = 0;
p_from.capacity = 0;
}
inline LocalVector &operator=(const Vector<T> &p_from) {
resize(p_from.size());
for (U i = 0; i < count; i++) {
@ -299,6 +336,15 @@ public:
}
return *this;
}
inline void operator=(Vector<T> &&p_from) {
resize(p_from.size());
for (U i = 0; i < count; i++) {
data[i] = MOVE_VAR(p_from[i]);
}
}
inline LocalVector &operator=(const PoolVector<T> &p_from) {
resize(p_from.size());
typename PoolVector<T>::Read r = p_from.read();

View File

@ -147,7 +147,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
warn_count = warn_max;
warn_timeout = time_now + warn_timeout_seconds;
if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
GLOBAL_CACHED(debug_setting_enable_warnings, bool, "debug/settings/physics_interpolation/enable_warnings");
if (debug_setting_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);

View File

@ -78,15 +78,15 @@ struct ErrorHandlerList {
void add_error_handler(ErrorHandlerList *p_handler);
void remove_error_handler(ErrorHandlerList *p_handler);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
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 char *p_message = "", bool fatal = false);
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();
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
_NO_INLINE_ 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 char *p_message = "", bool fatal = false);
_NO_INLINE_ 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);
_NO_INLINE_ 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);

View File

@ -2043,6 +2043,7 @@ void ObjectDB::remove_instance(Object *p_object) {
rw_lock.write_unlock();
}
Object *ObjectDB::get_instance(ObjectID p_instance_id) {
rw_lock.read_lock();
Object **obj = instances.getptr(p_instance_id);

View File

@ -856,6 +856,12 @@ public:
typedef void (*DebugFunc)(Object *p_obj);
static Object *get_instance(ObjectID p_instance_id);
template <class T>
static T *get_instance(ObjectID p_instance_id) {
return Object::cast_to<T>(get_instance(p_instance_id));
}
static void debug_objects(DebugFunc p_func);
static int get_object_count();

View File

@ -193,6 +193,25 @@ public:
};
void operator=(const StringName &p_name);
StringName &operator=(StringName &&p_name) {
if (_data == p_name._data) {
return *this;
}
unref();
_data = p_name._data;
p_name._data = nullptr;
return *this;
}
StringName(StringName &&p_name) {
_data = p_name._data;
p_name._data = nullptr;
}
StringName(const char *p_name, bool p_static = false);
StringName(const StringName &p_name);
StringName(const String &p_name, bool p_static = false);

View File

@ -70,6 +70,20 @@
#endif
// Should never inline.
#ifndef _NO_INLINE_
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define _NO_INLINE_ __attribute__((noinline))
#elif defined(__llvm__)
#define _NO_INLINE_ __attribute__((noinline))
#elif defined(_MSC_VER)
#define _NO_INLINE_ __declspec(noinline)
#else
#define _NO_INLINE_
#endif
#endif
// No discard allows the compiler to flag warnings if we don't use the return value of functions / classes
#ifndef _NO_DISCARD_
// c++ 17 onwards
@ -174,15 +188,35 @@ T *_nullptr() {
#define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a))
#endif
template<typename T>
struct REMOVE_REFERENCE {
using type = T;
};
template<typename T>
struct REMOVE_REFERENCE<T&> {
using type = T;
};
template<typename T>
struct REMOVE_REFERENCE<T&&> {
using type = T;
};
template<typename T>
typename REMOVE_REFERENCE<T>::type&& MOVE_VAR(T&& t) {
return static_cast<typename REMOVE_REFERENCE<T>::type&&>(t);
}
/** Generic swap template */
#ifndef SWAP
#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y))
template <class T>
inline void __swap_tmpl(T &x, T &y) {
T aux = x;
x = y;
y = aux;
T aux = MOVE_VAR(x);
x = MOVE_VAR(y);
y = MOVE_VAR(aux);
}
#endif //swap

View File

@ -180,7 +180,7 @@ private:
Transform *_transform;
Projection *_projection;
void *_ptr; //generic pointer
uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)];
uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 };
} _data GCC_ALIGNED_8;
void reference(const Variant &p_variant);
@ -485,7 +485,23 @@ public:
static void construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct = nullptr, void *p_construct_ud = nullptr);
void operator=(const Variant &p_variant); // only this is enough for all the other types
void operator=(Variant &&p_variant) {
if (unlikely(this == &p_variant)) {
return;
}
clear();
type = p_variant.type;
_data = p_variant._data;
p_variant.type = NIL;
}
Variant(const Variant &p_variant);
Variant(Variant &&p_variant) {
type = p_variant.type;
_data = p_variant._data;
p_variant.type = NIL;
}
_FORCE_INLINE_ Variant() {
type = NIL;
}

View File

@ -62,6 +62,9 @@
<argument index="0" name="rect" type="Rect2" />
<argument index="1" name="epsilon" type="float" default="2.0" />
<description>
Generates polygon outlines from the opaque (non-transparent) areas of the [BitMap] using a Marching Squares algorithm.
Returns an [Array] of [PoolVector2Array], where each [PoolVector2Array] represents a polygon outline. These outlines can be directly assigned to the [code]polygon[/code] property of nodes like [CollisionPolygon2D] or [OccluderPolygon2D].
The [code]epsilon[/code] parameter controls polygon simplification. A lower value produces more accurate polygons, but at the cost of increased polygon size and potential performance impact. A higher value simplifies the polygons, reducing their size and improving performance, but with less accuracy.
</description>
</method>
<method name="resize">

View File

@ -743,6 +743,12 @@
<member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="" default="0">
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
</member>
<member name="gui/common/drop_mouse_on_gui_input_disabled" type="bool" setter="" getter="" default="false">
If enabled, the moment [member Viewport.gui_disable_input] is set to [code]false[/code] to disable GUI input in a viewport, current mouse over and mouse focus will be dropped.
That behavior helps to keep a robust GUI state, with no surprises when input is resumed regardless what has happened in the meantime.
If disabled, the legacy behavior is used, which consists in just not doing anything besides the GUI input disable itself.
[b]Note:[/b] This is set to [code]true[/code] by default for new projects and is the recommended setting.
</member>
<member name="gui/common/swap_ok_cancel" type="bool" setter="" getter="">
If [code]true[/code], swaps OK and Cancel buttons in dialogs on Windows and UWP to follow interface conventions.
</member>

View File

@ -1476,14 +1476,6 @@
Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance.material_override].
</description>
</method>
<method name="instance_reset_physics_interpolation">
<return type="void" />
<argument index="0" name="instance" type="RID" />
<description>
Prevents physics interpolation for the current physics tick.
This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location.
</description>
</method>
<method name="instance_set_base">
<return type="void" />
<argument index="0" name="instance" type="RID" />
@ -1525,14 +1517,6 @@
Sets a margin to increase the size of the AABB when culling objects from the view frustum. This allows you to avoid culling objects that fall outside the view frustum. Equivalent to [member GeometryInstance.extra_cull_margin].
</description>
</method>
<method name="instance_set_interpolated">
<return type="void" />
<argument index="0" name="instance" type="RID" />
<argument index="1" name="interpolated" type="bool" />
<description>
Turns on and off physics interpolation for the instance.
</description>
</method>
<method name="instance_set_layer_mask">
<return type="void" />
<argument index="0" name="instance" type="RID" />

View File

@ -52,6 +52,7 @@
[b]Note:[/b] The simulation does not take the effect of gears into account, you will need to add logic for this if you wish to simulate gears.
A negative value will result in the wheel reversing.
</member>
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
<member name="steering" type="float" setter="set_steering" getter="get_steering" default="0.0">
The steering angle for the wheel. Setting this to a non-zero value will result in the vehicle turning when it's moving.
</member>

View File

@ -280,7 +280,7 @@ void RasterizerGLES2::begin_frame(double frame_step) {
frame_step = 0.001;
}
double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
GLOBAL_CACHED(time_roll_over, double, "rendering/limits/time/time_rollover_secs");
time_total = Math::fmod(time_total, time_roll_over);
storage->frame.time[0] = time_total;

View File

@ -4059,9 +4059,12 @@ void RasterizerSceneGLES2::initialize() {
}
void RasterizerSceneGLES2::iteration() {
shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode")));
GLOBAL_CACHED(rendering_quality_shadows_filter_mode, int32_t, "rendering/quality/shadows/filter_mode");
GLOBAL_CACHED(rendering_quality_directional_shadow_size, int32_t, "rendering/quality/directional_shadow/size");
const int directional_shadow_size_new = next_power_of_2(int(GLOBAL_GET("rendering/quality/directional_shadow/size")));
shadow_filter_mode = ShadowFilterMode(int(rendering_quality_shadows_filter_mode));
const int directional_shadow_size_new = next_power_of_2(rendering_quality_directional_shadow_size);
if (directional_shadow_size != directional_shadow_size_new) {
directional_shadow_size = directional_shadow_size_new;
directional_shadow_create();

View File

@ -2308,7 +2308,8 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, RS:
}
//bool has_morph = p_blend_shapes.size();
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream");
bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
Surface::Attrib attribs[RS::ARRAY_MAX];

View File

@ -1226,9 +1226,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
//actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
//actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley");
GLOBAL_CACHED(render_force_lambert_over_burley, bool, "rendering/quality/shading/force_lambert_over_burley");
if (!force_lambert) {
if (!render_force_lambert_over_burley) {
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n";
}
@ -1236,9 +1236,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n";
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n";
bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx");
GLOBAL_CACHED(render_force_blinn_over_ggx, bool, "rendering/quality/shading/force_blinn_over_ggx");
if (!force_blinn) {
if (!render_force_blinn_over_ggx) {
actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
} else {
actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n";

View File

@ -178,7 +178,9 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
strings.push_back("#define USE_HIGHP_PRECISION\n");
#endif
if (GLOBAL_GET("rendering/gles2/compatibility/enable_high_float.Android")) {
GLOBAL_CACHED(gles2_compat_enable_high_float_android, bool, "rendering/gles2/compatibility/enable_high_float.Android");
if (gles2_compat_enable_high_float_android) {
// enable USE_HIGHP_PRECISION but safeguarded by an availability check as highp support is optional in GLES2
// see Section 4.5.4 of the GLSL_ES_Specification_1.00
strings.push_back("#ifdef GL_FRAGMENT_PRECISION_HIGH\n #define USE_HIGHP_PRECISION\n#endif\n");

View File

@ -200,7 +200,7 @@ void RasterizerGLES3::begin_frame(double frame_step) {
frame_step = 0.001;
}
double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
GLOBAL_CACHED(time_roll_over, double, "rendering/limits/time/time_rollover_secs");
time_total = Math::fmod(time_total, time_roll_over);
storage->frame.time[0] = time_total;

View File

@ -5315,22 +5315,31 @@ void RasterizerSceneGLES3::initialize() {
}
void RasterizerSceneGLES3::iteration() {
shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode")));
GLOBAL_CACHED(rendering_global_filter_mode, int32_t, "rendering/quality/shadows/filter_mode");
GLOBAL_CACHED(rendering_global_directional_shadow_size, int32_t, "rendering/quality/directional_shadow/size");
GLOBAL_CACHED(rendering_global_subsurface_scattering_follow_surface, bool, "rendering/quality/subsurface_scattering/follow_surface");
GLOBAL_CACHED(rendering_global_subsurface_scattering_weight_samples, bool, "rendering/quality/subsurface_scattering/weight_samples");
GLOBAL_CACHED(rendering_global_subsurface_scattering_quality, int32_t, "rendering/quality/subsurface_scattering/quality");
GLOBAL_CACHED(rendering_global_subsurface_scattering_scale, float, "rendering/quality/subsurface_scattering/scale");
GLOBAL_CACHED(rendering_global_lightmapping_use_bicubic_sampling, bool, "rendering/quality/lightmapping/use_bicubic_sampling");
GLOBAL_CACHED(rendering_global_voxel_cone_tracinghigh_quality, bool, "rendering/quality/voxel_cone_tracing/high_quality");
const int directional_shadow_size_new = next_power_of_2(int(GLOBAL_GET("rendering/quality/directional_shadow/size")));
shadow_filter_mode = ShadowFilterMode(rendering_global_filter_mode);
const int directional_shadow_size_new = next_power_of_2(rendering_global_directional_shadow_size);
if (directional_shadow_size != directional_shadow_size_new) {
directional_shadow_size = directional_shadow_size_new;
directional_shadow_create();
}
subsurface_scatter_follow_surface = GLOBAL_GET("rendering/quality/subsurface_scattering/follow_surface");
subsurface_scatter_weight_samples = GLOBAL_GET("rendering/quality/subsurface_scattering/weight_samples");
subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/quality")));
subsurface_scatter_size = GLOBAL_GET("rendering/quality/subsurface_scattering/scale");
subsurface_scatter_follow_surface = rendering_global_subsurface_scattering_follow_surface;
subsurface_scatter_weight_samples = rendering_global_subsurface_scattering_weight_samples;
subsurface_scatter_quality = SubSurfaceScatterQuality(int(rendering_global_subsurface_scattering_quality));
subsurface_scatter_size = rendering_global_subsurface_scattering_scale;
storage->config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling");
storage->config.use_lightmap_filter_bicubic = rendering_global_lightmapping_use_bicubic_sampling;
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic);
state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET("rendering/quality/voxel_cone_tracing/high_quality"));
state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, rendering_global_voxel_cone_tracinghigh_quality);
}
void RasterizerSceneGLES3::finalize() {

View File

@ -3390,7 +3390,8 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, RS:
}
//bool has_morph = p_blend_shapes.size();
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(global_storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream")
bool use_split_stream = global_storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
Surface::Attrib attribs[RS::ARRAY_MAX];

View File

@ -1268,9 +1268,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley");
GLOBAL_CACHED(render_force_lambert_over_burley, bool, "rendering/quality/shading/force_lambert_over_burley");
if (!force_lambert) {
if (!render_force_lambert_over_burley) {
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n";
}
@ -1278,9 +1278,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n";
actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n";
bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx");
GLOBAL_CACHED(render_force_blinn_over_ggx, bool, "rendering/quality/shading/force_blinn_over_ggx");
if (!force_blinn) {
if (!render_force_blinn_over_ggx) {
actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
} else {
actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n";

View File

@ -418,13 +418,14 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
if (repeat > 0) {
tex_flags |= Texture::FLAG_REPEAT;
const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" &&
!GLOBAL_GET("rendering/quality/driver/fallback_to_gles2");
GLOBAL_CACHED(global_fallback_to_gles2, bool, "rendering/quality/driver/fallback_to_gles2");
const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" && !global_fallback_to_gles2;
if (!min_gles3 && !image->is_size_po2()) {
// The project can be run using GLES2. GLES2 does not guarantee that
// repeating textures with a non-power-of-two size will be displayed
// without artifacts (due to upscaling to the nearest power of 2).
if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) {
if (global_fallback_to_gles2) {
WARN_PRINT(vformat("%s: Imported a repeating texture with a size of %dx%d, but the project is configured to allow falling back to GLES2.\nNon-power-of-2 repeating textures may not display correctly on some platforms such as HTML5. This is because GLES2 does not mandate support for non-power-of-2 repeating textures.",
p_source_file, image->get_width(), image->get_height()));
} else {

View File

@ -1475,7 +1475,10 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
// Render every past/future step with the capture shader.
RS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid());
onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color"));
GLOBAL_CACHED(global_environment_default_clear_color, Color, "rendering/environment/default_clear_color");
onion.capture.material->set_shader_param("bkg_color", global_environment_default_clear_color);
onion.capture.material->set_shader_param("differences_only", onion.differences_only);
onion.capture.material->set_shader_param("present", onion.differences_only ? RS::get_singleton()->viewport_get_texture(present_rid) : RID());

View File

@ -3677,7 +3677,9 @@ void CanvasItemEditor::set_current_tool(Tool p_tool) {
void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels"));
GLOBAL_CACHED(global_gui_common_snap_controls_to_pixels, bool, "gui/common/snap_controls_to_pixels")
EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(global_gui_common_snap_controls_to_pixels);
bool has_container_parents = false;
int nb_control = 0;

View File

@ -894,7 +894,8 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c
Ref<Font> font = sampled_font;
const Color c = GLOBAL_GET("rendering/environment/default_clear_color");
GLOBAL_CACHED(global_default_clear_color, Color, "rendering/environment/default_clear_color");
const Color c = global_default_clear_color;
const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0;
font->draw(canvas_item, pos, sampled_text, Color(fg, fg, fg));

View File

@ -96,7 +96,9 @@ void ThemeEditorPreview::_propagate_redraw(Control *p_at) {
void ThemeEditorPreview::_refresh_interval() {
// In case the project settings have changed.
preview_bg->set_frame_color(GLOBAL_GET("rendering/environment/default_clear_color"));
GLOBAL_CACHED(rendering_default_clear_color, Color, "rendering/environment/default_clear_color");
preview_bg->set_frame_color(rendering_default_clear_color);
_propagate_redraw(preview_bg);
_propagate_redraw(preview_content);

View File

@ -512,6 +512,7 @@ private:
initial_settings["application/config/icon"] = "res://icon.png";
initial_settings["rendering/environment/default_environment"] = "res://default_env.tres";
initial_settings["physics/common/enable_pause_aware_picking"] = true;
initial_settings["gui/common/drop_mouse_on_gui_input_disabled"] = true;
if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("project.pandemonium"), initial_settings, Vector<String>(), false) != OK) {
set_message(TTR("Couldn't create project.pandemonium in project path."), MESSAGE_ERROR);

View File

@ -446,7 +446,9 @@ void EditorScriptTextEditor::_validate_script() {
warnings_panel->clear();
// Add missing connections.
if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
GLOBAL_CACHED(global_debug_warning_enable, bool, "debug/gdscript/warnings/enable")
if (global_debug_warning_enable) {
Node *base = get_tree()->get_edited_scene_root();
if (base && missing_connections.size() > 0) {
warnings_panel->push_table(1);

View File

@ -1359,6 +1359,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,1000,1"));
GLOBAL_DEF("physics/common/enable_pause_aware_picking", false);
GLOBAL_DEF("gui/common/drop_mouse_on_gui_input_disabled", false);
GLOBAL_DEF("debug/settings/stdout/print_fps", false);
GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
@ -2537,6 +2538,8 @@ bool Main::iteration() {
frames++;
Engine::get_singleton()->_idle_frames++;
GLOBAL_CACHED(debug_settings_stdout_print_pfs, bool, "debug/settings/stdout/print_fps");
if (frame > 1000000) {
// Wait a few seconds before printing FPS, as FPS reporting just after the engine has started is inaccurate.
if (hide_print_fps_attempts == 0) {
@ -2544,7 +2547,7 @@ bool Main::iteration() {
if (print_fps) {
print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else if (print_fps || GLOBAL_GET("debug/settings/stdout/print_fps")) {
} else if (print_fps || debug_settings_stdout_print_pfs) {
print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else {

View File

@ -39,3 +39,4 @@ MainLoop *test();
}
#endif // TEST_EXPRESSION_H

View File

@ -2091,7 +2091,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context
if (!_static) {
List<MethodInfo> methods;
bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
GLOBAL_CACHED(is_autocompleting_getters, bool, "debug/gdscript/completion/autocomplete_setters_and_getters");
ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name.begins_with("_")) {

View File

@ -8703,13 +8703,20 @@ void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol
}
void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && base_path.begins_with("res://addons/")) {
GLOBAL_CACHED(global_warnings_exclude_addons, bool, "debug/gdscript/warnings/exclude_addons")
if (global_warnings_exclude_addons && base_path.begins_with("res://addons/")) {
return;
}
if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
GLOBAL_CACHED(global_warnings_enable, bool, "debug/gdscript/warnings/enable");
if (tokenizer->is_ignoring_warnings() || !global_warnings_enable) {
return;
}
String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
if (tokenizer->get_warning_global_skips().has(warn_name)) {
return;
}
@ -8820,7 +8827,9 @@ Error GDScriptParser::_parse(const String &p_base_path) {
// Resolve warning ignores
Vector<Pair<int, String>> warning_skips = tokenizer->get_warning_skips();
bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize();
GLOBAL_CACHED(global_treat_warnings_as_errors, bool, "debug/gdscript/warnings/treat_warnings_as_errors");
bool warning_is_error = global_treat_warnings_as_errors;
for (List<GDScriptWarning>::Element *E = warnings.front(); E;) {
GDScriptWarning &w = E->get();
int skip_index = -1;

View File

@ -77,6 +77,7 @@
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="&quot;&quot;">
The name of the attached bone.
</member>
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
</members>
<constants>
</constants>

View File

@ -187,6 +187,7 @@ String BoneAttachment::get_configuration_warning() const {
}
BoneAttachment::BoneAttachment() {
set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF);
bound = false;
bone_idx = -1;
override_pose = false;

View File

@ -279,7 +279,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, false);
Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin;
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
make_goal(p_task, p_task->skeleton->get_global_transform_interpolated().affine_inverse(), blending_delta);
if (p_use_magnet && p_task->chain.middle_chain_item) {
p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta);

View File

@ -215,6 +215,9 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
String orientation = _get_android_orientation_label(
OS::get_singleton()->get_screen_orientation_from_string(GLOBAL_GET("display/window/handheld/orientation")));
GLOBAL_CACHED(display_window_resizable, bool, "display/window/size/resizable");
String manifest_activity_text = vformat(
" <activity android:name=\"com.pandemonium.game.PandemoniumApp\" "
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
@ -223,7 +226,7 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
"android:resizeableActivity=\"%s\">\n",
bool_to_string(p_preset->get("package/exclude_from_recents")),
orientation,
bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
bool_to_string(display_window_resizable));
manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n";
manifest_activity_text += " </activity>\n";
return manifest_activity_text;

View File

@ -297,6 +297,7 @@ void Camera2D::_notification(int p_what) {
// Force the limits etc to update.
_interpolation_data.xform_curr = get_camera_transform();
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
_update_process_mode();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (!smoothing_active && !is_physics_interpolated_and_enabled()) {

View File

@ -50,6 +50,13 @@ void Camera::_request_camera_update() {
_update_camera();
}
void Camera::fti_update_servers() {
if (camera.is_valid()) {
Transform tr = _get_adjusted_camera_transform(_get_cached_global_transform_interpolated());
RenderingServer::get_singleton()->camera_set_transform(camera, tr);
}
}
void Camera::_update_camera_mode() {
force_change = true;
switch (mode) {
@ -90,12 +97,8 @@ void Camera::_update_camera() {
if (!is_physics_interpolated_and_enabled()) {
RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform());
} else {
// Ideally we shouldn't be moving a physics interpolated camera within a frame,
// because it will break smooth interpolation, but it may occur on e.g. level load.
if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) {
_physics_interpolation_ensure_transform_calculated(true);
RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated);
}
// Force a refresh next frame.
fti_notify_node_changed();
}
// here goes listener stuff
@ -119,40 +122,6 @@ void Camera::_physics_interpolated_changed() {
_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;
@ -160,17 +129,8 @@ void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_p
}
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);
set_process_internal(_desired_process_internal);
set_physics_process_internal(_desired_physics_process_internal);
}
void Camera::_notification(int p_what) {
@ -187,49 +147,21 @@ void Camera::_notification(int p_what) {
world->_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
RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated);
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (is_physics_interpolated_and_enabled()) {
_physics_interpolation_ensure_data_flipped();
_interpolation_data.xform_curr = get_global_transform();
}
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (is_physics_interpolated_and_enabled()) {
_physics_interpolation_ensure_data_flipped();
_interpolation_data.xform_curr = get_global_transform();
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (is_physics_interpolated_and_enabled()) {
if (!Engine::get_singleton()->is_in_physics_frame()) {
PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera triggered from outside physics process");
}
#endif
}
#endif
_request_camera_update();
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
velocity_tracker->update_position(get_global_transform().origin);
}
// Allow auto-reset when first adding to the tree, as a convenience.
if (_is_physics_interpolation_reset_requested() && is_inside_tree()) {
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
_set_physics_interpolation_reset_requested(false);
}
} break;
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
if (is_inside_tree()) {
_interpolation_data.xform_curr = get_global_transform();
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
}
_update_process_mode();
} break;
case NOTIFICATION_EXIT_WORLD: {
if (!get_tree()->is_node_being_edited(this)) {
@ -272,8 +204,7 @@ Transform Camera::_get_adjusted_camera_transform(const Transform &p_xform) const
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_cached_global_transform_interpolated());
}
return _get_adjusted_camera_transform(get_global_transform());
@ -863,9 +794,16 @@ 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 (is_physics_interpolated_and_enabled()) {
if (p_mode == CLIP_PROCESS_IDLE) {
p_mode = CLIP_PROCESS_PHYSICS;
WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode.");
}
process_mode = p_mode;
set_desired_process_modes(false, true);
return;
}
if (process_mode == p_mode) {
@ -879,8 +817,11 @@ ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const {
return process_mode;
}
void ClippedCamera::physics_interpolation_flip_data() {
void ClippedCamera::fti_pump() {
_interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr;
// Must call the base class.
Spatial::fti_pump();
}
void ClippedCamera::_physics_interpolated_changed() {
@ -897,6 +838,11 @@ Transform ClippedCamera::_get_adjusted_camera_transform(const Transform &p_xform
return t;
}
void ClippedCamera::fti_update_servers() {
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();
}
void ClippedCamera::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
// Switch process mode to physics if we are turning on interpolation.
@ -964,10 +910,6 @@ void ClippedCamera::_notification(int p_what) {
_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_gizmos();
}

View File

@ -96,26 +96,10 @@ private:
Ref<SpatialVelocityTracker> 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).
// These can be set by derived Cameras.
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:
@ -123,9 +107,6 @@ protected:
// 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;
///////////////////////////////////////////////////////
@ -133,6 +114,7 @@ protected:
void _update_camera();
virtual void _request_camera_update();
void _update_camera_mode();
virtual void fti_update_servers();
void _notification(int p_what);
virtual void _validate_property(PropertyInfo &p_property) const;
@ -253,8 +235,9 @@ private:
protected:
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
virtual void physics_interpolation_flip_data();
virtual void fti_pump();
virtual void _physics_interpolated_changed();
virtual void fti_update_servers();
///////////////////////////////////////////////////////
void _notification(int p_what);

View File

@ -202,12 +202,14 @@ void MeshInstance::_resolve_skeleton_path() {
bool MeshInstance::_is_global_software_skinning_enabled() {
// Check if forced in project settings.
if (GLOBAL_GET("rendering/quality/skinning/force_software_skinning")) {
GLOBAL_CACHED(force_software_skinning, bool, "rendering/quality/skinning/force_software_skinning");
if (force_software_skinning) {
return true;
}
// Check if enabled in project settings.
if (!GLOBAL_GET("rendering/quality/skinning/software_skinning_fallback")) {
GLOBAL_CACHED(software_skinning_fallback, bool, "rendering/quality/skinning/software_skinning_fallback");
if (!software_skinning_fallback) {
return false;
}

View File

@ -47,7 +47,7 @@ public:
real_t getDiagonal() const { return m_Adiag; }
btVehicleJacobianEntry() {};
btVehicleJacobianEntry() {}
//constraint between two different rigidbodies
btVehicleJacobianEntry(
const Basis &world2A,
@ -369,6 +369,8 @@ VehicleWheel::VehicleWheel() {
m_raycastInfo.m_suspensionLength = 0.0;
body = nullptr;
set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF);
}
void VehicleBody::_update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s) {

View File

@ -85,6 +85,12 @@ void VisualInstance::set_instance_use_identity_transform(bool p_enable) {
}
}
void VisualInstance::fti_update_servers() {
if (!_is_using_identity_transform()) {
RenderingServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated());
}
}
void VisualInstance::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
@ -100,33 +106,26 @@ void VisualInstance::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (_is_vi_visible() || is_physics_interpolated_and_enabled()) {
if (_is_vi_visible()) {
if (!_is_using_identity_transform()) {
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
// For instance when first adding to the tree, when the previous transform is
// unset, to prevent streaking from the origin.
if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) {
if (_is_vi_visible()) {
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
// Physics interpolated VIs don't need to send their transform immediately after setting,
// indeed it is counterproductive, because the interpolated transform will be sent
// to the VisualServer immediately prior to rendering.
if (!is_physics_interpolated_and_enabled()) {
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
} else {
// For instance when first adding to the tree, when the previous transform is
// unset, to prevent streaking from the origin.
if (_is_physics_interpolation_reset_requested() && is_inside_tree()) {
if (_is_vi_visible()) {
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
}
_set_physics_interpolation_reset_requested(false);
}
_set_physics_interpolation_reset_requested(false);
}
}
}
} break;
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) {
// We must ensure the 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()) {
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
}
RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance);
}
} break;
case NOTIFICATION_EXIT_WORLD: {
RenderingServer::get_singleton()->instance_set_scenario(instance, RID());
RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID());
@ -142,10 +141,6 @@ void VisualInstance::_notification(int p_what) {
}
}
void VisualInstance::_physics_interpolated_changed() {
RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated());
}
RID VisualInstance::get_instance() const {
return instance;
}

View File

@ -55,8 +55,8 @@ class VisualInstance : public CullInstance {
protected:
void _update_visibility();
virtual void _refresh_portal_mode();
virtual void _physics_interpolated_changed();
void set_instance_use_identity_transform(bool p_enable);
virtual void fti_update_servers();
void _notification(int p_what);
static void _bind_methods();

View File

@ -1200,13 +1200,12 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) {
} break;
}
// if swapping from interpolated to non-interpolated, use this as
// an extra means to cause a reset
if (is_physics_interpolated() && !interpolate && is_inside_tree()) {
_propagate_physics_interpolated(interpolate);
// Auto-reset on changing interpolation mode.
if (is_physics_interpolated() && is_inside_tree()) {
propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
}
_propagate_physics_interpolated(interpolate);
}
void Node::reset_physics_interpolation() {

View File

@ -591,6 +591,13 @@ void SceneTree::set_physics_interpolation_enabled(bool p_enabled) {
_physics_interpolation_enabled = p_enabled;
RenderingServer::get_singleton()->set_physics_interpolation_enabled(p_enabled);
get_scene_tree_fti().set_enabled(get_root(), p_enabled);
// Perform an auto reset on the root node for convenience for the user.
if (root) {
root->reset_physics_interpolation();
}
}
bool SceneTree::is_physics_interpolation_enabled() const {
@ -612,12 +619,8 @@ void SceneTree::iteration_prepare() {
// Make sure any pending transforms from the last tick / frame
// are flushed before pumping the interpolation prev and currents.
flush_transform_notifications();
get_scene_tree_fti().tick_update();
RenderingServer::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();
}
}
@ -626,6 +629,11 @@ void SceneTree::iteration_end() {
// to be flushed to the RenderingServer before finishing a physics tick.
if (_physics_interpolation_enabled) {
flush_transform_notifications();
// Any objects performing client physics interpolation
// should be given an opportunity to keep their previous transforms
// up to date.
_client_physics_interpolation.physics_process();
}
}
@ -647,7 +655,8 @@ bool SceneTree::iteration(float p_time) {
call_group_flags(GROUP_CALL_REALTIME, "_pg_process", "trigger_physics_process");
_notify_group_pause("physics_process_internal", Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
if (GLOBAL_GET("physics/common/enable_pause_aware_picking")) {
GLOBAL_CACHED(global_enable_pause_aware_picking, bool, "physics/common/enable_pause_aware_picking");
if (global_enable_pause_aware_picking) {
call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_process_picking", true);
}
_notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS);
@ -684,6 +693,17 @@ bool SceneTree::idle(float p_time) {
//print_line("node count: "+itos(get_node_count()));
//print_line("TEXTURE RAM: "+itos(RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED)));
// First pass of scene tree fixed timestep interpolation.
if (get_scene_tree_fti().is_enabled()) {
// Special, we need to ensure RenderingServer is up to date
// with *all* the pending xforms *before* updating it during
// the FTI update.
// If this is not done, we can end up with a deferred `set_transform()`
// overwriting the interpolated xform in the server.
flush_transform_notifications();
get_scene_tree_fti().frame_update(get_root(), true);
}
root_lock++;
if (MainLoop::idle(p_time)) {
@ -800,6 +820,11 @@ bool SceneTree::idle(float p_time) {
#endif
// Second pass of scene tree fixed timestep interpolation.
// ToDo: Possibly needs another flush_transform_notifications here
// depending on whether there are side effects to _call_idle_callbacks().
get_scene_tree_fti().frame_update(get_root(), false);
RenderingServer::get_singleton()->pre_draw(true);
return _quit;

View File

@ -36,6 +36,7 @@
#include "core/io/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "scene/main/scene_tree_fti.h"
class PackedScene;
class Node;
@ -160,6 +161,7 @@ private:
StretchAspect stretch_aspect;
Size2i stretch_min;
real_t stretch_scale;
SceneTreeFTI scene_tree_fti;
void _update_font_oversampling(float p_ratio);
void _update_root_rect();
@ -468,6 +470,8 @@ public:
void client_physics_interpolation_add_spatial(SelfList<Spatial> *p_elem);
void client_physics_interpolation_remove_spatial(SelfList<Spatial> *p_elem);
SceneTreeFTI &get_scene_tree_fti() { return scene_tree_fti; }
static void add_idle_callback(IdleCallback p_callback);
SceneTree();
~SceneTree();

View File

@ -0,0 +1,288 @@
/**************************************************************************/
/* scene_tree_fti.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. */
/**************************************************************************/
#ifndef _3D_DISABLED
#include "scene_tree_fti.h"
#include "core/config/engine.h"
#include "core/error/error_macros.h"
#include "core/math/transform_interpolator.h"
#include "core/os/os.h"
#include "scene/main/spatial.h"
#include "scene/3d/visual_instance.h"
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;
// In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION
// will reset this, but this should help cover hidden nodes.
s->data.local_transform_prev = s->data.local_transform;
}
for (int n = 0; n < p_node->get_child_count(); n++) {
_reset_flags(p_node->get_child(n));
}
}
void SceneTreeFTI::set_enabled(Node *p_root, bool p_enabled) {
if (data.enabled == p_enabled) {
return;
}
data.spatial_tick_list[0].clear();
data.spatial_tick_list[1].clear();
// Spatial flags must be reset.
if (p_root) {
_reset_flags(p_root);
}
data.enabled = p_enabled;
}
void SceneTreeFTI::tick_update() {
if (!data.enabled) {
return;
}
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];
// 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) {
// Needs a reset so jittering will stop.
s->fti_pump();
// 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;
}
}
}
// 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;
// Pump.
s->fti_pump();
}
// Clear previous list and flip.
prev.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()) {
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);
}
if (!r_spatial.data.fti_on_frame_list) {
r_spatial.data.fti_on_frame_list = true;
}
}
void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) {
if (!data.enabled) {
return;
}
if (p_spatial->data.fti_on_frame_list) {
p_spatial->data.fti_on_frame_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);
}
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) {
Spatial *s = Object::cast_to<Spatial>(p_node);
// Don't recurse into hidden branches.
if (s && !s->is_visible()) {
// NOTE : If we change from recursing entire tree, we should do an is_visible_in_tree()
// check for the first of the branch.
return;
}
// Not a Spatial.
// Could be e.g. a viewport or something
// so we should still recurse to children.
if (!s) {
for (int n = 0; n < p_node->get_child_count(); n++) {
_update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
}
return;
}
// Start the active interpolation chain from here onwards
// as we recurse further into the SceneTree.
// Once we hit an active (interpolated) node, we have to fully
// process all ancestors because their xform will also change.
// Anything not moving (inactive) higher in the tree need not be processed.
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) {
p_active = true;
}
} else {
// On the frame end, we want to re-interpolate *anything* that has moved
// since the frame start.
if (s->data.dirty & Spatial::DIRTY_GLOBAL_INTERPOLATED) {
p_active = true;
}
}
}
if (data.frame_start) {
// Mark on the Spatial whether we have set global_transform_interp.
// This can later be used when calling `get_global_transform_interpolated()`
// to know which xform to return.
s->data.fti_global_xform_interp_set = p_active;
}
if (p_active) {
#if 0
bool dirty = s->data.dirty & Spatial::DIRTY_GLOBAL_INTERP;
if (data.debug) {
String sz;
for (int n = 0; n < p_depth; n++) {
sz += "\t";
}
print_line(sz + p_node->get_name() + (dirty ? " DIRTY" : ""));
}
#endif
// First calculate our local xform.
// This will either use interpolation, or just use the current local if not interpolated.
Transform local_interp;
if (s->is_physics_interpolated()) {
TransformInterpolator::interpolate_transform(s->data.local_transform_prev, s->data.local_transform, local_interp, p_interpolation_fraction);
} else {
local_interp = s->data.local_transform;
}
// Concatenate parent xform.
if (!s->is_set_as_toplevel()) {
if (p_parent_global_xform) {
s->data.global_transform_interpolated = (*p_parent_global_xform) * local_interp;
} else {
const Spatial *parent = s->get_parent_spatial();
if (parent) {
const Transform &parent_glob = parent->data.fti_global_xform_interp_set ? parent->data.global_transform_interpolated : parent->data.global_transform;
s->data.global_transform_interpolated = parent_glob * local_interp;
} else {
s->data.global_transform_interpolated = local_interp;
}
}
} else {
s->data.global_transform_interpolated = local_interp;
}
// Upload to VisualServer the interpolated global xform.
s->fti_update_servers();
} // if active.
// Remove the dirty interp flag from EVERYTHING as we go.
s->data.dirty &= ~Spatial::DIRTY_GLOBAL_INTERPOLATED;
// Recurse to children.
for (int n = 0; n < p_node->get_child_count(); n++) {
_update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1);
}
}
void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) {
if (!data.enabled || !p_root) {
return;
}
data.frame_start = p_frame_start;
float f = Engine::get_singleton()->get_physics_interpolation_fraction();
uint32_t frame = Engine::get_singleton()->get_frames_drawn();
// #define SCENE_TREE_FTI_TAKE_TIMINGS
#ifdef SCENE_TREE_FTI_TAKE_TIMINGS
uint64_t before = OS::get_singleton()->get_ticks_usec();
#endif
if (data.debug) {
print_line(String("\nScene: ") + (data.frame_start ? "start" : "end") + "\n");
}
// Probably not the most optimal approach as we traverse the entire SceneTree
// but simple and foolproof.
// Can be optimized later.
_update_dirty_spatials(p_root, frame, f, false);
if (!p_frame_start && data.debug) {
data.debug = false;
}
#ifdef SCENE_TREE_FTI_TAKE_TIMINGS
uint64_t after = OS::get_singleton()->get_ticks_usec();
if ((Engine::get_singleton()->get_frames_drawn() % 60) == 0) {
print_line("Took " + itos(after - before) + " usec " + (data.frame_start ? "start" : "end"));
}
#endif
}
#endif // ndef _3D_DISABLED

112
scene/main/scene_tree_fti.h Normal file
View File

@ -0,0 +1,112 @@
/**************************************************************************/
/* scene_tree_fti.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 SCENE_TREE_FTI_H
#define SCENE_TREE_FTI_H
#include "core/containers/local_vector.h"
#include "core/os/mutex.h"
class Spatial;
class Node;
struct Transform;
#ifdef _3D_DISABLED
// Stubs
class SceneTreeFTI {
public:
void frame_update(Node *p_root, bool p_frame_start) {}
void tick_update() {}
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_delete(Spatial *p_spatial) {}
};
#else
// Important.
// This class uses raw pointers, so it is essential that on deletion, this class is notified
// so that any references can be cleared up to prevent dangling pointer access.
// This class can be used from a custom SceneTree.
// Note we could potentially make SceneTreeFTI static / global to avoid the lookup through scene tree,
// but this covers the custom case of multiple scene trees.
// This class is not thread safe, but can be made thread safe easily with a mutex as in the 4.x version.
class SceneTreeFTI {
struct Data {
// Prev / Curr lists of spatials having local xforms pumped.
LocalVector<Spatial *> spatial_tick_list[2];
uint32_t mirror = 0;
bool enabled = false;
// Whether we are in physics ticks, or in a frame.
bool in_frame = false;
// Updating at the start of the frame, or the end on second pass.
bool frame_start = true;
bool debug = false;
} data;
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);
public:
// Hottest function, allow inlining the data.enabled check.
void spatial_notify_set_transform(Spatial &r_spatial) {
if (!data.enabled) {
return;
}
_spatial_notify_set_transform(r_spatial);
}
void spatial_notify_delete(Spatial *p_spatial);
// Calculate interpolated xforms, send to visual server.
void frame_update(Node *p_root, bool p_frame_start);
// Update local xform pumps.
void tick_update();
void set_enabled(Node *p_root, bool p_enabled);
bool is_enabled() const { return data.enabled; }
void set_debug_next_frame() { data.debug = true; }
};
#endif // ndef _3D_DISABLED
#endif // SCENE_TREE_FTI_H

View File

@ -118,7 +118,7 @@ void Spatial::_propagate_transform_changed(Spatial *p_origin) {
#endif
get_tree()->xform_change_list.add(&xform_change);
}
data.dirty |= DIRTY_GLOBAL;
data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED;
data.children_lock--;
}
@ -174,13 +174,27 @@ void Spatial::_notification(int p_what) {
_propagate_merging_allowed(merging_allowed);
}
data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene
data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED; //global is always dirty upon entering a scene
_notify_dirty();
notification(NOTIFICATION_ENTER_WORLD);
if (is_physics_interpolated_and_enabled()) {
// Always reset FTI when entering tree.
fti_pump();
// 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();
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (is_inside_tree()) {
get_tree()->get_scene_tree_fti().spatial_notify_delete(this);
}
notification(NOTIFICATION_EXIT_WORLD, true);
if (xform_change.in_list()) {
get_tree()->xform_change_list.remove(&xform_change);
@ -241,6 +255,13 @@ 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;
} break;
case NOTIFICATION_PAUSED: {
if (is_physics_interpolated_and_enabled()) {
data.local_transform_prev = data.local_transform;
}
} break;
default: {
@ -270,7 +291,22 @@ void Spatial::set_global_rotation(const Vector3 &p_euler_rad) {
set_global_transform(transform);
}
void Spatial::fti_pump() {
if (data.dirty & DIRTY_LOCAL) {
_update_local_transform();
}
data.local_transform_prev = data.local_transform;
}
void Spatial::fti_notify_node_changed() {
if (is_inside_tree()) {
get_tree()->get_scene_tree_fti().spatial_notify_set_transform(*this);
}
}
void Spatial::set_transform(const Transform &p_transform) {
fti_notify_node_changed();
data.local_transform = p_transform;
data.dirty |= DIRTY_VECTORS;
data.dirty &= ~DIRTY_LOCAL;
@ -393,14 +429,19 @@ Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fra
}
Transform Spatial::get_global_transform_interpolated() {
#if 1
// Pass through if physics interpolation is switched off.
// This is a convenience, as it allows you to easy turn off interpolation
// without changing any code.
if (!is_physics_interpolated_and_enabled()) {
return get_global_transform();
if (data.fti_global_xform_interp_set && is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame() && is_visible_in_tree()) {
return data.global_transform_interpolated;
}
// If we are in the physics frame, the interpolated global transform is meaningless.
return get_global_transform();
#else
// OLD METHOD - deprecated since moving to SceneTreeFTI,
// but leaving for reference and comparison for debugging.
// 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()) {
@ -408,6 +449,7 @@ Transform Spatial::get_global_transform_interpolated() {
}
return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction());
#endif
}
Transform Spatial::get_global_transform() const {
@ -473,6 +515,7 @@ Transform Spatial::get_relative_transform(const Node *p_parent) const {
}
void Spatial::set_translation(const Vector3 &p_translation) {
fti_notify_node_changed();
data.local_transform.origin = p_translation;
_change_notify("transform");
_propagate_transform_changed(this);
@ -482,6 +525,7 @@ void Spatial::set_translation(const Vector3 &p_translation) {
}
void Spatial::set_rotation(const Vector3 &p_euler_rad) {
fti_notify_node_changed();
if (data.dirty & DIRTY_VECTORS) {
data.scale = data.local_transform.basis.get_scale();
data.dirty &= ~DIRTY_VECTORS;
@ -501,6 +545,7 @@ void Spatial::set_rotation_degrees(const Vector3 &p_euler_deg) {
}
void Spatial::set_scale(const Vector3 &p_scale) {
fti_notify_node_changed();
if (data.dirty & DIRTY_VECTORS) {
data.rotation = data.local_transform.basis.get_rotation();
data.dirty &= ~DIRTY_VECTORS;
@ -1106,6 +1151,11 @@ Spatial::Spatial() :
data.disable_scale = false;
data.vi_visible = true;
data.merging_allowed = true;
data.fti_on_frame_list = false;
data.fti_on_tick_list = false;
data.fti_global_xform_interp_set = false;
data.merging_mode = MERGING_MODE_INHERIT;
data.client_physics_interpolation_data = nullptr;
@ -1123,4 +1173,8 @@ Spatial::Spatial() :
Spatial::~Spatial() {
_disable_client_physics_interpolation();
if (is_inside_tree()) {
get_tree()->get_scene_tree_fti().spatial_notify_delete(this);
}
}

View File

@ -55,6 +55,8 @@ class Spatial : public Node {
GDCLASS(Spatial, Node);
OBJ_CATEGORY("3D");
friend class SceneTreeFTI;
public:
enum MergingMode : unsigned int {
MERGING_MODE_INHERIT,
@ -77,15 +79,27 @@ private:
DIRTY_NONE = 0,
DIRTY_VECTORS = 1,
DIRTY_LOCAL = 2,
DIRTY_GLOBAL = 4
DIRTY_GLOBAL = 4,
DIRTY_GLOBAL_INTERPOLATED = 8,
};
mutable SelfList<Node> xform_change;
SelfList<Spatial> _client_physics_interpolation_spatials_list;
struct Data {
// Interpolated global transform - correct on the frame only.
// Only used with FTI.
Transform global_transform_interpolated;
// Current xforms are either
// * Used for everything (when not using FTI)
// * Correct on the physics tick (when using FTI)
mutable Transform global_transform;
mutable Transform local_transform;
// Only used with FTI.
Transform local_transform_prev;
mutable Vector3 rotation;
mutable Vector3 scale;
@ -109,6 +123,11 @@ private:
bool visible : 1;
bool disable_scale : 1;
// Scene tree interpolation
bool fti_on_frame_list : 1;
bool fti_on_tick_list : 1;
bool fti_global_xform_interp_set : 1;
bool merging_allowed : 1;
int children_lock;
@ -145,9 +164,27 @@ protected:
bool _is_vi_visible() const {
return data.vi_visible;
}
Transform _get_global_transform_interpolated(real_t p_interpolation_fraction);
const Transform &_get_cached_global_transform_interpolated() const { return data.global_transform_interpolated; }
void _disable_client_physics_interpolation();
// 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();
// 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() {}
// 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();
void _notification(int p_what);
static void _bind_methods();

View File

@ -174,14 +174,14 @@ class TooltipPanel : public PanelContainer {
GDCLASS(TooltipPanel, PanelContainer);
public:
TooltipPanel() {};
TooltipPanel() {}
};
class TooltipLabel : public Label {
GDCLASS(TooltipLabel, Label);
public:
TooltipLabel() {};
TooltipLabel() {}
};
/////////////////////////////////////
@ -387,7 +387,9 @@ void Viewport::_notification(int p_what) {
}
}
if (!GLOBAL_GET("physics/common/enable_pause_aware_picking")) {
GLOBAL_CACHED(physics_enable_pause_aware_picking, bool, "physics/common/enable_pause_aware_picking")
if (!physics_enable_pause_aware_picking) {
_process_picking(false);
}
} break;
@ -2005,9 +2007,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (gui.mouse_focus_mask == 0 && over != gui.mouse_over) {
if (gui.mouse_over) {
_gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
}
_drop_mouse_over();
_gui_cancel_tooltip();
@ -2126,9 +2126,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (over != gui.mouse_over) {
if (gui.mouse_over) {
_gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
}
_drop_mouse_over();
_gui_cancel_tooltip();
@ -2689,6 +2687,13 @@ void Viewport::_drop_mouse_focus() {
}
}
void Viewport::_drop_mouse_over() {
if (gui.mouse_over) {
_gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
gui.mouse_over = nullptr;
}
}
void Viewport::_drop_physics_mouseover(bool p_paused_only) {
physics_has_last_mousepos = false;
@ -2908,6 +2913,18 @@ bool Viewport::gui_has_modal_stack() const {
}
void Viewport::set_disable_input(bool p_disable) {
if (p_disable == disable_input) {
return;
}
GLOBAL_CACHED(global_drop_mouse_on_gui_input_disabled, bool, "gui/common/drop_mouse_on_gui_input_disabled")
if (p_disable && global_drop_mouse_on_gui_input_disabled) {
_drop_mouse_focus();
_drop_mouse_over();
_gui_cancel_tooltip();
}
disable_input = p_disable;
}

View File

@ -575,6 +575,7 @@ private:
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
void _drop_mouse_focus();
void _drop_mouse_over();
void _drop_physics_mouseover(bool p_paused_only = false);
void _update_canvas_items(Node *p_node);

View File

@ -2194,7 +2194,9 @@ SpatialMaterial::SpatialMaterial(bool p_orm) :
flags[i] = false;
}
force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
GLOBAL_CACHED(rendering_force_vertex_shading, bool, "rendering/quality/shading/force_vertex_shading");
force_vertex_shading = rendering_force_vertex_shading;
diffuse_mode = DIFFUSE_BURLEY;
specular_mode = SPECULAR_SCHLICK_GGX;

View File

@ -185,7 +185,9 @@ void AudioStreamPlaybackMicrophone::start(float p_from_pos) {
return;
}
if (!GLOBAL_GET("audio/enable_audio_input")) {
GLOBAL_CACHED(audio_enable_audio_input, bool, "audio/enable_audio_input");
if (!audio_enable_audio_input) {
WARN_PRINT("Need to enable Project settings > Audio > Enable Audio Input option to use capturing.");
return;
}

View File

@ -249,7 +249,9 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c
// Bodge to keep settings up to date, until the project settings PR is merged
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && ((Engine::get_singleton()->get_frames_drawn() % 16) == 0)) {
_max_polys = GLOBAL_GET("rendering/misc/occlusion_culling/max_active_polygons");
GLOBAL_CACHED(occlusion_culling_max_polygons, int32_t, "rendering/misc/occlusion_culling/max_active_polygons");
_max_polys = occlusion_culling_max_polygons;
}
#endif
_num_spheres = 0;

View File

@ -92,16 +92,8 @@ public:
RID material_override;
RID material_overlay;
// This is the main transform to be drawn with ..
// This will either be the interpolated transform (when using fixed timestep interpolation)
// or the ONLY transform (when not using FTI).
Transform transform;
// for interpolation we store the current transform (this physics tick)
// and the transform in the previous tick
Transform transform_curr;
Transform transform_prev;
int depth_layer;
uint32_t layer_mask;
@ -123,16 +115,6 @@ public:
bool baked_light : 1; //this flag is only to know if it actually did use baked light
bool redraw_if_visible : 1;
bool on_interpolate_list : 1;
bool on_interpolate_transform_list : 1;
bool interpolated : 1;
TransformInterpolator::Method interpolation_method : 3;
// For fixed timestep interpolation.
// Note 32 bits is plenty for checksum, no need for real_t
float transform_checksum_curr;
float transform_checksum_prev;
float depth; //used for sorting
SelfList<InstanceBase> dependency_item;
@ -158,12 +140,6 @@ public:
lightmap_capture = nullptr;
lightmap_slice = -1;
lightmap_uv_rect = Rect2(0, 0, 1, 1);
on_interpolate_list = false;
on_interpolate_transform_list = false;
interpolated = true;
interpolation_method = TransformInterpolator::INTERP_LERP;
transform_checksum_curr = 0.0;
transform_checksum_prev = 0.0;
}
};

View File

@ -569,8 +569,6 @@ public:
BIND2(instance_set_layer_mask, RID, uint32_t)
BIND3(instance_set_pivot_data, RID, float, bool)
BIND2(instance_set_transform, RID, const Transform &)
BIND2(instance_set_interpolated, RID, bool)
BIND1(instance_reset_physics_interpolation, RID)
BIND2(instance_attach_object_instance_id, RID, ObjectID)
BIND3(instance_set_blend_shape_weight, RID, int, float)
BIND3(instance_set_surface_material, RID, int, RID)

View File

@ -675,9 +675,6 @@ void RenderingServerScene::instance_set_scenario(RID p_instance, RID p_scenario)
_instance_destroy_occlusion_rep(instance);
}
// remove any interpolation data associated with the instance in this scenario
_interpolation_data.notify_free_instance(p_instance, *instance);
switch (instance->base_type) {
case RS::INSTANCE_LIGHT: {
InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
@ -767,27 +764,6 @@ void RenderingServerScene::instance_set_pivot_data(RID p_instance, float p_sorti
instance->use_aabb_center = p_use_aabb_center;
}
void RenderingServerScene::instance_reset_physics_interpolation(RID p_instance) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
if (_interpolation_data.interpolation_enabled && instance->interpolated) {
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
}
}
void RenderingServerScene::instance_set_interpolated(RID p_instance, bool p_interpolated) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
instance->interpolated = p_interpolated;
}
void RenderingServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
@ -796,40 +772,8 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor
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
}
#ifdef DEBUG_ENABLED
for (int i = 0; i < 4; i++) {
const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin;
ERR_FAIL_COND(Math::is_inf(v.x));
ERR_FAIL_COND(Math::is_nan(v.x));
ERR_FAIL_COND(Math::is_inf(v.y));
ERR_FAIL_COND(Math::is_nan(v.y));
ERR_FAIL_COND(Math::is_inf(v.z));
ERR_FAIL_COND(Math::is_nan(v.z));
}
#endif
instance->transform = p_transform;
_instance_queue_update(instance, true);
return;
}
float new_checksum = TransformInterpolator::checksum_transform(p_transform);
bool checksums_match = (instance->transform_checksum_curr == new_checksum) && (instance->transform_checksum_prev == new_checksum);
// we can't entirely reject no changes because we need the interpolation
// system to keep on stewing
// Optimized check. First checks the checksums. If they pass it does the slow check at the end.
// Alternatively we can do this non-optimized and ignore the checksum...
// if no change
if (checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform)) {
return;
if (instance->transform == p_transform) {
return; //must be checked to avoid worst evil
}
#ifdef DEBUG_ENABLED
@ -846,53 +790,10 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor
#endif
instance->transform_curr = p_transform;
// keep checksums up to date
instance->transform_checksum_curr = new_checksum;
if (!instance->on_interpolate_transform_list) {
_interpolation_data.instance_transform_update_list_curr->push_back(p_instance);
instance->on_interpolate_transform_list = true;
} else {
DEV_ASSERT(_interpolation_data.instance_transform_update_list_curr->size());
}
// If the instance is invisible, then we are simply updating the data flow, there is no need to calculate the interpolated
// transform or anything else.
// Ideally we would not even call the RenderingServer::set_transform() when invisible but that would entail having logic
// to keep track of the previous transform on the SceneTree side. The "early out" below is less efficient but a lot cleaner codewise.
if (!instance->visible) {
return;
}
// decide on the interpolation method .. slerp if possible
instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis);
if (!instance->on_interpolate_list) {
_interpolation_data.instance_interpolate_update_list.push_back(p_instance);
instance->on_interpolate_list = true;
} else {
DEV_ASSERT(_interpolation_data.instance_interpolate_update_list.size());
}
instance->transform = p_transform;
_instance_queue_update(instance, true);
}
void RenderingServerScene::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) {
r_instance.on_interpolate_list = false;
r_instance.on_interpolate_transform_list = false;
if (!interpolation_enabled) {
return;
}
// if the instance was on any of the lists, remove
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);
}
void RenderingServerScene::update_interpolation_tick(bool p_process) {
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames()));
@ -900,84 +801,11 @@ void RenderingServerScene::update_interpolation_tick(bool p_process) {
// update interpolation in storage
RSG::storage->update_interpolation_tick(p_process);
// detect any that were on the previous transform list that are no longer active,
// we should remove them from the interpolate list
for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_prev->size(); n++) {
const RID &rid = (*_interpolation_data.instance_transform_update_list_prev)[n];
Instance *instance = instance_owner.getornull(rid);
bool active = true;
// no longer active? (either the instance deleted or no longer being transformed)
if (instance && !instance->on_interpolate_transform_list) {
active = false;
instance->on_interpolate_list = false;
// make sure the most recent transform is set
instance->transform = instance->transform_curr;
// and that both prev and current are the same, just in case of any interpolations
instance->transform_prev = instance->transform_curr;
// make sure are updated one more time to ensure the AABBs are correct
_instance_queue_update(instance, true);
}
if (!instance) {
active = false;
}
if (!active) {
_interpolation_data.instance_interpolate_update_list.erase(rid);
}
}
// and now for any in the transform list (being actively interpolated), keep the previous transform
// value up to date ready for the next tick
if (p_process) {
for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_curr->size(); n++) {
const RID &rid = (*_interpolation_data.instance_transform_update_list_curr)[n];
Instance *instance = instance_owner.getornull(rid);
if (instance) {
instance->transform_prev = instance->transform_curr;
instance->transform_checksum_prev = instance->transform_checksum_curr;
instance->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.instance_transform_update_list_curr, _interpolation_data.instance_transform_update_list_prev);
// prepare for the next iteration
_interpolation_data.instance_transform_update_list_curr->clear();
}
void RenderingServerScene::update_interpolation_frame(bool p_process) {
// update interpolation in storage
RSG::storage->update_interpolation_frame(p_process);
if (p_process) {
real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
for (unsigned int i = 0; i < _interpolation_data.instance_interpolate_update_list.size(); i++) {
const RID &rid = _interpolation_data.instance_interpolate_update_list[i];
Instance *instance = instance_owner.getornull(rid);
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);
}
} // for n
}
}
void RenderingServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) {
@ -1031,25 +859,6 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible)
instance->visible = p_visible;
// Special case for physics interpolation, we want to ensure the interpolated data is up to date
if (_interpolation_data.interpolation_enabled && p_visible && instance->interpolated && instance->scenario && !instance->on_interpolate_list) {
// Do all the extra work we normally do on instance_set_transform(), because this is optimized out for hidden instances.
// This prevents a glitch of stale interpolation transform data when unhiding before the next physics tick.
instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis);
_interpolation_data.instance_interpolate_update_list.push_back(p_instance);
instance->on_interpolate_list = true;
_instance_queue_update(instance, true);
// We must also place on the transform update list for a tick, so the system
// can auto-detect if the instance is no longer moving, and remove from the interpolate lists again.
// If this step is ignored, an unmoving instance could remain on the interpolate lists indefinitely
// (or rather until the object is deleted) and cause unnecessary updates and drawcalls.
if (!instance->on_interpolate_transform_list) {
_interpolation_data.instance_transform_update_list_curr->push_back(p_instance);
instance->on_interpolate_transform_list = true;
}
}
// give the opportunity for the spatial partitioning scene to use a special implementation of visibility
// for efficiency (supported in BVH but not octree)
@ -4398,8 +4207,6 @@ bool RenderingServerScene::free(RID p_rid) {
Instance *instance = instance_owner.get(p_rid);
_interpolation_data.notify_free_instance(p_rid, *instance);
instance_set_use_lightmap(p_rid, RID(), RID(), -1, Rect2(0, 0, 1, 1));
instance_set_scenario(p_rid, RID());
instance_set_base(p_rid, RID());

View File

@ -398,12 +398,6 @@ public:
virtual void set_physics_interpolation_enabled(bool p_enabled);
struct InterpolationData {
void notify_free_instance(RID p_rid, Instance &r_instance);
LocalVector<RID> instance_interpolate_update_list;
LocalVector<RID> instance_transform_update_lists[2];
LocalVector<RID> *instance_transform_update_list_curr = &instance_transform_update_lists[0];
LocalVector<RID> *instance_transform_update_list_prev = &instance_transform_update_lists[1];
bool interpolation_enabled = false;
} _interpolation_data;
@ -664,8 +658,6 @@ public:
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask);
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center);
virtual void instance_set_transform(RID p_instance, const Transform &p_transform);
virtual void instance_set_interpolated(RID p_instance, bool p_interpolated);
virtual void instance_reset_physics_interpolation(RID p_instance);
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id);
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight);
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material);

View File

@ -478,8 +478,6 @@ public:
FUNC2(instance_set_layer_mask, RID, uint32_t)
FUNC3(instance_set_pivot_data, RID, float, bool)
FUNC2(instance_set_transform, RID, const Transform &)
FUNC2(instance_set_interpolated, RID, bool)
FUNC1(instance_reset_physics_interpolation, RID)
FUNC2(instance_attach_object_instance_id, RID, ObjectID)
FUNC3(instance_set_blend_shape_weight, RID, int, float)
FUNC3(instance_set_surface_material, RID, int, RID)

View File

@ -870,7 +870,8 @@ uint32_t RenderingServer::mesh_surface_get_format_stride(uint32_t p_format, int
}
void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t *r_strides) const {
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream");
bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
int attributes_base_offset = 0;
int attributes_stride = 0;
@ -1248,7 +1249,8 @@ bool RenderingServer::_mesh_find_format(RS::PrimitiveType p_primitive, const Arr
}
uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) {
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream");
bool use_split_stream = storage_split_stream && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
uint32_t offsets[RS::ARRAY_MAX];
@ -1268,7 +1270,8 @@ uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive
}
void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) {
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream");
bool use_split_stream = storage_split_stream && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
uint32_t offsets[RS::ARRAY_MAX];
@ -1338,7 +1341,8 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p
}
Array RenderingServer::_get_array_from_surface(uint32_t p_format, PoolVector<uint8_t> p_vertex_data, int p_vertex_len, PoolVector<uint8_t> p_index_data, int p_index_len) const {
bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream");
bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
uint32_t offsets[ARRAY_MAX];
uint32_t strides[RS::ARRAY_MAX];
@ -2168,8 +2172,6 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instance_set_scenario", "instance", "scenario"), &RenderingServer::instance_set_scenario);
ClassDB::bind_method(D_METHOD("instance_set_layer_mask", "instance", "mask"), &RenderingServer::instance_set_layer_mask);
ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform);
ClassDB::bind_method(D_METHOD("instance_set_interpolated", "instance", "interpolated"), &RenderingServer::instance_set_interpolated);
ClassDB::bind_method(D_METHOD("instance_reset_physics_interpolation", "instance"), &RenderingServer::instance_reset_physics_interpolation);
ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id);
ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight);
ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material);
@ -2187,7 +2189,9 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &RenderingServer::_instances_cull_convex_bind, DEFVAL(RID()));
#endif
ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create);
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring);
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate);

View File

@ -876,8 +876,6 @@ public:
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0;
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0;
virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0;
virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0;
virtual void instance_reset_physics_interpolation(RID p_instance) = 0;
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0;
virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0;
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0;