diff --git a/core/local_vector.h b/core/local_vector.h index 8a571c8a34..79b018ed0f 100644 --- a/core/local_vector.h +++ b/core/local_vector.h @@ -101,6 +101,22 @@ public: } } + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U count = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_unordered(idx); + from = idx; + count++; + } + return count; + } + void invert() { for (U i = 0; i < count / 2; i++) { SWAP(data[i], data[count - i - 1]); diff --git a/core/math/interpolator.cpp b/core/math/interpolator.cpp new file mode 100644 index 0000000000..d840044fcd --- /dev/null +++ b/core/math/interpolator.cpp @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* interpolator.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "interpolator.h" + +#include "core/math/transform.h" + +void Interpolator::interpolate_transform(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction, Method p_method) { + switch (p_method) { + default: { + interpolate_transform_linear(p_prev, p_curr, r_result, p_fraction); + } break; + case INTERP_SLERP: { + r_result.origin = p_prev.origin + ((p_curr.origin - p_prev.origin) * p_fraction); + r_result.basis = p_prev.basis.slerp(p_curr.basis, p_fraction); + } break; + } +} + +void Interpolator::interpolate_transform_linear(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction) { + // interpolate translate + r_result.origin = p_prev.origin + ((p_curr.origin - p_prev.origin) * p_fraction); + + // interpolate basis + for (int n = 0; n < 3; n++) { + r_result.basis.elements[n] = p_prev.basis.elements[n].linear_interpolate(p_curr.basis.elements[n], p_fraction); + } +} + +real_t Interpolator::checksum_transform(const Transform &p_transform) { + // just a really basic checksum, this can probably be improved + real_t sum = vec3_sum(p_transform.origin); + sum -= vec3_sum(p_transform.basis.elements[0]); + sum += vec3_sum(p_transform.basis.elements[1]); + sum -= vec3_sum(p_transform.basis.elements[2]); + return sum; +} + +bool Interpolator::should_slerp(const Basis &p_a, const Basis &p_b) { + // the two basis should be suitable, and also if they are close enough, + // no need for a slerp anyway. + bool slerp = false; + if (p_a.is_rotation()) { + Quat from(p_a); + + if (from.is_normalized() && p_b.is_rotation()) { + Quat to(p_b); + if (to.is_normalized()) { + // are they close together? + // calc cosine + real_t cosom = Math::abs(from.dot(to)); + if ((1.0 - cosom) > CMP_EPSILON) { + slerp = true; + } + } + } + } + + return slerp; +} diff --git a/core/math/interpolator.h b/core/math/interpolator.h new file mode 100644 index 0000000000..2c35ed51fe --- /dev/null +++ b/core/math/interpolator.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* interpolator.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 INTERPOLATOR_H +#define INTERPOLATOR_H + +#include "core/math/math_defs.h" +#include "core/math/vector3.h" + +// Keep all the functions for fixed timestep interpolation together + +class Transform; + +class Interpolator { + static real_t vec3_sum(const Vector3 &p_pt) { return p_pt.x + p_pt.y + p_pt.z; } + +public: + enum Method { + INTERP_LERP, + INTERP_SLERP, + }; + + static void interpolate_transform_linear(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction); + static void interpolate_transform(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction, Method p_method); + static real_t checksum_transform(const Transform &p_transform); + static bool should_slerp(const Basis &p_a, const Basis &p_b); +}; + +#endif // INTERPOLATOR_H diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 7775940993..84047c8ee9 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -675,6 +675,11 @@ Sets whether this is an instance load placeholder. See [InstancePlaceholder]. + + + + + @@ -787,6 +792,8 @@ Notification received when the node is ready, just before [constant NOTIFICATION_READY] is received. Unlike the latter, it's sent every time the node enters tree, instead of only once. + + Notification received from the OS when the mouse enters the game window. Implemented on desktop and web platforms. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 3a4462769b..eec944bf17 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1127,6 +1127,8 @@ The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. [b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.iterations_per_second] instead. + + Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of in-game clock and real clock, but allows smoothing out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended. [b]Note:[/b] For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics/common/physics_jitter_fix] to [code]0[/code]. diff --git a/main/main_timer_sync.cpp b/main/main_timer_sync.cpp index 508c3dc26d..c22dd8b918 100644 --- a/main/main_timer_sync.cpp +++ b/main/main_timer_sync.cpp @@ -291,6 +291,17 @@ int64_t MainTimerSync::DeltaSmoother::smooth_delta(int64_t p_delta) { // before advance_core considers changing the physics_steps return from // the typical values as defined by typical_physics_steps float MainTimerSync::get_physics_jitter_fix() { + // Turn off jitter fix when using fixed timestep interpolation + // Note this shouldn't be on UNTIL 2d interpolation is implemented, + // otherwise we will get people making 2d games with the physics_interpolation + // set to on getting jitter fix disabled unexpectedly. +#if 0 + if (Engine::get_singleton()->is_physics_interpolation_enabled()) { + // would be better to write a simple bypass for jitter fix but this will do to get started + return 0.0; + } +#endif + return Engine::get_singleton()->get_physics_jitter_fix(); } diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 19fe77e860..bb80459a21 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -98,6 +98,10 @@ void Camera::_update_camera() { } } +void Camera::_physics_interpolated_changed() { + VisualServer::get_singleton()->camera_set_interpolated(camera, is_physics_interpolated()); +} + void Camera::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { @@ -112,6 +116,9 @@ void Camera::_notification(int p_what) { viewport->_camera_set(this); } + ERR_FAIL_COND(get_world().is_null()); + VisualServer::get_singleton()->camera_set_scenario(camera, get_world()->get_scenario()); + } break; case NOTIFICATION_TRANSFORM_CHANGED: { _request_camera_update(); @@ -119,7 +126,14 @@ void Camera::_notification(int p_what) { velocity_tracker->update_position(get_global_transform().origin); } } break; + case NOTIFICATION_TELEPORT: { + if (is_physics_interpolated()) { + VisualServer::get_singleton()->camera_teleport(camera); + } + } break; case NOTIFICATION_EXIT_WORLD: { + VisualServer::get_singleton()->camera_set_scenario(camera, RID()); + if (!get_tree()->is_node_being_edited(this)) { if (is_current()) { clear_current(); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 5396fce634..b742cc0eba 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -94,6 +94,8 @@ protected: virtual void _request_camera_update(); void _update_camera_mode(); + virtual void _physics_interpolated_changed(); + void _notification(int p_what); virtual void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 7e51fe7075..38e943b663 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -85,6 +85,13 @@ void VisualInstance::_notification(int p_what) { VisualServer::get_singleton()->instance_set_transform(instance, gt); } } break; + case NOTIFICATION_TELEPORT: { + if (_get_spatial_flags() & SPATIAL_FLAG_VI_VISIBLE) { + if (is_physics_interpolated()) { + VisualServer::get_singleton()->instance_teleport(instance); + } + } + } break; case NOTIFICATION_EXIT_WORLD: { VisualServer::get_singleton()->instance_set_scenario(instance, RID()); VisualServer::get_singleton()->instance_attach_skeleton(instance, RID()); @@ -101,6 +108,10 @@ void VisualInstance::_notification(int p_what) { } } +void VisualInstance::_physics_interpolated_changed() { + VisualServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated()); +} + RID VisualInstance::get_instance() const { return instance; } diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 6487fc199e..ce206fdc5f 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -49,6 +49,7 @@ class VisualInstance : public CullInstance { protected: void _update_visibility(); virtual void _refresh_portal_mode(); + virtual void _physics_interpolated_changed(); void _notification(int p_what); static void _bind_methods(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0e2af71ce6..d0454d3e62 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -31,6 +31,7 @@ #include "node.h" #include "core/core_string_names.h" +#include "core/engine.h" #include "core/io/resource_loader.h" #include "core/message_queue.h" #include "core/print_string.h" @@ -189,6 +190,19 @@ void Node::_propagate_ready() { } } +void Node::_propagate_physics_interpolated(bool p_interpolated) { + data.physics_interpolated = p_interpolated; + + // allow a call to the VisualServer etc in derived classes + _physics_interpolated_changed(); + + data.blocked++; + for (int i = 0; i < data.children.size(); i++) { + data.children[i]->_propagate_physics_interpolated(p_interpolated); + } + data.blocked--; +} + void Node::_propagate_enter_tree() { // this needs to happen to all children before any enter_tree @@ -376,6 +390,8 @@ void Node::move_child_notify(Node *p_child) { // to be used when not wanted } +void Node::_physics_interpolated_changed() {} + void Node::set_physics_process(bool p_process) { if (data.physics_process == p_process) { return; @@ -754,6 +770,22 @@ bool Node::can_process() const { return true; } +void Node::set_physics_interpolated(bool p_interpolated) { + // if swapping from interpolated to non-interpolated, use this as + // an extra means to cause a teleport + if (is_physics_interpolated() && !p_interpolated) { + teleport(); + } + + _propagate_physics_interpolated(p_interpolated); +} + +void Node::teleport() { + if ((get_tree() && get_tree()->is_physics_interpolation_enabled()) && is_physics_interpolated()) { + propagate_notification(NOTIFICATION_TELEPORT); + } +} + float Node::get_physics_process_delta_time() const { if (data.tree) { return data.tree->get_physics_process_time(); @@ -2828,6 +2860,11 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_physics_process_internal", "enable"), &Node::set_physics_process_internal); ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal); + // Disabled for now + // ClassDB::bind_method(D_METHOD("set_physics_interpolated", "p_interpolated"), &Node::set_physics_interpolated); + // ClassDB::bind_method(D_METHOD("is_physics_interpolated"), &Node::is_physics_interpolated); + ClassDB::bind_method(D_METHOD("teleport"), &Node::teleport); + ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); @@ -2907,6 +2944,7 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS); BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); + BIND_CONSTANT(NOTIFICATION_TELEPORT); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); @@ -2952,6 +2990,9 @@ void Node::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority"); + // Disabled for now + // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_interpolated"), "set_physics_interpolated", "is_physics_interpolated"); + BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta"))); BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta"))); BIND_VMETHOD(MethodInfo("_enter_tree")); @@ -2990,6 +3031,7 @@ Node::Node() { data.idle_process_internal = false; data.inside_tree = false; data.ready_notified = false; + data.physics_interpolated = true; data.owner = nullptr; data.OW = nullptr; diff --git a/scene/main/node.h b/scene/main/node.h index 1f37f2e6fc..96f1190ef8 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -100,9 +100,6 @@ private: int blocked; // safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; SceneTree *tree; - bool inside_tree; - bool ready_notified; //this is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification - bool ready_first; #ifdef TOOLS_ENABLED NodePath import_path; //path used when imported, used by scene editors to keep tracking #endif @@ -120,25 +117,31 @@ private: Map rpc_methods; Map rpc_properties; - // variables used to properly sort the node when processing, ignored otherwise - //should move all the stuff below to bits - bool physics_process; - bool idle_process; int process_priority; - bool physics_process_internal; - bool idle_process_internal; + // variables used to properly sort the node when processing, ignored otherwise + //should move all the stuff below to bits + bool physics_process : 1; + bool idle_process : 1; - bool input; - bool unhandled_input; - bool unhandled_key_input; + bool physics_process_internal : 1; + bool idle_process_internal : 1; - bool parent_owned; - bool in_constructor; - bool use_placeholder; + bool input : 1; + bool unhandled_input : 1; + bool unhandled_key_input : 1; + bool physics_interpolated : 1; - bool display_folded; - bool editable_instance; + bool parent_owned : 1; + bool in_constructor : 1; + bool use_placeholder : 1; + + bool display_folded : 1; + bool editable_instance : 1; + + bool inside_tree : 1; + bool ready_notified : 1; //this is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification + bool ready_first : 1; mutable NodePath *path_cache; @@ -163,6 +166,7 @@ private: void _propagate_exit_tree(); void _propagate_after_exit_tree(); void _propagate_validate_owner(); + void _propagate_physics_interpolated(bool p_interpolated); void _print_stray_nodes(); void _propagate_pause_owner(Node *p_owner); Array _get_node_and_resource(const NodePath &p_path); @@ -193,6 +197,8 @@ protected: virtual void remove_child_notify(Node *p_child); virtual void move_child_notify(Node *p_child); + virtual void _physics_interpolated_changed(); + void _propagate_replace_owner(Node *p_owner, Node *p_by_owner); static void _bind_methods(); @@ -226,6 +232,7 @@ public: NOTIFICATION_INTERNAL_PROCESS = 25, NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26, NOTIFICATION_POST_ENTER_TREE = 27, + NOTIFICATION_TELEPORT = 28, //keep these linked to node NOTIFICATION_WM_MOUSE_ENTER = MainLoop::NOTIFICATION_WM_MOUSE_ENTER, NOTIFICATION_WM_MOUSE_EXIT = MainLoop::NOTIFICATION_WM_MOUSE_EXIT, @@ -386,6 +393,10 @@ public: bool can_process() const; bool can_process_notification(int p_what) const; + void set_physics_interpolated(bool p_interpolated); + _FORCE_INLINE_ bool is_physics_interpolated() const { return data.physics_interpolated; } + void teleport(); + void request_ready(); static void print_stray_nodes(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 2dc6e3a7fb..da5fc6fe61 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -465,11 +465,42 @@ void SceneTree::init() { MainLoop::init(); } +void SceneTree::set_physics_interpolation_enabled(bool p_enabled) { + // disallow interpolation in editor + if (Engine::get_singleton()->is_editor_hint()) { + p_enabled = false; + } + + if (p_enabled == _physics_interpolation_enabled) { + return; + } + + _physics_interpolation_enabled = p_enabled; + + if (root->get_world().is_valid()) { + RID scenario = root->get_world()->get_scenario(); + if (scenario.is_valid()) { + VisualServer::get_singleton()->scenario_set_physics_interpolation_enabled(scenario, p_enabled); + } + } +} + +bool SceneTree::is_physics_interpolation_enabled() const { + return _physics_interpolation_enabled; +} + bool SceneTree::iteration(float p_time) { root_lock++; current_frame++; + if (root->get_world().is_valid()) { + RID scenario = root->get_world()->get_scenario(); + if (scenario.is_valid()) { + VisualServer::get_singleton()->scenario_tick(scenario); + } + } + flush_transform_notifications(); MainLoop::iteration(p_time); @@ -602,6 +633,13 @@ bool SceneTree::idle(float p_time) { #endif + if (root->get_world().is_valid()) { + RID scenario = root->get_world()->get_scenario(); + if (scenario.is_valid()) { + VisualServer::get_singleton()->scenario_pre_draw(scenario, true); + } + } + return _quit; } @@ -2036,6 +2074,7 @@ SceneTree::SceneTree() { call_lock = 0; root_lock = 0; node_count = 0; + _physics_interpolation_enabled = false; //create with mainloop @@ -2045,6 +2084,7 @@ SceneTree::SceneTree() { if (!root->get_world().is_valid()) { root->set_world(Ref(memnew(World))); } + set_physics_interpolation_enabled(GLOBAL_DEF("physics/common/physics_interpolation", true)); // Initialize network state multiplayer_poll = true; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 83f1bd909f..fddc78a187 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -117,6 +117,7 @@ private: bool _quit; bool initialized; bool input_handled; + bool _physics_interpolation_enabled; Size2 last_screen_size; StringName tree_changed_name; @@ -407,6 +408,9 @@ public: void set_refuse_new_network_connections(bool p_refuse); bool is_refusing_new_network_connections() const; + void set_physics_interpolation_enabled(bool p_enabled); + bool is_physics_interpolation_enabled() const; + static void add_idle_callback(IdleCallback p_callback); SceneTree(); ~SceneTree(); diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 452662c275..b693d39da3 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -32,6 +32,7 @@ #define RASTERIZER_H #include "core/math/camera_matrix.h" +#include "core/math/interpolator.h" #include "servers/visual_server.h" #include "core/self_list.h" @@ -89,8 +90,16 @@ public: RID skeleton; RID material_override; + // 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; @@ -106,11 +115,21 @@ public: VS::ShadowCastingSetting cast_shadows; //fit in 32 bits - bool mirror : 8; - bool receive_shadows : 8; - bool visible : 8; - bool baked_light : 4; //this flag is only to know if it actually did use baked light - bool redraw_if_visible : 4; + bool mirror : 1; + bool receive_shadows : 1; + bool visible : 1; + 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; + Interpolator::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 @@ -137,6 +156,12 @@ 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 = Interpolator::INTERP_LERP; + transform_checksum_curr = 0.0; + transform_checksum_prev = 0.0; } }; diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 25a4e655d4..f95b35e8e3 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -94,7 +94,17 @@ void VisualServerRaster::request_frame_drawn_callback(Object *p_where, const Str frame_drawn_callbacks.push_back(fdc); } +void VisualServerRaster::scenario_tick(RID p_scenario) { + VSG::scene->_scenario_tick(p_scenario); +} + +void VisualServerRaster::scenario_pre_draw(RID p_scenario, bool p_will_draw) { + VSG::scene->_scenario_pre_draw(p_scenario, p_will_draw); +} + void VisualServerRaster::draw(bool p_swap_buffers, double frame_step) { + //VSG::scene->update_interpolate_list(); + //needs to be done before changes is reset to 0, to not force the editor to redraw VS::get_singleton()->emit_signal("frame_pre_draw"); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 9e7c754206..5b3ffa502b 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -436,10 +436,13 @@ public: /* CAMERA API */ BIND0R(RID, camera_create) + BIND2(camera_set_scenario, RID, RID) BIND4(camera_set_perspective, RID, float, float, float) BIND4(camera_set_orthogonal, RID, float, float, float) BIND5(camera_set_frustum, RID, float, Vector2, float, float) BIND2(camera_set_transform, RID, const Transform &) + BIND2(camera_set_interpolated, RID, bool) + BIND1(camera_teleport, RID) BIND2(camera_set_cull_mask, RID, uint32_t) BIND2(camera_set_environment, RID, RID) BIND2(camera_set_use_vertical_aspect, RID, bool) @@ -540,6 +543,7 @@ public: BIND2(scenario_set_environment, RID, RID) BIND3(scenario_set_reflection_atlas_size, RID, int, int) BIND2(scenario_set_fallback_environment, RID, RID) + BIND2(scenario_set_physics_interpolation_enabled, RID, bool) /* INSTANCING API */ BIND0R(RID, instance_create) @@ -548,6 +552,8 @@ public: BIND2(instance_set_scenario, RID, RID) BIND2(instance_set_layer_mask, RID, uint32_t) BIND2(instance_set_transform, RID, const Transform &) + BIND2(instance_set_interpolated, RID, bool) + BIND1(instance_teleport, 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) @@ -735,6 +741,8 @@ public: virtual bool has_changed() const; virtual void init(); virtual void finish(); + virtual void scenario_tick(RID p_scenario); + virtual void scenario_pre_draw(RID p_scenario, bool p_will_draw); /* STATUS INFORMATION */ diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index f80814b89c..d708bc5c3b 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -30,6 +30,7 @@ #include "visual_server_scene.h" +#include "core/math/interpolator.h" #include "core/os/os.h" #include "visual_server_globals.h" #include "visual_server_raster.h" @@ -38,11 +39,33 @@ /* CAMERA API */ +Transform VisualServerScene::Camera::get_transform() const { + if (!is_currently_interpolated()) { + return transform; + } + + Transform final; + Interpolator::interpolate_transform(transform_prev, transform, final, Engine::get_singleton()->get_physics_interpolation_fraction(), interpolation_method); + return final; +} + RID VisualServerScene::camera_create() { Camera *camera = memnew(Camera); return camera_owner.make_rid(camera); } +void VisualServerScene::camera_set_scenario(RID p_camera, RID p_scenario) { + Camera *camera = camera_owner.get(p_camera); + ERR_FAIL_COND(!camera); + + if (p_scenario.is_valid()) { + camera->scenario = scenario_owner.get(p_scenario); + ERR_FAIL_COND(!camera->scenario); + } else { + camera->scenario = nullptr; + } +} + void VisualServerScene::camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) { Camera *camera = camera_owner.get(p_camera); ERR_FAIL_COND(!camera); @@ -71,10 +94,37 @@ void VisualServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p camera->zfar = p_z_far; } +void VisualServerScene::camera_teleport(RID p_camera) { + Camera *camera = camera_owner.get(p_camera); + ERR_FAIL_COND(!camera); + + if (camera->is_currently_interpolated()) { + camera->scenario->_interpolation_data.camera_teleport_list.push_back(p_camera); + } +} + +void VisualServerScene::camera_set_interpolated(RID p_camera, bool p_interpolated) { + Camera *camera = camera_owner.get(p_camera); + ERR_FAIL_COND(!camera); + camera->interpolated = p_interpolated; +} + void VisualServerScene::camera_set_transform(RID p_camera, const Transform &p_transform) { Camera *camera = camera_owner.get(p_camera); ERR_FAIL_COND(!camera); + camera->transform = p_transform.orthonormalized(); + + if (camera->is_currently_interpolated()) { + if (!camera->on_interpolate_transform_list) { + camera->scenario->_interpolation_data.camera_transform_update_list_curr->push_back(p_camera); + camera->on_interpolate_transform_list = true; + } + + // decide on the interpolation method .. slerp if possible + bool can_slerp = Interpolator::should_slerp(camera->transform_prev.basis, camera->transform.basis); + camera->interpolation_method = can_slerp ? Interpolator::INTERP_SLERP : Interpolator::INTERP_LERP; + } } void VisualServerScene::camera_set_cull_mask(RID p_camera, uint32_t p_layers) { @@ -212,6 +262,7 @@ void VisualServerScene::SpatialPartitioningScene_Octree::set_balance(float p_bal VisualServerScene::Scenario::Scenario() { debug = VS::SCENARIO_DEBUG_DISABLED; + _interpolation_data.interpolation_enabled = false; bool use_bvh_or_octree = GLOBAL_GET("rendering/quality/spatial_partitioning/use_bvh"); @@ -379,6 +430,33 @@ RID VisualServerScene::scenario_create() { return scenario_rid; } +void VisualServerScene::scenario_set_physics_interpolation_enabled(RID p_scenario, bool p_enabled) { + Scenario *scenario = scenario_owner.get(p_scenario); + ERR_FAIL_COND(!scenario); + scenario->_interpolation_data.interpolation_enabled = p_enabled; +} + +void VisualServerScene::_scenario_tick(RID p_scenario) { + Scenario *scenario = scenario_owner.get(p_scenario); + ERR_FAIL_COND(!scenario); + + if (scenario->is_physics_interpolation_enabled()) { + update_interpolation_transform_list(scenario->_interpolation_data, true); + } +} + +void VisualServerScene::_scenario_pre_draw(RID p_scenario, bool p_will_draw) { + Scenario *scenario = scenario_owner.get(p_scenario); + ERR_FAIL_COND(!scenario); + + // even when running and not drawing scenes, we still need to clear intermediate per frame + // interpolation data .. hence the p_will_draw flag (so we can reduce the processing if the frame + // will not be drawn) + if (scenario->is_physics_interpolation_enabled()) { + update_interpolate_list(scenario->_interpolation_data, p_will_draw); + } +} + void VisualServerScene::scenario_set_debug(RID p_scenario, VS::ScenarioDebugMode p_debug_mode) { Scenario *scenario = scenario_owner.get(p_scenario); ERR_FAIL_COND(!scenario); @@ -675,30 +753,248 @@ void VisualServerScene::instance_set_layer_mask(RID p_instance, uint32_t p_mask) instance->layer_mask = p_mask; } + +void VisualServerScene::instance_teleport(RID p_instance) { + Instance *instance = instance_owner.get(p_instance); + ERR_FAIL_COND(!instance); + + if (instance->is_currently_interpolated()) { + instance->scenario->_interpolation_data.instance_teleport_list.push_back(p_instance); + } +} + +void VisualServerScene::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 VisualServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) { Instance *instance = instance_owner.get(p_instance); ERR_FAIL_COND(!instance); - if (instance->transform == p_transform) { - return; //must be checked to avoid worst evil - } + if (!instance->is_currently_interpolated()) { + 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.elements[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)); - } + for (int i = 0; i < 4; i++) { + const Vector3 &v = i < 3 ? p_transform.basis.elements[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); + instance->transform = p_transform; + _instance_queue_update(instance, true); + return; + } + + float new_checksum = Interpolator::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 + // bool no_change = (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform); + + // optimized check. First checks the checksums. If they pass it does the slow check at the end. + bool no_change = checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform); + + if (!no_change) { +#ifdef DEBUG_ENABLED + + for (int i = 0; i < 4; i++) { + const Vector3 &v = i < 3 ? p_transform.basis.elements[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_curr = p_transform; + + // decide on the interpolation method .. slerp if possible + bool can_slerp = Interpolator::should_slerp(instance->transform_prev.basis, instance->transform_curr.basis); + instance->interpolation_method = can_slerp ? Interpolator::INTERP_SLERP : Interpolator::INTERP_LERP; + + // keep checksums up to date + instance->transform_checksum_curr = new_checksum; + + // We temporarily need to also store the current transform here + // for use by the update_instance function, so the calculated AABB + // makes sense. + // instance->transform = p_transform; + + if (!instance->on_interpolate_transform_list) { + instance->scenario->_interpolation_data.instance_transform_update_list_curr->push_back(p_instance); + instance->on_interpolate_transform_list = true; + } else { + DEV_ASSERT(!instance->scenario->_interpolation_data.instance_transform_update_list_curr->size()); + } + + if (!instance->on_interpolate_list) { + instance->scenario->_interpolation_data.instance_interpolate_update_list.push_back(p_instance); + instance->on_interpolate_list = true; + } else { + DEV_ASSERT(!instance->scenario->_interpolation_data.instance_interpolate_update_list.size()); + } + + _instance_queue_update(instance, true); + } } + +void VisualServerScene::Scenario::InterpolationData::notify_free_camera(RID p_rid) { + if (!interpolation_enabled) { + return; + } + + // if the camera was on any of the lists, remove + camera_transform_update_list_curr->erase_multiple_unordered(p_rid); + camera_transform_update_list_prev->erase_multiple_unordered(p_rid); + camera_teleport_list.erase_multiple_unordered(p_rid); +} + +void VisualServerScene::Scenario::InterpolationData::notify_free_instance(RID p_rid) { + 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); + instance_teleport_list.erase_multiple_unordered(p_rid); +} + +void VisualServerScene::update_interpolation_transform_list(Scenario::InterpolationData &r_interpolation_data, bool 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 < r_interpolation_data.instance_transform_update_list_prev->size(); n++) { + const RID &rid = (*r_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; + } + + if (!instance) { + active = false; + } + + if (!active) { + r_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 < r_interpolation_data.instance_transform_update_list_curr->size(); n++) { + const RID &rid = (*r_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(r_interpolation_data.instance_transform_update_list_curr, r_interpolation_data.instance_transform_update_list_prev); + + // prepare for the next iteration + r_interpolation_data.instance_transform_update_list_curr->clear(); + + // CAMERAS + // detect any that were on the previous transform list that are no longer active, + for (unsigned int n = 0; n < r_interpolation_data.camera_transform_update_list_prev->size(); n++) { + const RID &rid = (*r_interpolation_data.camera_transform_update_list_prev)[n]; + Camera *camera = camera_owner.getornull(rid); + + // no longer active? (either the instance deleted or no longer being transformed) + if (camera && !camera->on_interpolate_transform_list) { + camera->transform = camera->transform_prev; + } + } + + // cameras , swap any current with previous + for (unsigned int n = 0; n < r_interpolation_data.camera_transform_update_list_curr->size(); n++) { + const RID &rid = (*r_interpolation_data.camera_transform_update_list_curr)[n]; + Camera *camera = camera_owner.getornull(rid); + if (camera) { + camera->transform_prev = camera->transform; + camera->on_interpolate_transform_list = false; + } + } + + // we maintain a mirror list for the transform updates, so we can detect when an instance + // is no longer being transformed, and remove it from the interpolate list + SWAP(r_interpolation_data.camera_transform_update_list_curr, r_interpolation_data.camera_transform_update_list_prev); + + // prepare for the next iteration + r_interpolation_data.camera_transform_update_list_curr->clear(); +} + +void VisualServerScene::update_interpolate_list(Scenario::InterpolationData &r_interpolation_data, bool p_process) { + // teleported instances + for (unsigned int n = 0; n < r_interpolation_data.instance_teleport_list.size(); n++) { + const RID &rid = r_interpolation_data.instance_teleport_list[n]; + Instance *instance = instance_owner.getornull(rid); + if (instance) { + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + } + } + + r_interpolation_data.instance_teleport_list.clear(); + + // camera teleports + for (unsigned int n = 0; n < r_interpolation_data.camera_teleport_list.size(); n++) { + const RID &rid = r_interpolation_data.camera_teleport_list[n]; + Camera *camera = camera_owner.getornull(rid); + if (camera) { + camera->transform_prev = camera->transform; + } + } + + r_interpolation_data.camera_teleport_list.clear(); + + if (p_process) { + real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + for (unsigned int i = 0; i < r_interpolation_data.instance_interpolate_update_list.size(); i++) { + const RID &rid = r_interpolation_data.instance_interpolate_update_list[i]; + Instance *instance = instance_owner.getornull(rid); + if (instance) { + Interpolator::interpolate_transform(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method); + } + } // for n + } +} + void VisualServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) { Instance *instance = instance_owner.get(p_instance); ERR_FAIL_COND(!instance); @@ -1529,22 +1825,30 @@ void VisualServerScene::instance_geometry_set_as_instance_lod(RID p_instance, RI void VisualServerScene::_update_instance(Instance *p_instance) { p_instance->version++; + // when not using interpolation the transform is used straight + const Transform *instance_xform = &p_instance->transform; + + // but when using interpolation the current transform is the most up to date on the tick + if (p_instance->is_currently_interpolated()) { + instance_xform = &p_instance->transform_curr; + } + if (p_instance->base_type == VS::INSTANCE_LIGHT) { InstanceLightData *light = static_cast(p_instance->base_data); - VSG::scene_render->light_instance_set_transform(light->instance, p_instance->transform); + VSG::scene_render->light_instance_set_transform(light->instance, *instance_xform); light->shadow_dirty = true; } if (p_instance->base_type == VS::INSTANCE_REFLECTION_PROBE) { InstanceReflectionProbeData *reflection_probe = static_cast(p_instance->base_data); - VSG::scene_render->reflection_probe_instance_set_transform(reflection_probe->instance, p_instance->transform); + VSG::scene_render->reflection_probe_instance_set_transform(reflection_probe->instance, *instance_xform); reflection_probe->reflection_dirty = true; } if (p_instance->base_type == VS::INSTANCE_PARTICLES) { - VSG::storage->particles_set_emission_transform(p_instance->base, p_instance->transform); + VSG::storage->particles_set_emission_transform(p_instance->base, *instance_xform); } if (p_instance->base_type == VS::INSTANCE_LIGHTMAP_CAPTURE) { @@ -1579,11 +1883,11 @@ void VisualServerScene::_update_instance(Instance *p_instance) { } } - p_instance->mirror = p_instance->transform.basis.determinant() < 0.0; + p_instance->mirror = instance_xform->basis.determinant() < 0.0; AABB new_aabb; - new_aabb = p_instance->transform.xform(p_instance->aabb); + new_aabb = instance_xform->xform(p_instance->aabb); p_instance->transformed_aabb = new_aabb; @@ -2378,8 +2682,11 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view } break; } - _prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint); - _render_scene(camera->transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1); + // This getter allows optional fixed timestep interpolation for the camera. + Transform camera_transform = camera->get_transform(); + + _prepare_scene(camera_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint); + _render_scene(camera_transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1); #endif } @@ -4032,9 +4339,12 @@ bool VisualServerScene::free(RID p_rid) { if (camera_owner.owns(p_rid)) { Camera *camera = camera_owner.get(p_rid); + if (camera->scenario) { + camera->scenario->_interpolation_data.notify_free_camera(p_rid); + } + camera_owner.free(p_rid); memdelete(camera); - } else if (scenario_owner.owns(p_rid)) { Scenario *scenario = scenario_owner.get(p_rid); @@ -4053,6 +4363,10 @@ bool VisualServerScene::free(RID p_rid) { Instance *instance = instance_owner.get(p_rid); + if (instance->scenario) { + instance->scenario->_interpolation_data.notify_free_instance(p_rid); + } + 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()); @@ -4063,6 +4377,7 @@ bool VisualServerScene::free(RID p_rid) { instance_owner.free(p_rid); memdelete(instance); + } else if (room_owner.owns(p_rid)) { Room *room = room_owner.get(p_rid); room_owner.free(p_rid); diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index ca5b2285dc..99e978b435 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -58,6 +58,7 @@ public: static VisualServerScene *singleton; /* CAMERA API */ + struct Scenario; struct Camera : public RID_Data { enum Type { @@ -71,12 +72,27 @@ public: float size; Vector2 offset; uint32_t visible_layers; - bool vaspect; RID env; + // transform_prev is only used when using fixed timestep interpolation Transform transform; + Transform transform_prev; + + Scenario *scenario; + + bool interpolated : 1; + bool on_interpolate_transform_list : 1; + + bool vaspect : 1; + Interpolator::Method interpolation_method : 3; + int32_t previous_room_id_hint; + // call get transform to get either the transform straight, + // or the interpolated transform if using fixed timestep interpolation + Transform get_transform() const; + bool is_currently_interpolated() const { return scenario && scenario->is_physics_interpolation_enabled() && interpolated; } + Camera() { visible_layers = 0xFFFFFFFF; fov = 70; @@ -86,17 +102,24 @@ public: size = 1.0; offset = Vector2(); vaspect = false; + scenario = nullptr; previous_room_id_hint = -1; + interpolated = true; + on_interpolate_transform_list = false; + interpolation_method = Interpolator::INTERP_LERP; } }; mutable RID_Owner camera_owner; virtual RID camera_create(); + virtual void camera_set_scenario(RID p_camera, RID p_scenario); virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far); virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far); virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far); virtual void camera_set_transform(RID p_camera, const Transform &p_transform); + virtual void camera_set_interpolated(RID p_camera, bool p_interpolated); + virtual void camera_teleport(RID p_camera); virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); virtual void camera_set_environment(RID p_camera, RID p_env); virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); @@ -199,6 +222,26 @@ public: SelfList::List instances; + bool is_physics_interpolation_enabled() const { return _interpolation_data.interpolation_enabled; } + + // fixed timestep interpolation + struct InterpolationData { + void notify_free_camera(RID p_rid); + void notify_free_instance(RID p_rid); + LocalVector instance_interpolate_update_list; + LocalVector instance_transform_update_lists[2]; + LocalVector *instance_transform_update_list_curr = &instance_transform_update_lists[0]; + LocalVector *instance_transform_update_list_prev = &instance_transform_update_lists[1]; + LocalVector instance_teleport_list; + + LocalVector camera_transform_update_lists[2]; + LocalVector *camera_transform_update_list_curr = &camera_transform_update_lists[0]; + LocalVector *camera_transform_update_list_prev = &camera_transform_update_lists[1]; + LocalVector camera_teleport_list; + + bool interpolation_enabled; + } _interpolation_data; + Scenario(); ~Scenario() { memdelete(sps); } }; @@ -214,6 +257,9 @@ public: virtual void scenario_set_environment(RID p_scenario, RID p_environment); virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment); virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_size, int p_subdiv); + virtual void scenario_set_physics_interpolation_enabled(RID p_scenario, bool p_enabled); + void _scenario_tick(RID p_scenario); + void _scenario_pre_draw(RID p_scenario, bool p_will_draw); /* INSTANCING API */ @@ -266,6 +312,8 @@ public: singleton->_instance_queue_update(this, p_aabb, p_materials); } + bool is_currently_interpolated() const { return scenario && scenario->is_physics_interpolation_enabled() && interpolated; } + Instance() : scenario_item(this), update_item(this) { @@ -307,6 +355,7 @@ public: }; SelfList::List _instance_update_list; + void _instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_materials = false); struct InstanceGeometryData : public InstanceBaseData { @@ -517,6 +566,8 @@ public: virtual void instance_set_scenario(RID p_instance, RID p_scenario); virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask); 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_teleport(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); @@ -709,6 +760,10 @@ public: void render_camera(Ref &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); void update_dirty_instances(); + // interpolation + void update_interpolation_transform_list(Scenario::InterpolationData &r_interpolation_data, bool p_process = true); + void update_interpolate_list(Scenario::InterpolationData &r_interpolation_data, bool p_process = true); + //probes struct GIProbeDataHeader { uint32_t version; diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp index 11b157c2fd..6dbe107550 100644 --- a/servers/visual/visual_server_wrap_mt.cpp +++ b/servers/visual/visual_server_wrap_mt.cpp @@ -36,6 +36,18 @@ void VisualServerWrapMT::thread_exit() { exit.set(); } +void VisualServerWrapMT::thread_scenario_tick(RID p_scenario) { + if (!draw_pending.decrement()) { + visual_server->scenario_tick(p_scenario); + } +} + +void VisualServerWrapMT::thread_scenario_pre_draw(RID p_scenario, bool p_will_draw) { + if (!draw_pending.decrement()) { + visual_server->scenario_pre_draw(p_scenario, p_will_draw); + } +} + void VisualServerWrapMT::thread_draw(bool p_swap_buffers, double frame_step) { if (!draw_pending.decrement()) { visual_server->draw(p_swap_buffers, frame_step); @@ -82,6 +94,24 @@ void VisualServerWrapMT::sync() { } } +void VisualServerWrapMT::scenario_tick(RID p_scenario) { + if (create_thread) { + draw_pending.increment(); + command_queue.push(this, &VisualServerWrapMT::thread_scenario_tick, p_scenario); + } else { + visual_server->scenario_tick(p_scenario); + } +} + +void VisualServerWrapMT::scenario_pre_draw(RID p_scenario, bool p_will_draw) { + if (create_thread) { + draw_pending.increment(); + command_queue.push(this, &VisualServerWrapMT::thread_scenario_pre_draw, p_scenario, p_will_draw); + } else { + visual_server->scenario_pre_draw(p_scenario, p_will_draw); + } +} + void VisualServerWrapMT::draw(bool p_swap_buffers, double frame_step) { if (create_thread) { draw_pending.increment(); diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index fe252639bb..01ec65f670 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -54,6 +54,8 @@ class VisualServerWrapMT : public VisualServer { SafeNumeric draw_pending; void thread_draw(bool p_swap_buffers, double frame_step); void thread_flush(); + void thread_scenario_tick(RID p_scenario); + void thread_scenario_pre_draw(RID p_scenario, bool p_will_draw); void thread_exit(); @@ -367,10 +369,13 @@ public: /* CAMERA API */ FUNCRID(camera) + FUNC2(camera_set_scenario, RID, RID) FUNC4(camera_set_perspective, RID, float, float, float) FUNC4(camera_set_orthogonal, RID, float, float, float) FUNC5(camera_set_frustum, RID, float, Vector2, float, float) FUNC2(camera_set_transform, RID, const Transform &) + FUNC2(camera_set_interpolated, RID, bool) + FUNC1(camera_teleport, RID) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) @@ -463,6 +468,7 @@ public: FUNC2(scenario_set_environment, RID, RID) FUNC3(scenario_set_reflection_atlas_size, RID, int, int) FUNC2(scenario_set_fallback_environment, RID, RID) + FUNC2(scenario_set_physics_interpolation_enabled, RID, bool) /* INSTANCING API */ FUNCRID(instance) @@ -471,6 +477,8 @@ public: FUNC2(instance_set_scenario, RID, RID) FUNC2(instance_set_layer_mask, RID, uint32_t) FUNC2(instance_set_transform, RID, const Transform &) + FUNC2(instance_set_interpolated, RID, bool) + FUNC1(instance_teleport, 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) @@ -653,6 +661,8 @@ public: virtual void finish(); virtual void draw(bool p_swap_buffers, double frame_step); virtual void sync(); + virtual void scenario_tick(RID p_scenario); + virtual void scenario_pre_draw(RID p_scenario, bool p_will_draw); FUNC0RC(bool, has_changed) /* RENDER INFO */ diff --git a/servers/visual_server.h b/servers/visual_server.h index b6da7a9136..3146e62a41 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -613,10 +613,13 @@ public: /* CAMERA API */ virtual RID camera_create() = 0; + virtual void camera_set_scenario(RID p_camera, RID p_scenario) = 0; virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) = 0; virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0; virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0; virtual void camera_set_transform(RID p_camera, const Transform &p_transform) = 0; + virtual void camera_set_interpolated(RID p_camera, bool p_interpolated) = 0; + virtual void camera_teleport(RID p_camera) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; @@ -827,6 +830,7 @@ public: virtual void scenario_set_environment(RID p_scenario, RID p_environment) = 0; virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_size, int p_subdiv) = 0; virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment) = 0; + virtual void scenario_set_physics_interpolation_enabled(RID p_scenario, bool p_enabled) = 0; /* INSTANCING API */ @@ -854,6 +858,8 @@ public: virtual void instance_set_scenario(RID p_instance, RID p_scenario) = 0; virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 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_teleport(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; @@ -1103,6 +1109,8 @@ public: virtual bool has_changed() const = 0; virtual void init() = 0; virtual void finish() = 0; + virtual void scenario_tick(RID p_scenario) = 0; + virtual void scenario_pre_draw(RID p_scenario, bool p_will_draw) = 0; /* STATUS INFORMATION */