This commit is contained in:
lawnjelly 2021-11-11 11:26:42 +00:00 committed by GitHub
commit 9057332f73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 810 additions and 45 deletions

View file

@ -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]);

View file

@ -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;
}

56
core/math/interpolator.h Normal file
View file

@ -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

View file

@ -675,6 +675,11 @@
Sets whether this is an instance load placeholder. See [InstancePlaceholder].
</description>
</method>
<method name="teleport">
<return type="void" />
<description>
</description>
</method>
<method name="update_configuration_warning">
<return type="void" />
<description>
@ -787,6 +792,8 @@
<constant name="NOTIFICATION_POST_ENTER_TREE" value="27">
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.
</constant>
<constant name="NOTIFICATION_TELEPORT" value="28">
</constant>
<constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
Notification received from the OS when the mouse enters the game window.
Implemented on desktop and web platforms.

View file

@ -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.
</member>
<member name="physics/common/physics_interpolation" type="bool" setter="" getter="" default="true">
</member>
<member name="physics/common/physics_jitter_fix" type="float" setter="" getter="" default="0.5">
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].

View file

@ -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();
}

View file

@ -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();

View file

@ -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;

View file

@ -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;
}

View file

@ -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();

View file

@ -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;

View file

@ -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<StringName, MultiplayerAPI::RPCMode> rpc_methods;
Map<StringName, MultiplayerAPI::RPCMode> 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();

View file

@ -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<World>(memnew(World)));
}
set_physics_interpolation_enabled(GLOBAL_DEF("physics/common/physics_interpolation", true));
// Initialize network state
multiplayer_poll = true;

View file

@ -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();

View file

@ -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;
}
};

View file

@ -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");

View file

@ -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 */

View file

@ -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<InstanceLightData *>(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<InstanceReflectionProbeData *>(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);

View file

@ -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> 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<Instance>::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<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];
LocalVector<RID> instance_teleport_list;
LocalVector<RID> camera_transform_update_lists[2];
LocalVector<RID> *camera_transform_update_list_curr = &camera_transform_update_lists[0];
LocalVector<RID> *camera_transform_update_list_prev = &camera_transform_update_lists[1];
LocalVector<RID> 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<Instance>::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<ARVRInterface> &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;

View file

@ -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();

View file

@ -54,6 +54,8 @@ class VisualServerWrapMT : public VisualServer {
SafeNumeric<uint64_t> 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 */

View file

@ -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 */