Physics Interpolation - refactor Camera and fix get_camera_transform()

* Moves 3D Camera interpolation scene side.
* Automatically switches `get_camera_transform()` to report interpolated transform during `_process()`.
* Fixes `ClippedCamera` to work with physics interpolation.
This commit is contained in:
lawnjelly
2024-06-05 07:45:03 +01:00
parent b203829361
commit 0b30d77384
20 changed files with 359 additions and 274 deletions

View File

@ -361,11 +361,7 @@ void RasterizerStorage::multimesh_instance_set_transform(RID p_multimesh, int p_
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign).");
}
PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process");
}
#endif
return;
@ -521,11 +517,7 @@ void RasterizerStorage::multimesh_set_as_bulk_array_interpolated(RID p_multimesh
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign).");
}
PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process");
}
#endif
}
@ -541,11 +533,7 @@ void RasterizerStorage::multimesh_set_as_bulk_array(RID p_multimesh, const PoolV
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("[Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign).");
}
PHYSICS_INTERPOLATION_WARNING("Interpolated MultiMesh triggered from outside physics process");
}
#endif
return;

View File

@ -53,6 +53,9 @@
// This is expensive.
// #define VISUAL_SERVER_CANVAS_CHECK_BOUNDS
// Uncomment this define to produce debugging output for physics interpolation.
// #define VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
#endif // DEV_ENABLED
#endif // VISUAL_SERVER_CONSTANTS_H

View File

@ -461,8 +461,6 @@ public:
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_reset_physics_interpolation, RID)
BIND2(camera_set_cull_mask, RID, uint32_t)
BIND2(camera_set_environment, RID, RID)
BIND2(camera_set_use_vertical_aspect, RID, bool)

View File

