mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-04-22 03:11:18 +02:00
Merge branch 'Relintai:master' into Relintai-master
This commit is contained in:
commit
48d17312e9
@ -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]
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -39,3 +39,4 @@ MainLoop *test();
|
||||
}
|
||||
|
||||
#endif // TEST_EXPRESSION_H
|
||||
|
||||
|
@ -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("_")) {
|
||||
|
@ -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;
|
||||
|
@ -77,6 +77,7 @@
|
||||
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="""">
|
||||
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>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
288
scene/main/scene_tree_fti.cpp
Normal file
288
scene/main/scene_tree_fti.cpp
Normal 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
112
scene/main/scene_tree_fti.h
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user