mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-04-07 12:31:49 +02:00
Physics Interpolation - Move 3D FTI to SceneTree
Moves 3D interpolation from `VisualServer` to the client code (`SceneTree`). Complete rework of 3D physics interpolation, but using the same user API.
This commit is contained in:
parent
dedeec9492
commit
8a735a3c02
@ -105,6 +105,15 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool erase_unordered(const T &p_val) {
|
||||||
|
int64_t idx = find(p_val);
|
||||||
|
if (idx >= 0) {
|
||||||
|
remove_unordered(idx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
U erase_multiple_unordered(const T &p_val) {
|
U erase_multiple_unordered(const T &p_val) {
|
||||||
U from = 0;
|
U from = 0;
|
||||||
U count = 0;
|
U count = 0;
|
||||||
|
@ -1476,14 +1476,6 @@
|
|||||||
Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance.material_override].
|
Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance.material_override].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="instance_reset_physics_interpolation">
|
|
||||||
<return type="void" />
|
|
||||||
<argument index="0" name="instance" type="RID" />
|
|
||||||
<description>
|
|
||||||
Prevents physics interpolation for the current physics tick.
|
|
||||||
This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location.
|
|
||||||
</description>
|
|
||||||
</method>
|
|
||||||
<method name="instance_set_base">
|
<method name="instance_set_base">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="instance" type="RID" />
|
<argument index="0" name="instance" type="RID" />
|
||||||
@ -1525,14 +1517,6 @@
|
|||||||
Sets a margin to increase the size of the AABB when culling objects from the view frustum. This allows you to avoid culling objects that fall outside the view frustum. Equivalent to [member GeometryInstance.extra_cull_margin].
|
Sets a margin to increase the size of the AABB when culling objects from the view frustum. This allows you to avoid culling objects that fall outside the view frustum. Equivalent to [member GeometryInstance.extra_cull_margin].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="instance_set_interpolated">
|
|
||||||
<return type="void" />
|
|
||||||
<argument index="0" name="instance" type="RID" />
|
|
||||||
<argument index="1" name="interpolated" type="bool" />
|
|
||||||
<description>
|
|
||||||
Turns on and off physics interpolation for the instance.
|
|
||||||
</description>
|
|
||||||
</method>
|
|
||||||
<method name="instance_set_layer_mask">
|
<method name="instance_set_layer_mask">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="instance" type="RID" />
|
<argument index="0" name="instance" type="RID" />
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
[b]Note:[/b] The simulation does not take the effect of gears into account, you will need to add logic for this if you wish to simulate gears.
|
[b]Note:[/b] The simulation does not take the effect of gears into account, you will need to add logic for this if you wish to simulate gears.
|
||||||
A negative value will result in the wheel reversing.
|
A negative value will result in the wheel reversing.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
|
||||||
<member name="steering" type="float" setter="set_steering" getter="get_steering" default="0.0">
|
<member name="steering" type="float" setter="set_steering" getter="get_steering" default="0.0">
|
||||||
The steering angle for the wheel. Setting this to a non-zero value will result in the vehicle turning when it's moving.
|
The steering angle for the wheel. Setting this to a non-zero value will result in the vehicle turning when it's moving.
|
||||||
</member>
|
</member>
|
||||||
|
@ -39,3 +39,4 @@ MainLoop *test();
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // TEST_EXPRESSION_H
|
#endif // TEST_EXPRESSION_H
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="""">
|
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="""">
|
||||||
The name of the attached bone.
|
The name of the attached bone.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
|
||||||
</members>
|
</members>
|
||||||
<constants>
|
<constants>
|
||||||
</constants>
|
</constants>
|
||||||
|
@ -187,6 +187,7 @@ String BoneAttachment::get_configuration_warning() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BoneAttachment::BoneAttachment() {
|
BoneAttachment::BoneAttachment() {
|
||||||
|
set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF);
|
||||||
bound = false;
|
bound = false;
|
||||||
bone_idx = -1;
|
bone_idx = -1;
|
||||||
override_pose = false;
|
override_pose = false;
|
||||||
|
@ -279,7 +279,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
|
|||||||
p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, false);
|
p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, false);
|
||||||
Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin;
|
Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin;
|
||||||
|
|
||||||
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
|
make_goal(p_task, p_task->skeleton->get_global_transform_interpolated().affine_inverse(), blending_delta);
|
||||||
|
|
||||||
if (p_use_magnet && p_task->chain.middle_chain_item) {
|
if (p_use_magnet && p_task->chain.middle_chain_item) {
|
||||||
p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta);
|
p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta);
|
||||||
|
@ -50,6 +50,13 @@ void Camera::_request_camera_update() {
|
|||||||
_update_camera();
|
_update_camera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Camera::fti_update_servers() {
|
||||||
|
if (camera.is_valid()) {
|
||||||
|
Transform tr = _get_adjusted_camera_transform(_get_cached_global_transform_interpolated());
|
||||||
|
VisualServer::get_singleton()->camera_set_transform(camera, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Camera::_update_camera_mode() {
|
void Camera::_update_camera_mode() {
|
||||||
force_change = true;
|
force_change = true;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@ -90,12 +97,8 @@ void Camera::_update_camera() {
|
|||||||
if (!is_physics_interpolated_and_enabled()) {
|
if (!is_physics_interpolated_and_enabled()) {
|
||||||
RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform());
|
RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform());
|
||||||
} else {
|
} else {
|
||||||
// Ideally we shouldn't be moving a physics interpolated camera within a frame,
|
// Force a refresh next frame.
|
||||||
// because it will break smooth interpolation, but it may occur on e.g. level load.
|
fti_notify_node_changed();
|
||||||
if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) {
|
|
||||||
_physics_interpolation_ensure_transform_calculated(true);
|
|
||||||
RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// here goes listener stuff
|
// here goes listener stuff
|
||||||
@ -119,40 +122,6 @@ void Camera::_physics_interpolated_changed() {
|
|||||||
_update_process_mode();
|
_update_process_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::_physics_interpolation_ensure_data_flipped() {
|
|
||||||
// The curr -> previous update can either occur
|
|
||||||
// on the INTERNAL_PHYSICS_PROCESS OR
|
|
||||||
// on NOTIFICATION_TRANSFORM_CHANGED,
|
|
||||||
// if NOTIFICATION_TRANSFORM_CHANGED takes place
|
|
||||||
// earlier than INTERNAL_PHYSICS_PROCESS on a tick.
|
|
||||||
// This is to ensure that the data keeps flowing, but the new data
|
|
||||||
// doesn't overwrite before prev has been set.
|
|
||||||
|
|
||||||
// Keep the data flowing.
|
|
||||||
uint64_t tick = Engine::get_singleton()->get_physics_frames();
|
|
||||||
if (_interpolation_data.last_update_physics_tick != tick) {
|
|
||||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
|
||||||
_interpolation_data.last_update_physics_tick = tick;
|
|
||||||
physics_interpolation_flip_data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::_physics_interpolation_ensure_transform_calculated(bool p_force) const {
|
|
||||||
DEV_CHECK_ONCE(!Engine::get_singleton()->is_in_physics_frame());
|
|
||||||
|
|
||||||
InterpolationData &id = _interpolation_data;
|
|
||||||
uint64_t frame = Engine::get_singleton()->get_frames_drawn();
|
|
||||||
|
|
||||||
if (id.last_update_frame != frame || p_force) {
|
|
||||||
id.last_update_frame = frame;
|
|
||||||
|
|
||||||
TransformInterpolator::interpolate_transform(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction());
|
|
||||||
|
|
||||||
Transform &tr = id.camera_xform_interpolated;
|
|
||||||
tr = _get_adjusted_camera_transform(id.xform_interpolated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) {
|
void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) {
|
||||||
_desired_process_internal = p_process_internal;
|
_desired_process_internal = p_process_internal;
|
||||||
_desired_physics_process_internal = p_physics_process_internal;
|
_desired_physics_process_internal = p_physics_process_internal;
|
||||||
@ -160,17 +129,8 @@ void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_p
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Camera::_update_process_mode() {
|
void Camera::_update_process_mode() {
|
||||||
bool process = _desired_process_internal;
|
set_process_internal(_desired_process_internal);
|
||||||
bool physics_process = _desired_physics_process_internal;
|
set_physics_process_internal(_desired_physics_process_internal);
|
||||||
|
|
||||||
if (is_physics_interpolated_and_enabled()) {
|
|
||||||
if (is_current()) {
|
|
||||||
process = true;
|
|
||||||
physics_process = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set_process_internal(process);
|
|
||||||
set_physics_process_internal(physics_process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::_notification(int p_what) {
|
void Camera::_notification(int p_what) {
|
||||||
@ -187,50 +147,21 @@ void Camera::_notification(int p_what) {
|
|||||||
world->_camera_set(this);
|
world->_camera_set(this);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
|
||||||
if (is_physics_interpolated_and_enabled() && camera.is_valid()) {
|
|
||||||
_physics_interpolation_ensure_transform_calculated();
|
|
||||||
|
|
||||||
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
|
|
||||||
print_line("\t\tinterpolated Camera: " + rtos(_interpolation_data.xform_interpolated.origin.x) + "\t( prev " + rtos(_interpolation_data.xform_prev.origin.x) + ", curr " + rtos(_interpolation_data.xform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
|
||||||
if (is_physics_interpolated_and_enabled()) {
|
|
||||||
_physics_interpolation_ensure_data_flipped();
|
|
||||||
_interpolation_data.xform_curr = get_global_transform();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||||
if (is_physics_interpolated_and_enabled()) {
|
|
||||||
_physics_interpolation_ensure_data_flipped();
|
|
||||||
_interpolation_data.xform_curr = get_global_transform();
|
|
||||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
|
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
|
||||||
|
if (is_physics_interpolated_and_enabled()) {
|
||||||
if (!Engine::get_singleton()->is_in_physics_frame()) {
|
if (!Engine::get_singleton()->is_in_physics_frame()) {
|
||||||
PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera triggered from outside physics process");
|
PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera triggered from outside physics process");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
_request_camera_update();
|
_request_camera_update();
|
||||||
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
|
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
|
||||||
velocity_tracker->update_position(get_global_transform().origin);
|
velocity_tracker->update_position(get_global_transform().origin);
|
||||||
}
|
}
|
||||||
// Allow auto-reset when first adding to the tree, as a convenience.
|
|
||||||
if (_is_physics_interpolation_reset_requested() && is_inside_tree()) {
|
|
||||||
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
|
|
||||||
_set_physics_interpolation_reset_requested(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
||||||
if (is_inside_tree()) {
|
_update_process_mode();
|
||||||
_interpolation_data.xform_curr = get_global_transform();
|
|
||||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
|
||||||
_update_process_mode();
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_EXIT_WORLD: {
|
case NOTIFICATION_EXIT_WORLD: {
|
||||||
if (!get_tree()->is_node_being_edited(this)) {
|
if (!get_tree()->is_node_being_edited(this)) {
|
||||||
@ -273,8 +204,7 @@ Transform Camera::_get_adjusted_camera_transform(const Transform &p_xform) const
|
|||||||
|
|
||||||
Transform Camera::get_camera_transform() const {
|
Transform Camera::get_camera_transform() const {
|
||||||
if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) {
|
if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) {
|
||||||
_physics_interpolation_ensure_transform_calculated();
|
return _get_adjusted_camera_transform(_get_cached_global_transform_interpolated());
|
||||||
return _interpolation_data.camera_xform_interpolated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _get_adjusted_camera_transform(get_global_transform());
|
return _get_adjusted_camera_transform(get_global_transform());
|
||||||
@ -864,9 +794,16 @@ float ClippedCamera::get_margin() const {
|
|||||||
return margin;
|
return margin;
|
||||||
}
|
}
|
||||||
void ClippedCamera::set_process_mode(ProcessMode p_mode) {
|
void ClippedCamera::set_process_mode(ProcessMode p_mode) {
|
||||||
if (is_physics_interpolated_and_enabled() && p_mode == CLIP_PROCESS_IDLE) {
|
if (is_physics_interpolated_and_enabled()) {
|
||||||
p_mode = CLIP_PROCESS_PHYSICS;
|
if (p_mode == CLIP_PROCESS_IDLE) {
|
||||||
WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode.");
|
p_mode = CLIP_PROCESS_PHYSICS;
|
||||||
|
WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
process_mode = p_mode;
|
||||||
|
|
||||||
|
set_desired_process_modes(false, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process_mode == p_mode) {
|
if (process_mode == p_mode) {
|
||||||
@ -880,8 +817,11 @@ ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const {
|
|||||||
return process_mode;
|
return process_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClippedCamera::physics_interpolation_flip_data() {
|
void ClippedCamera::fti_pump() {
|
||||||
_interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr;
|
_interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr;
|
||||||
|
|
||||||
|
// Must call the base class.
|
||||||
|
Spatial::fti_pump();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClippedCamera::_physics_interpolated_changed() {
|
void ClippedCamera::_physics_interpolated_changed() {
|
||||||
@ -898,6 +838,11 @@ Transform ClippedCamera::_get_adjusted_camera_transform(const Transform &p_xform
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClippedCamera::fti_update_servers() {
|
||||||
|
clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev;
|
||||||
|
Camera::fti_update_servers();
|
||||||
|
}
|
||||||
|
|
||||||
void ClippedCamera::_notification(int p_what) {
|
void ClippedCamera::_notification(int p_what) {
|
||||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
if (p_what == NOTIFICATION_ENTER_TREE) {
|
||||||
// Switch process mode to physics if we are turning on interpolation.
|
// Switch process mode to physics if we are turning on interpolation.
|
||||||
@ -965,10 +910,6 @@ void ClippedCamera::_notification(int p_what) {
|
|||||||
_update_camera();
|
_update_camera();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_physics_interpolated_and_enabled() && (p_what == NOTIFICATION_INTERNAL_PROCESS)) {
|
|
||||||
clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
|
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
|
||||||
update_gizmos();
|
update_gizmos();
|
||||||
}
|
}
|
||||||
|
@ -96,26 +96,10 @@ private:
|
|||||||
Ref<SpatialVelocityTracker> velocity_tracker;
|
Ref<SpatialVelocityTracker> velocity_tracker;
|
||||||
bool affect_lod = true;
|
bool affect_lod = true;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
// These can be set by derived Cameras.
|
||||||
// INTERPOLATION FUNCTIONS
|
|
||||||
void _physics_interpolation_ensure_transform_calculated(bool p_force = false) const;
|
|
||||||
void _physics_interpolation_ensure_data_flipped();
|
|
||||||
|
|
||||||
// These can be set by derived Cameras,
|
|
||||||
// if they wish to do processing (while still
|
|
||||||
// allowing physics interpolation to function).
|
|
||||||
bool _desired_process_internal = false;
|
bool _desired_process_internal = false;
|
||||||
bool _desired_physics_process_internal = false;
|
bool _desired_physics_process_internal = false;
|
||||||
|
|
||||||
mutable struct InterpolationData {
|
|
||||||
Transform xform_curr;
|
|
||||||
Transform xform_prev;
|
|
||||||
Transform xform_interpolated;
|
|
||||||
Transform camera_xform_interpolated; // After modification according to camera type.
|
|
||||||
uint32_t last_update_physics_tick = 0;
|
|
||||||
uint32_t last_update_frame = UINT32_MAX;
|
|
||||||
} _interpolation_data;
|
|
||||||
|
|
||||||
void _update_process_mode();
|
void _update_process_mode();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -123,9 +107,6 @@ protected:
|
|||||||
// This is because physics interpolation may need to request process modes additionally.
|
// This is because physics interpolation may need to request process modes additionally.
|
||||||
void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal);
|
void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal);
|
||||||
|
|
||||||
// Opportunity for derived classes to interpolate extra attributes.
|
|
||||||
virtual void physics_interpolation_flip_data() {}
|
|
||||||
|
|
||||||
virtual void _physics_interpolated_changed();
|
virtual void _physics_interpolated_changed();
|
||||||
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
|
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
@ -133,6 +114,7 @@ protected:
|
|||||||
void _update_camera();
|
void _update_camera();
|
||||||
virtual void _request_camera_update();
|
virtual void _request_camera_update();
|
||||||
void _update_camera_mode();
|
void _update_camera_mode();
|
||||||
|
virtual void fti_update_servers();
|
||||||
|
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
virtual void _validate_property(PropertyInfo &p_property) const;
|
virtual void _validate_property(PropertyInfo &p_property) const;
|
||||||
@ -253,8 +235,9 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
|
virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const;
|
||||||
virtual void physics_interpolation_flip_data();
|
virtual void fti_pump();
|
||||||
virtual void _physics_interpolated_changed();
|
virtual void _physics_interpolated_changed();
|
||||||
|
virtual void fti_update_servers();
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
@ -47,7 +47,7 @@ public:
|
|||||||
|
|
||||||
real_t getDiagonal() const { return m_Adiag; }
|
real_t getDiagonal() const { return m_Adiag; }
|
||||||
|
|
||||||
btVehicleJacobianEntry() {};
|
btVehicleJacobianEntry() {}
|
||||||
//constraint between two different rigidbodies
|
//constraint between two different rigidbodies
|
||||||
btVehicleJacobianEntry(
|
btVehicleJacobianEntry(
|
||||||
const Basis &world2A,
|
const Basis &world2A,
|
||||||
@ -369,6 +369,8 @@ VehicleWheel::VehicleWheel() {
|
|||||||
m_raycastInfo.m_suspensionLength = 0.0;
|
m_raycastInfo.m_suspensionLength = 0.0;
|
||||||
|
|
||||||
body = nullptr;
|
body = nullptr;
|
||||||
|
|
||||||
|
set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VehicleBody::_update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s) {
|
void VehicleBody::_update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s) {
|
||||||
|
@ -85,6 +85,12 @@ void VisualInstance::set_instance_use_identity_transform(bool p_enable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VisualInstance::fti_update_servers() {
|
||||||
|
if (!_is_using_identity_transform()) {
|
||||||
|
VisualServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VisualInstance::_notification(int p_what) {
|
void VisualInstance::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ENTER_WORLD: {
|
case NOTIFICATION_ENTER_WORLD: {
|
||||||
@ -100,33 +106,26 @@ void VisualInstance::_notification(int p_what) {
|
|||||||
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||||
if (_is_vi_visible() || is_physics_interpolated_and_enabled()) {
|
if (_is_vi_visible()) {
|
||||||
if (!_is_using_identity_transform()) {
|
if (!_is_using_identity_transform()) {
|
||||||
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
|
// Physics interpolated VIs don't need to send their transform immediately after setting,
|
||||||
|
// indeed it is counterproductive, because the interpolated transform will be sent
|
||||||
// For instance when first adding to the tree, when the previous transform is
|
// to the VisualServer immediately prior to rendering.
|
||||||
// unset, to prevent streaking from the origin.
|
if (!is_physics_interpolated_and_enabled()) {
|
||||||
if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) {
|
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
|
||||||
if (_is_vi_visible()) {
|
} else {
|
||||||
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
|
// For instance when first adding to the tree, when the previous transform is
|
||||||
|
// unset, to prevent streaking from the origin.
|
||||||
|
if (_is_physics_interpolation_reset_requested() && is_inside_tree()) {
|
||||||
|
if (_is_vi_visible()) {
|
||||||
|
_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
|
||||||
|
}
|
||||||
|
_set_physics_interpolation_reset_requested(false);
|
||||||
}
|
}
|
||||||
_set_physics_interpolation_reset_requested(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
|
||||||
if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) {
|
|
||||||
// We must ensure the VisualServer transform is up to date before resetting.
|
|
||||||
// This is because NOTIFICATION_TRANSFORM_CHANGED is deferred,
|
|
||||||
// and cannot be relied to be called in order before NOTIFICATION_RESET_PHYSICS_INTERPOLATION.
|
|
||||||
if (!_is_using_identity_transform()) {
|
|
||||||
RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform());
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case NOTIFICATION_EXIT_WORLD: {
|
case NOTIFICATION_EXIT_WORLD: {
|
||||||
RenderingServer::get_singleton()->instance_set_scenario(instance, RID());
|
RenderingServer::get_singleton()->instance_set_scenario(instance, RID());
|
||||||
RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID());
|
RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID());
|
||||||
@ -142,10 +141,6 @@ void VisualInstance::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualInstance::_physics_interpolated_changed() {
|
|
||||||
RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated());
|
|
||||||
}
|
|
||||||
|
|
||||||
RID VisualInstance::get_instance() const {
|
RID VisualInstance::get_instance() const {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ class VisualInstance : public CullInstance {
|
|||||||
protected:
|
protected:
|
||||||
void _update_visibility();
|
void _update_visibility();
|
||||||
virtual void _refresh_portal_mode();
|
virtual void _refresh_portal_mode();
|
||||||
virtual void _physics_interpolated_changed();
|
|
||||||
void set_instance_use_identity_transform(bool p_enable);
|
void set_instance_use_identity_transform(bool p_enable);
|
||||||
|
virtual void fti_update_servers();
|
||||||
|
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
@ -592,6 +592,8 @@ void SceneTree::set_physics_interpolation_enabled(bool p_enabled) {
|
|||||||
|
|
||||||
RenderingServer::get_singleton()->set_physics_interpolation_enabled(p_enabled);
|
RenderingServer::get_singleton()->set_physics_interpolation_enabled(p_enabled);
|
||||||
|
|
||||||
|
get_scene_tree_fti().set_enabled(get_root(), p_enabled);
|
||||||
|
|
||||||
// Perform an auto reset on the root node for convenience for the user.
|
// Perform an auto reset on the root node for convenience for the user.
|
||||||
if (root) {
|
if (root) {
|
||||||
root->reset_physics_interpolation();
|
root->reset_physics_interpolation();
|
||||||
@ -617,6 +619,7 @@ void SceneTree::iteration_prepare() {
|
|||||||
// Make sure any pending transforms from the last tick / frame
|
// Make sure any pending transforms from the last tick / frame
|
||||||
// are flushed before pumping the interpolation prev and currents.
|
// are flushed before pumping the interpolation prev and currents.
|
||||||
flush_transform_notifications();
|
flush_transform_notifications();
|
||||||
|
get_scene_tree_fti().tick_update();
|
||||||
RenderingServer::get_singleton()->tick();
|
RenderingServer::get_singleton()->tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,6 +692,17 @@ bool SceneTree::idle(float p_time) {
|
|||||||
//print_line("node count: "+itos(get_node_count()));
|
//print_line("node count: "+itos(get_node_count()));
|
||||||
//print_line("TEXTURE RAM: "+itos(RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED)));
|
//print_line("TEXTURE RAM: "+itos(RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED)));
|
||||||
|
|
||||||
|
// First pass of scene tree fixed timestep interpolation.
|
||||||
|
if (get_scene_tree_fti().is_enabled()) {
|
||||||
|
// Special, we need to ensure RenderingServer is up to date
|
||||||
|
// with *all* the pending xforms *before* updating it during
|
||||||
|
// the FTI update.
|
||||||
|
// If this is not done, we can end up with a deferred `set_transform()`
|
||||||
|
// overwriting the interpolated xform in the server.
|
||||||
|
flush_transform_notifications();
|
||||||
|
get_scene_tree_fti().frame_update(get_root(), true);
|
||||||
|
}
|
||||||
|
|
||||||
root_lock++;
|
root_lock++;
|
||||||
|
|
||||||
if (MainLoop::idle(p_time)) {
|
if (MainLoop::idle(p_time)) {
|
||||||
@ -805,6 +819,11 @@ bool SceneTree::idle(float p_time) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Second pass of scene tree fixed timestep interpolation.
|
||||||
|
// ToDo: Possibly needs another flush_transform_notifications here
|
||||||
|
// depending on whether there are side effects to _call_idle_callbacks().
|
||||||
|
get_scene_tree_fti().frame_update(get_root(), false);
|
||||||
|
|
||||||
RenderingServer::get_singleton()->pre_draw(true);
|
RenderingServer::get_singleton()->pre_draw(true);
|
||||||
|
|
||||||
return _quit;
|
return _quit;
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "core/io/multiplayer_api.h"
|
#include "core/io/multiplayer_api.h"
|
||||||
#include "core/os/main_loop.h"
|
#include "core/os/main_loop.h"
|
||||||
#include "core/os/thread_safe.h"
|
#include "core/os/thread_safe.h"
|
||||||
|
#include "scene/main/scene_tree_fti.h"
|
||||||
|
|
||||||
class PackedScene;
|
class PackedScene;
|
||||||
class Node;
|
class Node;
|
||||||
@ -160,6 +161,7 @@ private:
|
|||||||
StretchAspect stretch_aspect;
|
StretchAspect stretch_aspect;
|
||||||
Size2i stretch_min;
|
Size2i stretch_min;
|
||||||
real_t stretch_scale;
|
real_t stretch_scale;
|
||||||
|
SceneTreeFTI scene_tree_fti;
|
||||||
|
|
||||||
void _update_font_oversampling(float p_ratio);
|
void _update_font_oversampling(float p_ratio);
|
||||||
void _update_root_rect();
|
void _update_root_rect();
|
||||||
@ -468,6 +470,8 @@ public:
|
|||||||
void client_physics_interpolation_add_spatial(SelfList<Spatial> *p_elem);
|
void client_physics_interpolation_add_spatial(SelfList<Spatial> *p_elem);
|
||||||
void client_physics_interpolation_remove_spatial(SelfList<Spatial> *p_elem);
|
void client_physics_interpolation_remove_spatial(SelfList<Spatial> *p_elem);
|
||||||
|
|
||||||
|
SceneTreeFTI &get_scene_tree_fti() { return scene_tree_fti; }
|
||||||
|
|
||||||
static void add_idle_callback(IdleCallback p_callback);
|
static void add_idle_callback(IdleCallback p_callback);
|
||||||
SceneTree();
|
SceneTree();
|
||||||
~SceneTree();
|
~SceneTree();
|
||||||
|
288
scene/main/scene_tree_fti.cpp
Normal file
288
scene/main/scene_tree_fti.cpp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* scene_tree_fti.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _3D_DISABLED
|
||||||
|
|
||||||
|
#include "scene_tree_fti.h"
|
||||||
|
#include "core/config/engine.h"
|
||||||
|
#include "core/error/error_macros.h"
|
||||||
|
#include "core/math/transform_interpolator.h"
|
||||||
|
#include "core/os/os.h"
|
||||||
|
#include "scene/main/spatial.h"
|
||||||
|
#include "scene/3d/visual_instance.h"
|
||||||
|
|
||||||
|
void SceneTreeFTI::_reset_flags(Node *p_node) {
|
||||||
|
Spatial *s = Object::cast_to<Spatial>(p_node);
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
s->data.fti_on_frame_list = false;
|
||||||
|
s->data.fti_on_tick_list = false;
|
||||||
|
|
||||||
|
// In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION
|
||||||
|
// will reset this, but this should help cover hidden nodes.
|
||||||
|
s->data.local_transform_prev = s->data.local_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||||
|
_reset_flags(p_node->get_child(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::set_enabled(Node *p_root, bool p_enabled) {
|
||||||
|
if (data.enabled == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.spatial_tick_list[0].clear();
|
||||||
|
data.spatial_tick_list[1].clear();
|
||||||
|
|
||||||
|
// Spatial flags must be reset.
|
||||||
|
if (p_root) {
|
||||||
|
_reset_flags(p_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.enabled = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::tick_update() {
|
||||||
|
if (!data.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t curr_mirror = data.mirror;
|
||||||
|
uint32_t prev_mirror = curr_mirror ? 0 : 1;
|
||||||
|
|
||||||
|
LocalVector<Spatial *> &curr = data.spatial_tick_list[curr_mirror];
|
||||||
|
LocalVector<Spatial *> &prev = data.spatial_tick_list[prev_mirror];
|
||||||
|
|
||||||
|
// First detect on the previous list but not on this tick list.
|
||||||
|
for (uint32_t n = 0; n < prev.size(); n++) {
|
||||||
|
Spatial *s = prev[n];
|
||||||
|
if (!s->data.fti_on_tick_list) {
|
||||||
|
// Needs a reset so jittering will stop.
|
||||||
|
s->fti_pump();
|
||||||
|
|
||||||
|
// This may not get updated so set it to the same as global xform.
|
||||||
|
// TODO: double check this is the best value.
|
||||||
|
s->data.global_transform_interpolated = s->get_global_transform();
|
||||||
|
|
||||||
|
// Remove from interpolation list.
|
||||||
|
if (s->data.fti_on_frame_list) {
|
||||||
|
s->data.fti_on_frame_list = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now pump all on the current list.
|
||||||
|
for (uint32_t n = 0; n < curr.size(); n++) {
|
||||||
|
Spatial *s = curr[n];
|
||||||
|
|
||||||
|
// Reset, needs to be marked each tick.
|
||||||
|
s->data.fti_on_tick_list = false;
|
||||||
|
|
||||||
|
// Pump.
|
||||||
|
s->fti_pump();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear previous list and flip.
|
||||||
|
prev.clear();
|
||||||
|
data.mirror = prev_mirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::_spatial_notify_set_transform(Spatial &r_spatial) {
|
||||||
|
// This may be checked by the calling routine already,
|
||||||
|
// but needs to be double checked for custom SceneTrees.
|
||||||
|
if (!data.enabled || !r_spatial.is_physics_interpolated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r_spatial.data.fti_on_tick_list) {
|
||||||
|
r_spatial.data.fti_on_tick_list = true;
|
||||||
|
data.spatial_tick_list[data.mirror].push_back(&r_spatial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r_spatial.data.fti_on_frame_list) {
|
||||||
|
r_spatial.data.fti_on_frame_list = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) {
|
||||||
|
if (!data.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_spatial->data.fti_on_frame_list) {
|
||||||
|
p_spatial->data.fti_on_frame_list = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can potentially be optimized for large scenes with large churn,
|
||||||
|
// as it will be doing a linear search through the lists.
|
||||||
|
data.spatial_tick_list[0].erase_unordered(p_spatial);
|
||||||
|
data.spatial_tick_list[1].erase_unordered(p_spatial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::_update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform, int p_depth) {
|
||||||
|
Spatial *s = Object::cast_to<Spatial>(p_node);
|
||||||
|
|
||||||
|
// Don't recurse into hidden branches.
|
||||||
|
if (s && !s->is_visible()) {
|
||||||
|
// NOTE : If we change from recursing entire tree, we should do an is_visible_in_tree()
|
||||||
|
// check for the first of the branch.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a Spatial.
|
||||||
|
// Could be e.g. a viewport or something
|
||||||
|
// so we should still recurse to children.
|
||||||
|
if (!s) {
|
||||||
|
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||||
|
_update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the active interpolation chain from here onwards
|
||||||
|
// as we recurse further into the SceneTree.
|
||||||
|
// Once we hit an active (interpolated) node, we have to fully
|
||||||
|
// process all ancestors because their xform will also change.
|
||||||
|
// Anything not moving (inactive) higher in the tree need not be processed.
|
||||||
|
if (!p_active) {
|
||||||
|
if (data.frame_start) {
|
||||||
|
// On the frame start, activate whenever we hit something that requests interpolation.
|
||||||
|
if (s->data.fti_on_frame_list) {
|
||||||
|
p_active = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On the frame end, we want to re-interpolate *anything* that has moved
|
||||||
|
// since the frame start.
|
||||||
|
if (s->data.dirty & Spatial::DIRTY_GLOBAL_INTERPOLATED) {
|
||||||
|
p_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.frame_start) {
|
||||||
|
// Mark on the Spatial whether we have set global_transform_interp.
|
||||||
|
// This can later be used when calling `get_global_transform_interpolated()`
|
||||||
|
// to know which xform to return.
|
||||||
|
s->data.fti_global_xform_interp_set = p_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_active) {
|
||||||
|
#if 0
|
||||||
|
bool dirty = s->data.dirty & Spatial::DIRTY_GLOBAL_INTERP;
|
||||||
|
|
||||||
|
if (data.debug) {
|
||||||
|
String sz;
|
||||||
|
for (int n = 0; n < p_depth; n++) {
|
||||||
|
sz += "\t";
|
||||||
|
}
|
||||||
|
print_line(sz + p_node->get_name() + (dirty ? " DIRTY" : ""));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// First calculate our local xform.
|
||||||
|
// This will either use interpolation, or just use the current local if not interpolated.
|
||||||
|
Transform local_interp;
|
||||||
|
if (s->is_physics_interpolated()) {
|
||||||
|
TransformInterpolator::interpolate_transform(s->data.local_transform_prev, s->data.local_transform, local_interp, p_interpolation_fraction);
|
||||||
|
} else {
|
||||||
|
local_interp = s->data.local_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate parent xform.
|
||||||
|
if (!s->is_set_as_toplevel()) {
|
||||||
|
if (p_parent_global_xform) {
|
||||||
|
s->data.global_transform_interpolated = (*p_parent_global_xform) * local_interp;
|
||||||
|
} else {
|
||||||
|
const Spatial *parent = s->get_parent_spatial();
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
const Transform &parent_glob = parent->data.fti_global_xform_interp_set ? parent->data.global_transform_interpolated : parent->data.global_transform;
|
||||||
|
s->data.global_transform_interpolated = parent_glob * local_interp;
|
||||||
|
} else {
|
||||||
|
s->data.global_transform_interpolated = local_interp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s->data.global_transform_interpolated = local_interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload to VisualServer the interpolated global xform.
|
||||||
|
s->fti_update_servers();
|
||||||
|
|
||||||
|
} // if active.
|
||||||
|
|
||||||
|
// Remove the dirty interp flag from EVERYTHING as we go.
|
||||||
|
s->data.dirty &= ~Spatial::DIRTY_GLOBAL_INTERPOLATED;
|
||||||
|
|
||||||
|
// Recurse to children.
|
||||||
|
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||||
|
_update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) {
|
||||||
|
if (!data.enabled || !p_root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.frame_start = p_frame_start;
|
||||||
|
|
||||||
|
float f = Engine::get_singleton()->get_physics_interpolation_fraction();
|
||||||
|
uint32_t frame = Engine::get_singleton()->get_frames_drawn();
|
||||||
|
|
||||||
|
// #define SCENE_TREE_FTI_TAKE_TIMINGS
|
||||||
|
#ifdef SCENE_TREE_FTI_TAKE_TIMINGS
|
||||||
|
uint64_t before = OS::get_singleton()->get_ticks_usec();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (data.debug) {
|
||||||
|
print_line(String("\nScene: ") + (data.frame_start ? "start" : "end") + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probably not the most optimal approach as we traverse the entire SceneTree
|
||||||
|
// but simple and foolproof.
|
||||||
|
// Can be optimized later.
|
||||||
|
_update_dirty_spatials(p_root, frame, f, false);
|
||||||
|
|
||||||
|
if (!p_frame_start && data.debug) {
|
||||||
|
data.debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCENE_TREE_FTI_TAKE_TIMINGS
|
||||||
|
uint64_t after = OS::get_singleton()->get_ticks_usec();
|
||||||
|
if ((Engine::get_singleton()->get_frames_drawn() % 60) == 0) {
|
||||||
|
print_line("Took " + itos(after - before) + " usec " + (data.frame_start ? "start" : "end"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ndef _3D_DISABLED
|
112
scene/main/scene_tree_fti.h
Normal file
112
scene/main/scene_tree_fti.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* scene_tree_fti.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SCENE_TREE_FTI_H
|
||||||
|
#define SCENE_TREE_FTI_H
|
||||||
|
|
||||||
|
#include "core/containers/local_vector.h"
|
||||||
|
#include "core/os/mutex.h"
|
||||||
|
|
||||||
|
class Spatial;
|
||||||
|
class Node;
|
||||||
|
class Transform;
|
||||||
|
|
||||||
|
#ifdef _3D_DISABLED
|
||||||
|
// Stubs
|
||||||
|
class SceneTreeFTI {
|
||||||
|
public:
|
||||||
|
void frame_update(Node *p_root, bool p_frame_start) {}
|
||||||
|
void tick_update() {}
|
||||||
|
void set_enabled(Node *p_root, bool p_enabled) {}
|
||||||
|
bool is_enabled() const { return false; }
|
||||||
|
|
||||||
|
void spatial_notify_set_transform(Spatial &r_spatial) {}
|
||||||
|
void spatial_notify_delete(Spatial *p_spatial) {}
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Important.
|
||||||
|
// This class uses raw pointers, so it is essential that on deletion, this class is notified
|
||||||
|
// so that any references can be cleared up to prevent dangling pointer access.
|
||||||
|
|
||||||
|
// This class can be used from a custom SceneTree.
|
||||||
|
|
||||||
|
// Note we could potentially make SceneTreeFTI static / global to avoid the lookup through scene tree,
|
||||||
|
// but this covers the custom case of multiple scene trees.
|
||||||
|
|
||||||
|
// This class is not thread safe, but can be made thread safe easily with a mutex as in the 4.x version.
|
||||||
|
|
||||||
|
class SceneTreeFTI {
|
||||||
|
struct Data {
|
||||||
|
// Prev / Curr lists of spatials having local xforms pumped.
|
||||||
|
LocalVector<Spatial *> spatial_tick_list[2];
|
||||||
|
uint32_t mirror = 0;
|
||||||
|
|
||||||
|
bool enabled = false;
|
||||||
|
|
||||||
|
// Whether we are in physics ticks, or in a frame.
|
||||||
|
bool in_frame = false;
|
||||||
|
|
||||||
|
// Updating at the start of the frame, or the end on second pass.
|
||||||
|
bool frame_start = true;
|
||||||
|
|
||||||
|
bool debug = false;
|
||||||
|
} data;
|
||||||
|
|
||||||
|
void _update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform = nullptr, int p_depth = 0);
|
||||||
|
void _reset_flags(Node *p_node);
|
||||||
|
void _spatial_notify_set_transform(Spatial &r_spatial);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Hottest function, allow inlining the data.enabled check.
|
||||||
|
void spatial_notify_set_transform(Spatial &r_spatial) {
|
||||||
|
if (!data.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_spatial_notify_set_transform(r_spatial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spatial_notify_delete(Spatial *p_spatial);
|
||||||
|
|
||||||
|
// Calculate interpolated xforms, send to visual server.
|
||||||
|
void frame_update(Node *p_root, bool p_frame_start);
|
||||||
|
|
||||||
|
// Update local xform pumps.
|
||||||
|
void tick_update();
|
||||||
|
|
||||||
|
void set_enabled(Node *p_root, bool p_enabled);
|
||||||
|
bool is_enabled() const { return data.enabled; }
|
||||||
|
|
||||||
|
void set_debug_next_frame() { data.debug = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ndef _3D_DISABLED
|
||||||
|
|
||||||
|
#endif // SCENE_TREE_FTI_H
|
@ -118,7 +118,7 @@ void Spatial::_propagate_transform_changed(Spatial *p_origin) {
|
|||||||
#endif
|
#endif
|
||||||
get_tree()->xform_change_list.add(&xform_change);
|
get_tree()->xform_change_list.add(&xform_change);
|
||||||
}
|
}
|
||||||
data.dirty |= DIRTY_GLOBAL;
|
data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED;
|
||||||
|
|
||||||
data.children_lock--;
|
data.children_lock--;
|
||||||
}
|
}
|
||||||
@ -174,13 +174,27 @@ void Spatial::_notification(int p_what) {
|
|||||||
_propagate_merging_allowed(merging_allowed);
|
_propagate_merging_allowed(merging_allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene
|
data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED; //global is always dirty upon entering a scene
|
||||||
_notify_dirty();
|
_notify_dirty();
|
||||||
|
|
||||||
notification(NOTIFICATION_ENTER_WORLD);
|
notification(NOTIFICATION_ENTER_WORLD);
|
||||||
|
|
||||||
|
if (is_physics_interpolated_and_enabled()) {
|
||||||
|
// Always reset FTI when entering tree.
|
||||||
|
fti_pump();
|
||||||
|
|
||||||
|
// No need to interpolate as we are doing a reset.
|
||||||
|
data.global_transform_interpolated = get_global_transform();
|
||||||
|
|
||||||
|
// Make sure servers are up to date.
|
||||||
|
fti_update_servers();
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_EXIT_TREE: {
|
case NOTIFICATION_EXIT_TREE: {
|
||||||
|
if (is_inside_tree()) {
|
||||||
|
get_tree()->get_scene_tree_fti().spatial_notify_delete(this);
|
||||||
|
}
|
||||||
|
|
||||||
notification(NOTIFICATION_EXIT_WORLD, true);
|
notification(NOTIFICATION_EXIT_WORLD, true);
|
||||||
if (xform_change.in_list()) {
|
if (xform_change.in_list()) {
|
||||||
get_tree()->xform_change_list.remove(&xform_change);
|
get_tree()->xform_change_list.remove(&xform_change);
|
||||||
@ -241,6 +255,13 @@ void Spatial::_notification(int p_what) {
|
|||||||
if (data.client_physics_interpolation_data) {
|
if (data.client_physics_interpolation_data) {
|
||||||
data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr;
|
data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr;
|
||||||
}
|
}
|
||||||
|
data.local_transform_prev = data.local_transform;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NOTIFICATION_PAUSED: {
|
||||||
|
if (is_physics_interpolated_and_enabled()) {
|
||||||
|
data.local_transform_prev = data.local_transform;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
@ -270,7 +291,22 @@ void Spatial::set_global_rotation(const Vector3 &p_euler_rad) {
|
|||||||
set_global_transform(transform);
|
set_global_transform(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Spatial::fti_pump() {
|
||||||
|
if (data.dirty & DIRTY_LOCAL) {
|
||||||
|
_update_local_transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
data.local_transform_prev = data.local_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spatial::fti_notify_node_changed() {
|
||||||
|
if (is_inside_tree()) {
|
||||||
|
get_tree()->get_scene_tree_fti().spatial_notify_set_transform(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Spatial::set_transform(const Transform &p_transform) {
|
void Spatial::set_transform(const Transform &p_transform) {
|
||||||
|
fti_notify_node_changed();
|
||||||
data.local_transform = p_transform;
|
data.local_transform = p_transform;
|
||||||
data.dirty |= DIRTY_VECTORS;
|
data.dirty |= DIRTY_VECTORS;
|
||||||
data.dirty &= ~DIRTY_LOCAL;
|
data.dirty &= ~DIRTY_LOCAL;
|
||||||
@ -393,14 +429,19 @@ Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fra
|
|||||||
}
|
}
|
||||||
|
|
||||||
Transform Spatial::get_global_transform_interpolated() {
|
Transform Spatial::get_global_transform_interpolated() {
|
||||||
|
#if 1
|
||||||
// Pass through if physics interpolation is switched off.
|
// Pass through if physics interpolation is switched off.
|
||||||
// This is a convenience, as it allows you to easy turn off interpolation
|
// This is a convenience, as it allows you to easy turn off interpolation
|
||||||
// without changing any code.
|
// without changing any code.
|
||||||
if (!is_physics_interpolated_and_enabled()) {
|
if (data.fti_global_xform_interp_set && is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame() && is_visible_in_tree()) {
|
||||||
return get_global_transform();
|
return data.global_transform_interpolated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are in the physics frame, the interpolated global transform is meaningless.
|
return get_global_transform();
|
||||||
|
#else
|
||||||
|
// OLD METHOD - deprecated since moving to SceneTreeFTI,
|
||||||
|
// but leaving for reference and comparison for debugging.
|
||||||
|
|
||||||
// However, there is an exception, we may want to use this as a means of starting off the client
|
// However, there is an exception, we may want to use this as a means of starting off the client
|
||||||
// interpolation pump if not already started (when _is_physics_interpolated_client_side() is false).
|
// interpolation pump if not already started (when _is_physics_interpolated_client_side() is false).
|
||||||
if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) {
|
if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) {
|
||||||
@ -408,6 +449,7 @@ Transform Spatial::get_global_transform_interpolated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction());
|
return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform Spatial::get_global_transform() const {
|
Transform Spatial::get_global_transform() const {
|
||||||
@ -473,6 +515,7 @@ Transform Spatial::get_relative_transform(const Node *p_parent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Spatial::set_translation(const Vector3 &p_translation) {
|
void Spatial::set_translation(const Vector3 &p_translation) {
|
||||||
|
fti_notify_node_changed();
|
||||||
data.local_transform.origin = p_translation;
|
data.local_transform.origin = p_translation;
|
||||||
_change_notify("transform");
|
_change_notify("transform");
|
||||||
_propagate_transform_changed(this);
|
_propagate_transform_changed(this);
|
||||||
@ -482,6 +525,7 @@ void Spatial::set_translation(const Vector3 &p_translation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Spatial::set_rotation(const Vector3 &p_euler_rad) {
|
void Spatial::set_rotation(const Vector3 &p_euler_rad) {
|
||||||
|
fti_notify_node_changed();
|
||||||
if (data.dirty & DIRTY_VECTORS) {
|
if (data.dirty & DIRTY_VECTORS) {
|
||||||
data.scale = data.local_transform.basis.get_scale();
|
data.scale = data.local_transform.basis.get_scale();
|
||||||
data.dirty &= ~DIRTY_VECTORS;
|
data.dirty &= ~DIRTY_VECTORS;
|
||||||
@ -501,6 +545,7 @@ void Spatial::set_rotation_degrees(const Vector3 &p_euler_deg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Spatial::set_scale(const Vector3 &p_scale) {
|
void Spatial::set_scale(const Vector3 &p_scale) {
|
||||||
|
fti_notify_node_changed();
|
||||||
if (data.dirty & DIRTY_VECTORS) {
|
if (data.dirty & DIRTY_VECTORS) {
|
||||||
data.rotation = data.local_transform.basis.get_rotation();
|
data.rotation = data.local_transform.basis.get_rotation();
|
||||||
data.dirty &= ~DIRTY_VECTORS;
|
data.dirty &= ~DIRTY_VECTORS;
|
||||||
@ -1106,6 +1151,11 @@ Spatial::Spatial() :
|
|||||||
data.disable_scale = false;
|
data.disable_scale = false;
|
||||||
data.vi_visible = true;
|
data.vi_visible = true;
|
||||||
data.merging_allowed = true;
|
data.merging_allowed = true;
|
||||||
|
|
||||||
|
data.fti_on_frame_list = false;
|
||||||
|
data.fti_on_tick_list = false;
|
||||||
|
data.fti_global_xform_interp_set = false;
|
||||||
|
|
||||||
data.merging_mode = MERGING_MODE_INHERIT;
|
data.merging_mode = MERGING_MODE_INHERIT;
|
||||||
|
|
||||||
data.client_physics_interpolation_data = nullptr;
|
data.client_physics_interpolation_data = nullptr;
|
||||||
@ -1123,4 +1173,8 @@ Spatial::Spatial() :
|
|||||||
|
|
||||||
Spatial::~Spatial() {
|
Spatial::~Spatial() {
|
||||||
_disable_client_physics_interpolation();
|
_disable_client_physics_interpolation();
|
||||||
|
|
||||||
|
if (is_inside_tree()) {
|
||||||
|
get_tree()->get_scene_tree_fti().spatial_notify_delete(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@ class Spatial : public Node {
|
|||||||
GDCLASS(Spatial, Node);
|
GDCLASS(Spatial, Node);
|
||||||
OBJ_CATEGORY("3D");
|
OBJ_CATEGORY("3D");
|
||||||
|
|
||||||
|
friend class SceneTreeFTI;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum MergingMode : unsigned int {
|
enum MergingMode : unsigned int {
|
||||||
MERGING_MODE_INHERIT,
|
MERGING_MODE_INHERIT,
|
||||||
@ -77,15 +79,27 @@ private:
|
|||||||
DIRTY_NONE = 0,
|
DIRTY_NONE = 0,
|
||||||
DIRTY_VECTORS = 1,
|
DIRTY_VECTORS = 1,
|
||||||
DIRTY_LOCAL = 2,
|
DIRTY_LOCAL = 2,
|
||||||
DIRTY_GLOBAL = 4
|
DIRTY_GLOBAL = 4,
|
||||||
|
DIRTY_GLOBAL_INTERPOLATED = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
mutable SelfList<Node> xform_change;
|
mutable SelfList<Node> xform_change;
|
||||||
SelfList<Spatial> _client_physics_interpolation_spatials_list;
|
SelfList<Spatial> _client_physics_interpolation_spatials_list;
|
||||||
|
|
||||||
struct Data {
|
struct Data {
|
||||||
|
// Interpolated global transform - correct on the frame only.
|
||||||
|
// Only used with FTI.
|
||||||
|
Transform global_transform_interpolated;
|
||||||
|
|
||||||
|
// Current xforms are either
|
||||||
|
// * Used for everything (when not using FTI)
|
||||||
|
// * Correct on the physics tick (when using FTI)
|
||||||
mutable Transform global_transform;
|
mutable Transform global_transform;
|
||||||
mutable Transform local_transform;
|
mutable Transform local_transform;
|
||||||
|
|
||||||
|
// Only used with FTI.
|
||||||
|
Transform local_transform_prev;
|
||||||
|
|
||||||
mutable Vector3 rotation;
|
mutable Vector3 rotation;
|
||||||
mutable Vector3 scale;
|
mutable Vector3 scale;
|
||||||
|
|
||||||
@ -109,6 +123,11 @@ private:
|
|||||||
bool visible : 1;
|
bool visible : 1;
|
||||||
bool disable_scale : 1;
|
bool disable_scale : 1;
|
||||||
|
|
||||||
|
// Scene tree interpolation
|
||||||
|
bool fti_on_frame_list : 1;
|
||||||
|
bool fti_on_tick_list : 1;
|
||||||
|
bool fti_global_xform_interp_set : 1;
|
||||||
|
|
||||||
bool merging_allowed : 1;
|
bool merging_allowed : 1;
|
||||||
|
|
||||||
int children_lock;
|
int children_lock;
|
||||||
@ -145,9 +164,27 @@ protected:
|
|||||||
bool _is_vi_visible() const {
|
bool _is_vi_visible() const {
|
||||||
return data.vi_visible;
|
return data.vi_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform _get_global_transform_interpolated(real_t p_interpolation_fraction);
|
Transform _get_global_transform_interpolated(real_t p_interpolation_fraction);
|
||||||
|
const Transform &_get_cached_global_transform_interpolated() const { return data.global_transform_interpolated; }
|
||||||
void _disable_client_physics_interpolation();
|
void _disable_client_physics_interpolation();
|
||||||
|
|
||||||
|
// Calling this announces to the FTI system that a node has been moved,
|
||||||
|
// or requires an update in terms of interpolation
|
||||||
|
// (e.g. changing Camera zoom even if position hasn't changed).
|
||||||
|
void fti_notify_node_changed();
|
||||||
|
|
||||||
|
// Opportunity after FTI to update the servers
|
||||||
|
// with global_transform_interpolated,
|
||||||
|
// and any custom interpolated data in derived classes.
|
||||||
|
// Make sure to call the parent class fti_update_servers(),
|
||||||
|
// so all data is updated to the servers.
|
||||||
|
virtual void fti_update_servers() {}
|
||||||
|
|
||||||
|
// Pump the FTI data, also gives a chance for inherited classes
|
||||||
|
// to pump custom data, but they *must* call the base class here too.
|
||||||
|
virtual void fti_pump();
|
||||||
|
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
@ -92,16 +92,8 @@ public:
|
|||||||
RID material_override;
|
RID material_override;
|
||||||
RID material_overlay;
|
RID material_overlay;
|
||||||
|
|
||||||
// This is the main transform to be drawn with ..
|
|
||||||
// This will either be the interpolated transform (when using fixed timestep interpolation)
|
|
||||||
// or the ONLY transform (when not using FTI).
|
|
||||||
Transform transform;
|
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;
|
int depth_layer;
|
||||||
uint32_t layer_mask;
|
uint32_t layer_mask;
|
||||||
|
|
||||||
@ -123,16 +115,6 @@ public:
|
|||||||
bool baked_light : 1; //this flag is only to know if it actually did use baked light
|
bool baked_light : 1; //this flag is only to know if it actually did use baked light
|
||||||
bool redraw_if_visible : 1;
|
bool redraw_if_visible : 1;
|
||||||
|
|
||||||
bool on_interpolate_list : 1;
|
|
||||||
bool on_interpolate_transform_list : 1;
|
|
||||||
bool interpolated : 1;
|
|
||||||
TransformInterpolator::Method interpolation_method : 3;
|
|
||||||
|
|
||||||
// For fixed timestep interpolation.
|
|
||||||
// Note 32 bits is plenty for checksum, no need for real_t
|
|
||||||
float transform_checksum_curr;
|
|
||||||
float transform_checksum_prev;
|
|
||||||
|
|
||||||
float depth; //used for sorting
|
float depth; //used for sorting
|
||||||
|
|
||||||
SelfList<InstanceBase> dependency_item;
|
SelfList<InstanceBase> dependency_item;
|
||||||
@ -158,12 +140,6 @@ public:
|
|||||||
lightmap_capture = nullptr;
|
lightmap_capture = nullptr;
|
||||||
lightmap_slice = -1;
|
lightmap_slice = -1;
|
||||||
lightmap_uv_rect = Rect2(0, 0, 1, 1);
|
lightmap_uv_rect = Rect2(0, 0, 1, 1);
|
||||||
on_interpolate_list = false;
|
|
||||||
on_interpolate_transform_list = false;
|
|
||||||
interpolated = true;
|
|
||||||
interpolation_method = TransformInterpolator::INTERP_LERP;
|
|
||||||
transform_checksum_curr = 0.0;
|
|
||||||
transform_checksum_prev = 0.0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -569,8 +569,6 @@ public:
|
|||||||
BIND2(instance_set_layer_mask, RID, uint32_t)
|
BIND2(instance_set_layer_mask, RID, uint32_t)
|
||||||
BIND3(instance_set_pivot_data, RID, float, bool)
|
BIND3(instance_set_pivot_data, RID, float, bool)
|
||||||
BIND2(instance_set_transform, RID, const Transform &)
|
BIND2(instance_set_transform, RID, const Transform &)
|
||||||
BIND2(instance_set_interpolated, RID, bool)
|
|
||||||
BIND1(instance_reset_physics_interpolation, RID)
|
|
||||||
BIND2(instance_attach_object_instance_id, RID, ObjectID)
|
BIND2(instance_attach_object_instance_id, RID, ObjectID)
|
||||||
BIND3(instance_set_blend_shape_weight, RID, int, float)
|
BIND3(instance_set_blend_shape_weight, RID, int, float)
|
||||||
BIND3(instance_set_surface_material, RID, int, RID)
|
BIND3(instance_set_surface_material, RID, int, RID)
|
||||||
|
@ -675,9 +675,6 @@ void RenderingServerScene::instance_set_scenario(RID p_instance, RID p_scenario)
|
|||||||
_instance_destroy_occlusion_rep(instance);
|
_instance_destroy_occlusion_rep(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any interpolation data associated with the instance in this scenario
|
|
||||||
_interpolation_data.notify_free_instance(p_instance, *instance);
|
|
||||||
|
|
||||||
switch (instance->base_type) {
|
switch (instance->base_type) {
|
||||||
case RS::INSTANCE_LIGHT: {
|
case RS::INSTANCE_LIGHT: {
|
||||||
InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
|
InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
|
||||||
@ -767,27 +764,6 @@ void RenderingServerScene::instance_set_pivot_data(RID p_instance, float p_sorti
|
|||||||
instance->use_aabb_center = p_use_aabb_center;
|
instance->use_aabb_center = p_use_aabb_center;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingServerScene::instance_reset_physics_interpolation(RID p_instance) {
|
|
||||||
Instance *instance = instance_owner.get(p_instance);
|
|
||||||
ERR_FAIL_COND(!instance);
|
|
||||||
|
|
||||||
if (_interpolation_data.interpolation_enabled && instance->interpolated) {
|
|
||||||
instance->transform_prev = instance->transform_curr;
|
|
||||||
instance->transform_checksum_prev = instance->transform_checksum_curr;
|
|
||||||
|
|
||||||
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
|
|
||||||
print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
|
||||||
print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingServerScene::instance_set_interpolated(RID p_instance, bool p_interpolated) {
|
|
||||||
Instance *instance = instance_owner.get(p_instance);
|
|
||||||
ERR_FAIL_COND(!instance);
|
|
||||||
instance->interpolated = p_interpolated;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) {
|
void RenderingServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) {
|
||||||
Instance *instance = instance_owner.get(p_instance);
|
Instance *instance = instance_owner.get(p_instance);
|
||||||
ERR_FAIL_COND(!instance);
|
ERR_FAIL_COND(!instance);
|
||||||
@ -796,40 +772,8 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor
|
|||||||
print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!(_interpolation_data.interpolation_enabled && instance->interpolated) || !instance->scenario) {
|
if (instance->transform == p_transform) {
|
||||||
if (instance->transform == p_transform) {
|
return; //must be checked to avoid worst evil
|
||||||
return; //must be checked to avoid worst evil
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin;
|
|
||||||
ERR_FAIL_COND(Math::is_inf(v.x));
|
|
||||||
ERR_FAIL_COND(Math::is_nan(v.x));
|
|
||||||
ERR_FAIL_COND(Math::is_inf(v.y));
|
|
||||||
ERR_FAIL_COND(Math::is_nan(v.y));
|
|
||||||
ERR_FAIL_COND(Math::is_inf(v.z));
|
|
||||||
ERR_FAIL_COND(Math::is_nan(v.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
instance->transform = p_transform;
|
|
||||||
_instance_queue_update(instance, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float new_checksum = TransformInterpolator::checksum_transform(p_transform);
|
|
||||||
bool checksums_match = (instance->transform_checksum_curr == new_checksum) && (instance->transform_checksum_prev == new_checksum);
|
|
||||||
|
|
||||||
// we can't entirely reject no changes because we need the interpolation
|
|
||||||
// system to keep on stewing
|
|
||||||
|
|
||||||
// Optimized check. First checks the checksums. If they pass it does the slow check at the end.
|
|
||||||
// Alternatively we can do this non-optimized and ignore the checksum...
|
|
||||||
// if no change
|
|
||||||
if (checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
@ -846,53 +790,10 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
instance->transform_curr = p_transform;
|
instance->transform = p_transform;
|
||||||
|
|
||||||
// keep checksums up to date
|
|
||||||
instance->transform_checksum_curr = new_checksum;
|
|
||||||
|
|
||||||
if (!instance->on_interpolate_transform_list) {
|
|
||||||
_interpolation_data.instance_transform_update_list_curr->push_back(p_instance);
|
|
||||||
instance->on_interpolate_transform_list = true;
|
|
||||||
} else {
|
|
||||||
DEV_ASSERT(_interpolation_data.instance_transform_update_list_curr->size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the instance is invisible, then we are simply updating the data flow, there is no need to calculate the interpolated
|
|
||||||
// transform or anything else.
|
|
||||||
// Ideally we would not even call the RenderingServer::set_transform() when invisible but that would entail having logic
|
|
||||||
// to keep track of the previous transform on the SceneTree side. The "early out" below is less efficient but a lot cleaner codewise.
|
|
||||||
if (!instance->visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decide on the interpolation method .. slerp if possible
|
|
||||||
instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis);
|
|
||||||
|
|
||||||
if (!instance->on_interpolate_list) {
|
|
||||||
_interpolation_data.instance_interpolate_update_list.push_back(p_instance);
|
|
||||||
instance->on_interpolate_list = true;
|
|
||||||
} else {
|
|
||||||
DEV_ASSERT(_interpolation_data.instance_interpolate_update_list.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
_instance_queue_update(instance, true);
|
_instance_queue_update(instance, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingServerScene::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) {
|
|
||||||
r_instance.on_interpolate_list = false;
|
|
||||||
r_instance.on_interpolate_transform_list = false;
|
|
||||||
|
|
||||||
if (!interpolation_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the instance was on any of the lists, remove
|
|
||||||
instance_interpolate_update_list.erase_multiple_unordered(p_rid);
|
|
||||||
instance_transform_update_list_curr->erase_multiple_unordered(p_rid);
|
|
||||||
instance_transform_update_list_prev->erase_multiple_unordered(p_rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingServerScene::update_interpolation_tick(bool p_process) {
|
void RenderingServerScene::update_interpolation_tick(bool p_process) {
|
||||||
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
|
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
|
||||||
print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
||||||
@ -900,84 +801,11 @@ void RenderingServerScene::update_interpolation_tick(bool p_process) {
|
|||||||
|
|
||||||
// update interpolation in storage
|
// update interpolation in storage
|
||||||
RSG::storage->update_interpolation_tick(p_process);
|
RSG::storage->update_interpolation_tick(p_process);
|
||||||
|
|
||||||
// detect any that were on the previous transform list that are no longer active,
|
|
||||||
// we should remove them from the interpolate list
|
|
||||||
|
|
||||||
for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_prev->size(); n++) {
|
|
||||||
const RID &rid = (*_interpolation_data.instance_transform_update_list_prev)[n];
|
|
||||||
Instance *instance = instance_owner.getornull(rid);
|
|
||||||
|
|
||||||
bool active = true;
|
|
||||||
|
|
||||||
// no longer active? (either the instance deleted or no longer being transformed)
|
|
||||||
if (instance && !instance->on_interpolate_transform_list) {
|
|
||||||
active = false;
|
|
||||||
instance->on_interpolate_list = false;
|
|
||||||
|
|
||||||
// make sure the most recent transform is set
|
|
||||||
instance->transform = instance->transform_curr;
|
|
||||||
|
|
||||||
// and that both prev and current are the same, just in case of any interpolations
|
|
||||||
instance->transform_prev = instance->transform_curr;
|
|
||||||
|
|
||||||
// make sure are updated one more time to ensure the AABBs are correct
|
|
||||||
_instance_queue_update(instance, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!instance) {
|
|
||||||
active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!active) {
|
|
||||||
_interpolation_data.instance_interpolate_update_list.erase(rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// and now for any in the transform list (being actively interpolated), keep the previous transform
|
|
||||||
// value up to date ready for the next tick
|
|
||||||
if (p_process) {
|
|
||||||
for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_curr->size(); n++) {
|
|
||||||
const RID &rid = (*_interpolation_data.instance_transform_update_list_curr)[n];
|
|
||||||
Instance *instance = instance_owner.getornull(rid);
|
|
||||||
if (instance) {
|
|
||||||
instance->transform_prev = instance->transform_curr;
|
|
||||||
instance->transform_checksum_prev = instance->transform_checksum_curr;
|
|
||||||
instance->on_interpolate_transform_list = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we maintain a mirror list for the transform updates, so we can detect when an instance
|
|
||||||
// is no longer being transformed, and remove it from the interpolate list
|
|
||||||
SWAP(_interpolation_data.instance_transform_update_list_curr, _interpolation_data.instance_transform_update_list_prev);
|
|
||||||
|
|
||||||
// prepare for the next iteration
|
|
||||||
_interpolation_data.instance_transform_update_list_curr->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingServerScene::update_interpolation_frame(bool p_process) {
|
void RenderingServerScene::update_interpolation_frame(bool p_process) {
|
||||||
// update interpolation in storage
|
// update interpolation in storage
|
||||||
RSG::storage->update_interpolation_frame(p_process);
|
RSG::storage->update_interpolation_frame(p_process);
|
||||||
|
|
||||||
if (p_process) {
|
|
||||||
real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _interpolation_data.instance_interpolate_update_list.size(); i++) {
|
|
||||||
const RID &rid = _interpolation_data.instance_interpolate_update_list[i];
|
|
||||||
Instance *instance = instance_owner.getornull(rid);
|
|
||||||
if (instance) {
|
|
||||||
TransformInterpolator::interpolate_transform_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method);
|
|
||||||
|
|
||||||
#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION
|
|
||||||
print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// make sure AABBs are constantly up to date through the interpolation
|
|
||||||
_instance_queue_update(instance, true);
|
|
||||||
}
|
|
||||||
} // for n
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) {
|
void RenderingServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) {
|
||||||
@ -1031,25 +859,6 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible)
|
|||||||
|
|
||||||
instance->visible = p_visible;
|
instance->visible = p_visible;
|
||||||
|
|
||||||
// Special case for physics interpolation, we want to ensure the interpolated data is up to date
|
|
||||||
if (_interpolation_data.interpolation_enabled && p_visible && instance->interpolated && instance->scenario && !instance->on_interpolate_list) {
|
|
||||||
// Do all the extra work we normally do on instance_set_transform(), because this is optimized out for hidden instances.
|
|
||||||
// This prevents a glitch of stale interpolation transform data when unhiding before the next physics tick.
|
|
||||||
instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis);
|
|
||||||
_interpolation_data.instance_interpolate_update_list.push_back(p_instance);
|
|
||||||
instance->on_interpolate_list = true;
|
|
||||||
_instance_queue_update(instance, true);
|
|
||||||
|
|
||||||
// We must also place on the transform update list for a tick, so the system
|
|
||||||
// can auto-detect if the instance is no longer moving, and remove from the interpolate lists again.
|
|
||||||
// If this step is ignored, an unmoving instance could remain on the interpolate lists indefinitely
|
|
||||||
// (or rather until the object is deleted) and cause unnecessary updates and drawcalls.
|
|
||||||
if (!instance->on_interpolate_transform_list) {
|
|
||||||
_interpolation_data.instance_transform_update_list_curr->push_back(p_instance);
|
|
||||||
instance->on_interpolate_transform_list = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// give the opportunity for the spatial partitioning scene to use a special implementation of visibility
|
// give the opportunity for the spatial partitioning scene to use a special implementation of visibility
|
||||||
// for efficiency (supported in BVH but not octree)
|
// for efficiency (supported in BVH but not octree)
|
||||||
|
|
||||||
@ -4398,8 +4207,6 @@ bool RenderingServerScene::free(RID p_rid) {
|
|||||||
|
|
||||||
Instance *instance = instance_owner.get(p_rid);
|
Instance *instance = instance_owner.get(p_rid);
|
||||||
|
|
||||||
_interpolation_data.notify_free_instance(p_rid, *instance);
|
|
||||||
|
|
||||||
instance_set_use_lightmap(p_rid, RID(), RID(), -1, Rect2(0, 0, 1, 1));
|
instance_set_use_lightmap(p_rid, RID(), RID(), -1, Rect2(0, 0, 1, 1));
|
||||||
instance_set_scenario(p_rid, RID());
|
instance_set_scenario(p_rid, RID());
|
||||||
instance_set_base(p_rid, RID());
|
instance_set_base(p_rid, RID());
|
||||||
|
@ -398,12 +398,6 @@ public:
|
|||||||
virtual void set_physics_interpolation_enabled(bool p_enabled);
|
virtual void set_physics_interpolation_enabled(bool p_enabled);
|
||||||
|
|
||||||
struct InterpolationData {
|
struct InterpolationData {
|
||||||
void notify_free_instance(RID p_rid, Instance &r_instance);
|
|
||||||
LocalVector<RID> instance_interpolate_update_list;
|
|
||||||
LocalVector<RID> instance_transform_update_lists[2];
|
|
||||||
LocalVector<RID> *instance_transform_update_list_curr = &instance_transform_update_lists[0];
|
|
||||||
LocalVector<RID> *instance_transform_update_list_prev = &instance_transform_update_lists[1];
|
|
||||||
|
|
||||||
bool interpolation_enabled = false;
|
bool interpolation_enabled = false;
|
||||||
} _interpolation_data;
|
} _interpolation_data;
|
||||||
|
|
||||||
@ -664,8 +658,6 @@ public:
|
|||||||
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask);
|
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask);
|
||||||
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center);
|
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center);
|
||||||
virtual void instance_set_transform(RID p_instance, const Transform &p_transform);
|
virtual void instance_set_transform(RID p_instance, const Transform &p_transform);
|
||||||
virtual void instance_set_interpolated(RID p_instance, bool p_interpolated);
|
|
||||||
virtual void instance_reset_physics_interpolation(RID p_instance);
|
|
||||||
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id);
|
virtual void instance_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_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);
|
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material);
|
||||||
|
@ -478,8 +478,6 @@ public:
|
|||||||
FUNC2(instance_set_layer_mask, RID, uint32_t)
|
FUNC2(instance_set_layer_mask, RID, uint32_t)
|
||||||
FUNC3(instance_set_pivot_data, RID, float, bool)
|
FUNC3(instance_set_pivot_data, RID, float, bool)
|
||||||
FUNC2(instance_set_transform, RID, const Transform &)
|
FUNC2(instance_set_transform, RID, const Transform &)
|
||||||
FUNC2(instance_set_interpolated, RID, bool)
|
|
||||||
FUNC1(instance_reset_physics_interpolation, RID)
|
|
||||||
FUNC2(instance_attach_object_instance_id, RID, ObjectID)
|
FUNC2(instance_attach_object_instance_id, RID, ObjectID)
|
||||||
FUNC3(instance_set_blend_shape_weight, RID, int, float)
|
FUNC3(instance_set_blend_shape_weight, RID, int, float)
|
||||||
FUNC3(instance_set_surface_material, RID, int, RID)
|
FUNC3(instance_set_surface_material, RID, int, RID)
|
||||||
|
@ -2168,8 +2168,6 @@ void RenderingServer::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("instance_set_scenario", "instance", "scenario"), &RenderingServer::instance_set_scenario);
|
ClassDB::bind_method(D_METHOD("instance_set_scenario", "instance", "scenario"), &RenderingServer::instance_set_scenario);
|
||||||
ClassDB::bind_method(D_METHOD("instance_set_layer_mask", "instance", "mask"), &RenderingServer::instance_set_layer_mask);
|
ClassDB::bind_method(D_METHOD("instance_set_layer_mask", "instance", "mask"), &RenderingServer::instance_set_layer_mask);
|
||||||
ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform);
|
ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform);
|
||||||
ClassDB::bind_method(D_METHOD("instance_set_interpolated", "instance", "interpolated"), &RenderingServer::instance_set_interpolated);
|
|
||||||
ClassDB::bind_method(D_METHOD("instance_reset_physics_interpolation", "instance"), &RenderingServer::instance_reset_physics_interpolation);
|
|
||||||
ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id);
|
ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id);
|
||||||
ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight);
|
ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight);
|
||||||
ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material);
|
ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material);
|
||||||
@ -2187,7 +2185,9 @@ void RenderingServer::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID()));
|
ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID()));
|
||||||
ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID()));
|
ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID()));
|
||||||
ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &RenderingServer::_instances_cull_convex_bind, DEFVAL(RID()));
|
ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &RenderingServer::_instances_cull_convex_bind, DEFVAL(RID()));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create);
|
ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create);
|
||||||
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring);
|
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring);
|
||||||
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate);
|
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate);
|
||||||
|
@ -876,8 +876,6 @@ public:
|
|||||||
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0;
|
virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0;
|
||||||
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0;
|
virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0;
|
||||||
virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0;
|
virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0;
|
||||||
virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0;
|
|
||||||
virtual void instance_reset_physics_interpolation(RID p_instance) = 0;
|
|
||||||
virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0;
|
virtual void instance_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_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;
|
virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user