@ -40,16 +40,6 @@
/* CAMERA API */
Transform VisualServerScene::Camera::get_transform_interpolated() const {
if (!interpolated) {
return transform;
}
Transform final;
TransformInterpolator::interpolate_transform_via_method(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);
@ -83,59 +73,11 @@ void VisualServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p
camera->zfar = p_z_far;
}
void VisualServerScene::camera_reset_physics_interpolation(RID p_camera) {
Camera *camera = camera_owner.get(p_camera);
ERR_FAIL_COND(!camera);
if (_interpolation_data.interpolation_enabled && camera->interpolated) {
_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 (_interpolation_data.interpolation_enabled) {
if (camera->interpolated) {
if (!camera->on_interpolate_transform_list) {
_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
camera->interpolation_method = TransformInterpolator::find_method(camera->transform_prev.basis, camera->transform.basis);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
// Effectively a WARN_PRINT_ONCE but after a certain number of occurrences.
static int32_t warn_count = -256;
if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("[Physics interpolation] Camera interpolation is being triggered from outside physics process, this might lead to issues (possibly benign).");
}
warn_count++;
}
#endif
} else {
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = -256;
if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("[Physics interpolation] Non-interpolated Camera is being triggered from physics process, this might lead to issues (possibly benign).");
}
warn_count++;
}
#endif
}
}
}
void VisualServerScene::camera_set_cull_mask(RID p_camera, uint32_t p_layers) {
@ -828,7 +770,13 @@ void VisualServerScene::instance_reset_physics_interpolation(RID p_instance) {
ERR_FAIL_COND(!instance);
if (_interpolation_data.interpolation_enabled && instance->interpolated) {
_interpolation_data.instance_teleport_list.push_back(p_instance);
instance->transform_prev = instance->transform_curr;
instance->transform_checksum_prev = instance->transform_checksum_curr;
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames()));
print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x));
#endif
}
}
@ -842,6 +790,10 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames()));
#endif
if (!(_interpolation_data.interpolation_enabled && instance->interpolated) || !instance->scenario) {
if (instance->transform == p_transform) {
return; //must be checked to avoid worst evil
@ -865,24 +817,7 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if ((_interpolation_data.interpolation_enabled && !instance->interpolated) && (Engine::get_singleton()->is_in_physics_frame())) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
String node_name;
ObjectID id = instance->object_id;
if (id != 0) {
if (ObjectDB::get_instance(id)) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
if (node && node->is_inside_tree()) {
node_name = "\"" + String(node->get_path()) + "\"";
} else {
node_name = "\"unknown\"";
}
}
}
WARN_PRINT("[Physics interpolation] Non-interpolated Instance is being triggered from physics process, this might lead to issues: " + node_name + " (possibly benign).");
}
PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Non-interpolated triggered from physics process");
}
#endif
@ -918,6 +853,10 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
instance->transform_curr = p_transform;
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x));
#endif
// keep checksums up to date
instance->transform_checksum_curr = new_checksum;
@ -950,41 +889,11 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
String node_name;
ObjectID id = instance->object_id;
if (id != 0) {
if (ObjectDB::get_instance(id)) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
if (node && node->is_inside_tree()) {
node_name = "\"" + String(node->get_path()) + "\"";
} else {
node_name = "\"unknown\"";
}
}
}
WARN_PRINT("[Physics interpolation] Instance interpolation is being triggered from outside physics process, this might lead to issues: " + node_name + " (possibly benign).");
}
PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Interpolated triggered from outside physics process");
}
#endif
}
void VisualServerScene::InterpolationData::notify_free_camera(RID p_rid, Camera &r_camera) {
r_camera.on_interpolate_transform_list = false;
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::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) {
r_instance.on_interpolate_list = false;
r_instance.on_interpolate_transform_list = false;
@ -997,10 +906,13 @@ void VisualServerScene::InterpolationData::notify_free_instance(RID p_rid, Insta
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_tick(bool p_process) {
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames()));
#endif
// update interpolation in storage
VSG::storage->update_interpolation_tick(p_process);
@ -1057,64 +969,12 @@ void VisualServerScene::update_interpolation_tick(bool p_process) {
// prepare for the next iteration
_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 < _interpolation_data.camera_transform_update_list_prev->size(); n++) {
const RID &rid = (*_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 < _interpolation_data.camera_transform_update_list_curr->size(); n++) {
const RID &rid = (*_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(_interpolation_data.camera_transform_update_list_curr, _interpolation_data.camera_transform_update_list_prev);
// prepare for the next iteration
_interpolation_data.camera_transform_update_list_curr->clear();
}
void VisualServerScene::update_interpolation_frame(bool p_process) {
// update interpolation in storage
VSG::storage->update_interpolation_frame(p_process);
// teleported instances
for (unsigned int n = 0; n < _interpolation_data.instance_teleport_list.size(); n++) {
const RID &rid = _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;
}
}
_interpolation_data.instance_teleport_list.clear();
// camera teleports
for (unsigned int n = 0; n < _interpolation_data.camera_teleport_list.size(); n++) {
const RID &rid = _interpolation_data.camera_teleport_list[n];
Camera *camera = camera_owner.getornull(rid);
if (camera) {
camera->transform_prev = camera->transform;
}
}
_interpolation_data.camera_teleport_list.clear();
if (p_process) {
real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
@ -1124,6 +984,10 @@ void VisualServerScene::update_interpolation_frame(bool p_process) {
if (instance) {
TransformInterpolator::interpolate_transform_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method);
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames()));
#endif
// make sure AABBs are constantly up to date through the interpolation
_instance_queue_update(instance, true);
}
@ -2930,10 +2794,8 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view
} break;
}
Transform camera_transform = _interpolation_data.interpolation_enabled ? camera->get_transform_interpolated() : camera->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);
_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
}
@ -4615,8 +4477,6 @@ void VisualServerScene::update_dirty_instances() {
bool VisualServerScene::free(RID p_rid) {
if (camera_owner.owns(p_rid)) {
Camera *camera = camera_owner.get(p_rid);
_interpolation_data.notify_free_camera(p_rid, *camera);
camera_owner.free(p_rid);
memdelete(camera);
} else if (scenario_owner.owns(p_rid)) {

View File

@ -81,20 +81,12 @@ public:
uint32_t visible_layers;
RID env;
// transform_prev is only used when using fixed timestep interpolation
Transform transform;
Transform transform_prev;
bool interpolated : 1;
bool on_interpolate_transform_list : 1;
bool vaspect : 1;
TransformInterpolator::Method interpolation_method : 3;
int32_t previous_room_id_hint;
Transform get_transform_interpolated() const;
Camera() {
visible_layers = 0xFFFFFFFF;
fov = 70;
@ -105,9 +97,6 @@ public:
offset = Vector2();
vaspect = false;
previous_room_id_hint = -1;
interpolated = true;
on_interpolate_transform_list = false;
interpolation_method = TransformInterpolator::INTERP_LERP;
}
};
@ -118,8 +107,6 @@ public:
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_reset_physics_interpolation(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);
@ -410,18 +397,11 @@ public:
virtual void set_physics_interpolation_enabled(bool p_enabled);
struct InterpolationData {
void notify_free_camera(RID p_rid, Camera &r_camera);
void notify_free_instance(RID p_rid, Instance &r_instance);
LocalVector<RID> instance_interpolate_update_list;
LocalVector<RID> instance_transform_update_lists[2];
LocalVector<RID> *instance_transform_update_list_curr = &instance_transform_update_lists[0];
LocalVector<RID> *instance_transform_update_list_prev = &instance_transform_update_lists[1];
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 = false;
} _interpolation_data;

View File

@ -375,8 +375,6 @@ public:
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_reset_physics_interpolation, RID)
FUNC2(camera_set_cull_mask, RID, uint32_t)
FUNC2(camera_set_environment, RID, RID)
FUNC2(camera_set_use_vertical_aspect, RID, bool)

View File

@ -2095,8 +2095,6 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("camera_set_orthogonal", "camera", "size", "z_near", "z_far"), &VisualServer::camera_set_orthogonal);
ClassDB::bind_method(D_METHOD("camera_set_frustum", "camera", "size", "offset", "z_near", "z_far"), &VisualServer::camera_set_frustum);
ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &VisualServer::camera_set_transform);
ClassDB::bind_method(D_METHOD("camera_set_interpolated", "camera", "interpolated"), &VisualServer::camera_set_interpolated);
ClassDB::bind_method(D_METHOD("camera_reset_physics_interpolation", "camera"), &VisualServer::camera_reset_physics_interpolation);
ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &VisualServer::camera_set_cull_mask);
ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &VisualServer::camera_set_environment);
ClassDB::bind_method(D_METHOD("camera_set_use_vertical_aspect", "camera", "enable"), &VisualServer::camera_set_use_vertical_aspect);

View File

@ -634,8 +634,6 @@ public:
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_reset_physics_interpolation(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;