diff options
Diffstat (limited to 'scene/3d')
53 files changed, 1109 insertions, 235 deletions
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6888462876..591528b915 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -562,7 +562,7 @@ void AudioStreamPlayer3D::seek(float p_seconds) { void AudioStreamPlayer3D::stop() { setplay.set(-1); - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer3D::is_playing() const { @@ -596,10 +596,6 @@ void AudioStreamPlayer3D::_set_playing(bool p_enable) { internal->set_playing(p_enable); } -bool AudioStreamPlayer3D::_is_active() const { - return internal->is_active(); -} - void AudioStreamPlayer3D::_validate_property(PropertyInfo &p_property) const { internal->validate_property(p_property); } @@ -779,8 +775,7 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer3D::set_autoplay); ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer3D::is_autoplay_enabled); - ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer3D::_set_playing); - ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer3D::_is_active); + ClassDB::bind_method(D_METHOD("set_playing", "enable"), &AudioStreamPlayer3D::_set_playing); ClassDB::bind_method(D_METHOD("set_max_distance", "meters"), &AudioStreamPlayer3D::set_max_distance); ClassDB::bind_method(D_METHOD("get_max_distance"), &AudioStreamPlayer3D::get_max_distance); @@ -830,7 +825,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db", PROPERTY_HINT_RANGE, "-24,6,suffix:dB"), "set_max_db", "get_max_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,or_greater,suffix:m"), "set_max_distance", "get_max_distance"); @@ -862,7 +857,7 @@ void AudioStreamPlayer3D::_bind_methods() { } AudioStreamPlayer3D::AudioStreamPlayer3D() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), callable_mp(this, &AudioStreamPlayer3D::stop), true)); velocity_tracker.instantiate(); set_disable_scale(true); cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength"); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 4889512037..7fe1b7079a 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -33,7 +33,7 @@ void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "bone_name") { - // Because it is a constant function, we cannot use the _get_skeleton_3d function. + // Because it is a constant function, we cannot use the get_skeleton function. const Skeleton3D *parent = nullptr; if (use_external_skeleton) { if (external_skeleton_node_cache.is_valid()) { @@ -134,7 +134,7 @@ void BoneAttachment3D::_update_external_skeleton_cache() { } void BoneAttachment3D::_check_bind() { - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk && !bound) { if (bone_idx <= -1) { @@ -148,7 +148,7 @@ void BoneAttachment3D::_check_bind() { } } -Skeleton3D *BoneAttachment3D::_get_skeleton3d() { +Skeleton3D *BoneAttachment3D::get_skeleton() { if (use_external_skeleton) { if (external_skeleton_node_cache.is_valid()) { return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); @@ -166,7 +166,7 @@ Skeleton3D *BoneAttachment3D::_get_skeleton3d() { void BoneAttachment3D::_check_unbind() { if (bound) { - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk) { sk->disconnect(SceneStringName(skeleton_updated), callable_mp(this, &BoneAttachment3D::on_skeleton_update)); @@ -181,7 +181,7 @@ void BoneAttachment3D::_transform_changed() { } if (override_pose && !overriding) { - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); ERR_FAIL_NULL_MSG(sk, "Cannot override pose: Skeleton not found!"); ERR_FAIL_INDEX_MSG(bone_idx, sk->get_bone_count(), "Cannot override pose: Bone index is out of range!"); @@ -200,7 +200,7 @@ void BoneAttachment3D::_transform_changed() { void BoneAttachment3D::set_bone_name(const String &p_name) { bone_name = p_name; - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk) { set_bone_idx(sk->find_bone(bone_name)); } @@ -217,7 +217,7 @@ void BoneAttachment3D::set_bone_idx(const int &p_idx) { bone_idx = p_idx; - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk) { if (bone_idx <= -1 || bone_idx >= sk->get_bone_count()) { WARN_PRINT("Bone index out of range! Cannot connect BoneAttachment to node!"); @@ -247,7 +247,7 @@ void BoneAttachment3D::set_override_pose(bool p_override) { set_notify_transform(override_pose); set_process_internal(override_pose); if (!override_pose && bone_idx >= 0) { - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk) { sk->reset_bone_pose(bone_idx); } @@ -318,7 +318,7 @@ void BoneAttachment3D::on_skeleton_update() { } updating = true; if (bone_idx >= 0) { - Skeleton3D *sk = _get_skeleton3d(); + Skeleton3D *sk = get_skeleton(); if (sk) { if (!override_pose) { if (use_external_skeleton) { @@ -369,6 +369,8 @@ BoneAttachment3D::BoneAttachment3D() { } void BoneAttachment3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_skeleton"), &BoneAttachment3D::get_skeleton); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &BoneAttachment3D::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_name"), &BoneAttachment3D::get_bone_name); diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index e19180b0ea..5435c4ad0c 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -57,7 +57,6 @@ class BoneAttachment3D : public Node3D { bool updating = false; void _transform_changed(); void _update_external_skeleton_cache(); - Skeleton3D *_get_skeleton3d(); protected: void _validate_property(PropertyInfo &p_property) const; @@ -79,6 +78,8 @@ public: virtual PackedStringArray get_configuration_warnings() const override; + Skeleton3D *get_skeleton(); + void set_bone_name(const String &p_name); String get_bone_name() const; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 8515aacba7..c70fa3ca2e 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,9 @@ #include "camera_3d.h" #include "core/math/projection.h" +#include "core/math/transform_interpolator.h" #include "scene/main/viewport.h" +#include "servers/rendering/rendering_server_constants.h" void Camera3D::_update_audio_listener_state() { } @@ -88,7 +90,16 @@ void Camera3D::_update_camera() { return; } - RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + if (!is_physics_interpolated_and_enabled()) { + RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + } else { + // Ideally we shouldn't be moving a physics interpolated camera within a frame, + // because it will break smooth interpolation, but it may occur on e.g. level load. + if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } if (is_part_of_edited_scene() || !is_current()) { return; @@ -97,6 +108,64 @@ void Camera3D::_update_camera() { get_viewport()->_camera_3d_transform_changed_notify(); } +void Camera3D::_physics_interpolated_changed() { + _update_process_mode(); +} + +void Camera3D::_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 Camera3D::_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_3d(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction()); + + Transform3D &tr = id.camera_xform_interpolated; + tr = _get_adjusted_camera_transform(id.xform_interpolated); + } +} + +void Camera3D::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) { + _desired_process_internal = p_process_internal; + _desired_physics_process_internal = p_physics_process_internal; + _update_process_mode(); +} + +void Camera3D::_update_process_mode() { + bool process = _desired_process_internal; + bool physics_process = _desired_physics_process_internal; + + if (is_physics_interpolated_and_enabled()) { + if (is_current()) { + process = true; + physics_process = true; + } + } + set_process_internal(process); + set_physics_process_internal(physics_process); +} + void Camera3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { @@ -118,11 +187,58 @@ void Camera3D::_notification(int p_what) { #endif } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(); + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated Camera3D: " + rtos(_interpolation_data.xform_interpolated.origin.x) + "\t( prev " + rtos(_interpolation_data.xform_prev.origin.x) + ", curr " + rtos(_interpolation_data.xform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera3D triggered from outside physics process"); + } +#endif + } _request_camera_update(); if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { velocity_tracker->update_position(get_global_transform().origin); } + // Allow auto-reset when first adding to the tree, as a convenience. + if (_is_physics_interpolation_reset_requested() && is_inside_tree()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + _set_physics_interpolation_reset_requested(false); + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (is_inside_tree()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } + } break; + + case NOTIFICATION_PAUSED: { + if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } } break; case NOTIFICATION_EXIT_WORLD: { @@ -151,23 +267,34 @@ void Camera3D::_notification(int p_what) { if (viewport) { viewport->find_world_3d()->_register_camera(this); } + _update_process_mode(); } break; case NOTIFICATION_LOST_CURRENT: { if (viewport) { viewport->find_world_3d()->_remove_camera(this); } + _update_process_mode(); } break; } } -Transform3D Camera3D::get_camera_transform() const { - Transform3D tr = get_global_transform().orthonormalized(); +Transform3D Camera3D::_get_adjusted_camera_transform(const Transform3D &p_xform) const { + Transform3D tr = p_xform.orthonormalized(); tr.origin += tr.basis.get_column(1) * v_offset; tr.origin += tr.basis.get_column(0) * h_offset; return tr; } +Transform3D Camera3D::get_camera_transform() const { + if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) { + _physics_interpolation_ensure_transform_calculated(); + return _interpolation_data.camera_xform_interpolated; + } + + return _get_adjusted_camera_transform(get_global_transform()); +} + Projection Camera3D::_get_camera_projection(real_t p_near) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; Projection cm; @@ -379,6 +506,11 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Plane p(get_camera_transform().xform_inv(p_pos), 1.0); p = cm.xform4(p); + + // Prevent divide by zero. + // TODO: Investigate, this was causing NaNs. + ERR_FAIL_COND_V(p.d == 0, Point2()); + p.normal /= p.d; Point2 res; diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index dbf2ffc1dd..3e9f940ad6 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -98,7 +98,39 @@ private: RID pyramid_shape; Vector<Vector3> pyramid_shape_points; + /////////////////////////////////////////////////////// + // 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 Camera3Ds, if they wish to do processing + // (while still allowing physics interpolation to function). + bool _desired_process_internal = false; + bool _desired_physics_process_internal = false; + + mutable struct InterpolationData { + Transform3D xform_curr; + Transform3D xform_prev; + Transform3D xform_interpolated; + Transform3D camera_xform_interpolated; // After modification according to camera type. + uint32_t last_update_physics_tick = 0; + uint32_t last_update_frame = UINT32_MAX; + } _interpolation_data; + + void _update_process_mode(); + protected: + // Use from derived classes to set process modes instead of setting directly. + // This is because physics interpolation may need to request process modes additionally. + void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal); + + // Opportunity for derived classes to interpolate extra attributes. + virtual void physics_interpolation_flip_data() {} + + virtual void _physics_interpolated_changed() override; + virtual Transform3D _get_adjusted_camera_transform(const Transform3D &p_xform) const; + /////////////////////////////////////////////////////// + void _update_camera(); virtual void _request_camera_update(); void _update_camera_mode(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 03fe5e1fad..acbc443a93 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -448,6 +448,10 @@ void CPUParticles3D::set_emission_ring_inner_radius(real_t p_radius) { emission_ring_inner_radius = p_radius; } +void CPUParticles3D::set_emission_ring_cone_angle(real_t p_angle) { + emission_ring_cone_angle = p_angle; +} + void CPUParticles3D::set_scale_curve_x(Ref<Curve> p_scale_curve) { scale_curve_x = p_scale_curve; } @@ -501,6 +505,10 @@ real_t CPUParticles3D::get_emission_ring_inner_radius() const { return emission_ring_inner_radius; } +real_t CPUParticles3D::get_emission_ring_cone_angle() const { + return emission_ring_cone_angle; +} + CPUParticles3D::EmissionShape CPUParticles3D::get_emission_shape() const { return emission_shape; } @@ -878,8 +886,14 @@ void CPUParticles3D::_particles_process(double p_delta) { } } break; case EMISSION_SHAPE_RING: { + real_t radius_clamped = MAX(0.001, emission_ring_radius); + real_t top_radius = MAX(radius_clamped - Math::tan(Math::deg_to_rad(90.0 - emission_ring_cone_angle)) * emission_ring_height, 0.0); + real_t y_pos = Math::randf(); + real_t skew = MAX(MIN(radius_clamped, top_radius) / MAX(radius_clamped, top_radius), 0.5); + y_pos = radius_clamped < top_radius ? Math::pow(y_pos, skew) : 1.0 - Math::pow(y_pos, skew); real_t ring_random_angle = Math::randf() * Math_TAU; - real_t ring_random_radius = Math::sqrt(Math::randf() * (emission_ring_radius * emission_ring_radius - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius); + real_t ring_random_radius = Math::sqrt(Math::randf() * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius); + ring_random_radius = Math::lerp(ring_random_radius, ring_random_radius * (top_radius / radius_clamped), y_pos); Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized(); Vector3 ortho_axis; if (axis.abs() == Vector3(1.0, 0.0, 0.0)) { @@ -890,7 +904,7 @@ void CPUParticles3D::_particles_process(double p_delta) { ortho_axis = ortho_axis.normalized(); ortho_axis.rotate(axis, ring_random_angle); ortho_axis = ortho_axis.normalized(); - p.transform.origin = ortho_axis * ring_random_radius + (Math::randf() * emission_ring_height - emission_ring_height / 2.0) * axis; + p.transform.origin = ortho_axis * ring_random_radius + (y_pos * emission_ring_height - emission_ring_height / 2.0) * axis; } break; case EMISSION_SHAPE_MAX: { // Max value for validity check. break; @@ -1550,6 +1564,9 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &CPUParticles3D::set_emission_ring_inner_radius); ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &CPUParticles3D::get_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("set_emission_ring_cone_angle", "cone_angle"), &CPUParticles3D::set_emission_ring_cone_angle); + ClassDB::bind_method(D_METHOD("get_emission_ring_cone_angle"), &CPUParticles3D::get_emission_ring_cone_angle); + ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles3D::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles3D::set_gravity); @@ -1577,9 +1594,10 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_height", "get_emission_ring_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_radius", "get_emission_ring_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_cone_angle", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_emission_ring_cone_angle", "get_emission_ring_cone_angle"); ADD_GROUP("Particle Flags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); @@ -1716,6 +1734,7 @@ CPUParticles3D::CPUParticles3D() { set_emission_ring_height(1); set_emission_ring_radius(1); set_emission_ring_inner_radius(0); + set_emission_ring_cone_angle(90); set_gravity(Vector3(0, -9.8, 0)); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 82ea4bbef3..978bb64e71 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -178,6 +178,7 @@ private: real_t emission_ring_height = 0.0; real_t emission_ring_radius = 0.0; real_t emission_ring_inner_radius = 0.0; + real_t emission_ring_cone_angle = 0.0; Ref<Curve> scale_curve_x; Ref<Curve> scale_curve_y; @@ -282,6 +283,7 @@ public: void set_emission_ring_height(real_t p_height); void set_emission_ring_radius(real_t p_radius); void set_emission_ring_inner_radius(real_t p_radius); + void set_emission_ring_cone_angle(real_t p_angle); void set_scale_curve_x(Ref<Curve> p_scale_curve); void set_scale_curve_y(Ref<Curve> p_scale_curve); void set_scale_curve_z(Ref<Curve> p_scale_curve); @@ -297,6 +299,7 @@ public: real_t get_emission_ring_height() const; real_t get_emission_ring_radius() const; real_t get_emission_ring_inner_radius() const; + real_t get_emission_ring_cone_angle() const; Ref<Curve> get_scale_curve_x() const; Ref<Curve> get_scale_curve_y() const; Ref<Curve> get_scale_curve_z() const; diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 485599d0fb..8702b1d3da 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -163,7 +163,7 @@ void Decal::_validate_property(PropertyInfo &p_property) const { } PackedStringArray Decal::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile rendering backends.")); diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 54631a8dff..195074ba2f 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -116,7 +116,7 @@ AABB FogVolume::get_aabb() const { } PackedStringArray FogVolume::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 3771b385e5..2cef607d29 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -459,14 +459,6 @@ void GPUParticles3D::_notification(int p_what) { // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update. case NOTIFICATION_INTERNAL_PROCESS: { - const Vector3 velocity = (get_global_position() - previous_position) / get_process_delta_time(); - - if (velocity != previous_velocity) { - RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); - previous_velocity = velocity; - } - previous_position = get_global_position(); - if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { @@ -487,8 +479,21 @@ void GPUParticles3D::_notification(int p_what) { } } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Update velocity in physics process, so that velocity calculations remain correct + // if the physics tick rate is lower than the rendered framerate (especially without physics interpolation). + const Vector3 velocity = (get_global_position() - previous_position) / get_physics_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } + previous_position = get_global_position(); + } break; + case NOTIFICATION_ENTER_TREE: { set_process_internal(false); + set_physics_process_internal(false); if (sub_emitter != NodePath()) { _attach_sub_emitter(); } @@ -499,6 +504,7 @@ void GPUParticles3D::_notification(int p_what) { } previous_position = get_global_transform().origin; set_process_internal(true); + set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 3a05ec9c9e..9791f23bc3 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -524,7 +524,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { } PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = GPUParticlesCollision3D::get_configuration_warnings(); if (bake_mask == 0) { warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 54adafbefb..6b3510a72a 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -30,7 +30,7 @@ #include "label_3d.h" -#include "scene/main/viewport.h" +#include "scene/main/window.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" @@ -197,14 +197,14 @@ void Label3D::_notification(int p_what) { if (!pending_update) { _im_update(); } - Viewport *viewport = get_viewport(); - ERR_FAIL_NULL(viewport); - viewport->connect("size_changed", callable_mp(this, &Label3D::_font_changed)); + Window *window = get_window(); + ERR_FAIL_NULL(window); + window->connect("size_changed", callable_mp(this, &Label3D::_font_changed)); } break; case NOTIFICATION_EXIT_TREE: { - Viewport *viewport = get_viewport(); - ERR_FAIL_NULL(viewport); - viewport->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed)); + Window *window = get_window(); + ERR_FAIL_NULL(window); + window->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed)); } break; case NOTIFICATION_TRANSLATION_CHANGED: { String new_text = atr(text); @@ -797,13 +797,13 @@ Ref<Font> Label3D::_get_font_or_default() const { } const StringName theme_name = SceneStringName(font); - List<StringName> theme_types; - ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types); + Vector<StringName> theme_types; + ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types); ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context(); - List<Ref<Theme>> themes = global_context->get_themes(); + Vector<Ref<Theme>> themes = global_context->get_themes(); if (Engine::get_singleton()->is_editor_hint()) { - themes.push_front(ThemeDB::get_singleton()->get_project_theme()); + themes.insert(0, ThemeDB::get_singleton()->get_project_theme()); } for (const Ref<Theme> &theme : themes) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 038a78609f..26a574cd26 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -151,6 +151,14 @@ bool LightmapGIData::is_using_spherical_harmonics() const { return uses_spherical_harmonics; } +void LightmapGIData::_set_uses_packed_directional(bool p_enable) { + _uses_packed_directional = p_enable; +} + +bool LightmapGIData::_is_using_packed_directional() const { + return _uses_packed_directional; +} + void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure) { if (p_points.size()) { int pc = p_points.size(); @@ -255,6 +263,9 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics", "uses_spherical_harmonics"), &LightmapGIData::set_uses_spherical_harmonics); ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics"), &LightmapGIData::is_using_spherical_harmonics); + ClassDB::bind_method(D_METHOD("_set_uses_packed_directional", "_uses_packed_directional"), &LightmapGIData::_set_uses_packed_directional); + ClassDB::bind_method(D_METHOD("_is_using_packed_directional"), &LightmapGIData::_is_using_packed_directional); + ClassDB::bind_method(D_METHOD("add_user", "path", "uv_scale", "slice_index", "sub_instance"), &LightmapGIData::add_user); ClassDB::bind_method(D_METHOD("get_user_count"), &LightmapGIData::get_user_count); ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &LightmapGIData::get_user_path); @@ -267,6 +278,7 @@ void LightmapGIData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "_uses_packed_directional", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_uses_packed_directional", "_is_using_packed_directional"); #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &LightmapGIData::set_light_texture); @@ -709,7 +721,7 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f const Vector3 *pp = probe_positions.ptr(); bool exists = false; for (int j = 0; j < ppcount; j++) { - if (pp[j].is_equal_approx(real_pos)) { + if (pp[j].distance_to(real_pos) < (p_cell_size * 0.5f)) { exists = true; break; } @@ -1072,6 +1084,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa if (env.is_valid()) { environment_image = RS::get_singleton()->environment_bake_panorama(env->get_rid(), true, Size2i(128, 64)); + environment_transform = Basis::from_euler(env->get_sky_rotation()).inverse(); } } } break; @@ -1186,6 +1199,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } gi_data->set_lightmap_textures(textures); + gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically. gi_data->set_uses_spherical_harmonics(directional); for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) { @@ -1351,6 +1365,12 @@ void LightmapGI::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POST_ENTER_TREE: { if (light_data.is_valid()) { + ERR_FAIL_COND_MSG( + light_data->is_using_spherical_harmonics() && !light_data->_is_using_packed_directional(), + vformat( + "%s (%s): The directional lightmap textures are stored in a format that isn't supported anymore. Please bake lightmaps again to make lightmaps display from this node again.", + get_light_data()->get_path(), get_name())); + _assign_lightmaps(); } } break; @@ -1580,7 +1600,7 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { } PackedStringArray LightmapGI::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("Lightmap can only be baked from a device that supports the RD backends. Lightmap baking may fail.")); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 67480132b6..6377c420d1 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -49,6 +49,8 @@ class LightmapGIData : public Resource { bool uses_spherical_harmonics = false; bool interior = false; + bool _uses_packed_directional = false; + RID lightmap; AABB bounds; float baked_exposure = 1.0; @@ -92,6 +94,9 @@ public: void set_uses_spherical_harmonics(bool p_enable); bool is_using_spherical_harmonics() const; + void _set_uses_packed_directional(bool p_enable); + bool _is_using_packed_directional() const; + bool is_interior() const; float get_baked_exposure() const; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 85bf8846b9..f551cb401c 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -517,12 +517,12 @@ bool MeshInstance3D::_property_get_revert(const StringName &p_name, Variant &r_p Ref<ArrayMesh> MeshInstance3D::bake_mesh_from_current_blend_shape_mix(Ref<ArrayMesh> p_existing) { Ref<ArrayMesh> source_mesh = get_mesh(); - ERR_FAIL_NULL_V_MSG(source_mesh, Ref<ArrayMesh>(), "The source mesh must be a valid ArrayMesh."); + ERR_FAIL_COND_V_MSG(source_mesh.is_null(), Ref<ArrayMesh>(), "The source mesh must be a valid ArrayMesh."); Ref<ArrayMesh> bake_mesh; if (p_existing.is_valid()) { - ERR_FAIL_NULL_V_MSG(p_existing, Ref<ArrayMesh>(), "The existing mesh must be a valid ArrayMesh."); + ERR_FAIL_COND_V_MSG(p_existing.is_null(), Ref<ArrayMesh>(), "The existing mesh must be a valid ArrayMesh."); ERR_FAIL_COND_V_MSG(source_mesh == p_existing, Ref<ArrayMesh>(), "The source mesh can not be the same mesh as the existing mesh."); bake_mesh = p_existing; @@ -671,6 +671,172 @@ Ref<ArrayMesh> MeshInstance3D::bake_mesh_from_current_blend_shape_mix(Ref<ArrayM return bake_mesh; } +Ref<ArrayMesh> MeshInstance3D::bake_mesh_from_current_skeleton_pose(Ref<ArrayMesh> p_existing) { + Ref<ArrayMesh> source_mesh = get_mesh(); + ERR_FAIL_COND_V_MSG(source_mesh.is_null(), Ref<ArrayMesh>(), "The source mesh must be a valid ArrayMesh."); + + Ref<ArrayMesh> bake_mesh; + + if (p_existing.is_valid()) { + ERR_FAIL_COND_V_MSG(source_mesh == p_existing, Ref<ArrayMesh>(), "The source mesh can not be the same mesh as the existing mesh."); + + bake_mesh = p_existing; + } else { + bake_mesh.instantiate(); + } + + ERR_FAIL_COND_V_MSG(skin_ref.is_null(), Ref<ArrayMesh>(), "The source mesh must have a valid skin."); + ERR_FAIL_COND_V_MSG(skin_internal.is_null(), Ref<ArrayMesh>(), "The source mesh must have a valid skin."); + RID skeleton = skin_ref->get_skeleton(); + ERR_FAIL_COND_V_MSG(!skeleton.is_valid(), Ref<ArrayMesh>(), "The source mesh must have its skin registered with a valid skeleton."); + + const int bone_count = RenderingServer::get_singleton()->skeleton_get_bone_count(skeleton); + ERR_FAIL_COND_V(bone_count <= 0, Ref<ArrayMesh>()); + ERR_FAIL_COND_V(bone_count < skin_internal->get_bind_count(), Ref<ArrayMesh>()); + + LocalVector<Transform3D> bone_transforms; + bone_transforms.resize(bone_count); + for (int bone_index = 0; bone_index < bone_count; bone_index++) { + bone_transforms[bone_index] = RenderingServer::get_singleton()->skeleton_bone_get_transform(skeleton, bone_index); + } + + bake_mesh->clear_surfaces(); + + int mesh_surface_count = source_mesh->get_surface_count(); + + for (int surface_index = 0; surface_index < mesh_surface_count; surface_index++) { + ERR_CONTINUE(source_mesh->surface_get_primitive_type(surface_index) != Mesh::PRIMITIVE_TRIANGLES); + + uint32_t surface_format = source_mesh->surface_get_format(surface_index); + + ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_VERTEX)); + ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_BONES)); + ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_WEIGHTS)); + + unsigned int bones_per_vertex = surface_format & Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS ? 8 : 4; + + surface_format &= ~Mesh::ARRAY_FORMAT_BONES; + surface_format &= ~Mesh::ARRAY_FORMAT_WEIGHTS; + + const Array &source_mesh_arrays = source_mesh->surface_get_arrays(surface_index); + + ERR_FAIL_COND_V(source_mesh_arrays.size() != RS::ARRAY_MAX, Ref<ArrayMesh>()); + + const Vector<Vector3> &source_mesh_vertex_array = source_mesh_arrays[Mesh::ARRAY_VERTEX]; + const Vector<Vector3> &source_mesh_normal_array = source_mesh_arrays[Mesh::ARRAY_NORMAL]; + const Vector<float> &source_mesh_tangent_array = source_mesh_arrays[Mesh::ARRAY_TANGENT]; + const Vector<int> &source_mesh_bones_array = source_mesh_arrays[Mesh::ARRAY_BONES]; + const Vector<float> &source_mesh_weights_array = source_mesh_arrays[Mesh::ARRAY_WEIGHTS]; + + unsigned int vertex_count = source_mesh_vertex_array.size(); + int expected_bone_array_size = vertex_count * bones_per_vertex; + ERR_CONTINUE(source_mesh_bones_array.size() != expected_bone_array_size); + ERR_CONTINUE(source_mesh_weights_array.size() != expected_bone_array_size); + + Array new_mesh_arrays; + new_mesh_arrays.resize(Mesh::ARRAY_MAX); + for (int i = 0; i < source_mesh_arrays.size(); i++) { + if (i == Mesh::ARRAY_VERTEX || i == Mesh::ARRAY_NORMAL || i == Mesh::ARRAY_TANGENT || i == Mesh::ARRAY_BONES || i == Mesh::ARRAY_WEIGHTS) { + continue; + } + new_mesh_arrays[i] = source_mesh_arrays[i]; + } + + bool use_normal_array = source_mesh_normal_array.size() == source_mesh_vertex_array.size(); + bool use_tangent_array = source_mesh_tangent_array.size() / 4 == source_mesh_vertex_array.size(); + + Vector<Vector3> lerped_vertex_array = source_mesh_vertex_array; + Vector<Vector3> lerped_normal_array = source_mesh_normal_array; + Vector<float> lerped_tangent_array = source_mesh_tangent_array; + + const Vector3 *source_vertices_ptr = source_mesh_vertex_array.ptr(); + const Vector3 *source_normals_ptr = source_mesh_normal_array.ptr(); + const float *source_tangents_ptr = source_mesh_tangent_array.ptr(); + const int *source_bones_ptr = source_mesh_bones_array.ptr(); + const float *source_weights_ptr = source_mesh_weights_array.ptr(); + + Vector3 *lerped_vertices_ptrw = lerped_vertex_array.ptrw(); + Vector3 *lerped_normals_ptrw = lerped_normal_array.ptrw(); + float *lerped_tangents_ptrw = lerped_tangent_array.ptrw(); + + for (unsigned int vertex_index = 0; vertex_index < vertex_count; vertex_index++) { + Vector3 lerped_vertex; + Vector3 lerped_normal; + Vector3 lerped_tangent; + + const Vector3 &source_vertex = source_vertices_ptr[vertex_index]; + + Vector3 source_normal; + if (use_normal_array) { + source_normal = source_normals_ptr[vertex_index]; + } + + int tangent_index = vertex_index * 4; + Vector4 source_tangent; + Vector3 source_tangent_vec3; + if (use_tangent_array) { + source_tangent = Vector4( + source_tangents_ptr[tangent_index], + source_tangents_ptr[tangent_index + 1], + source_tangents_ptr[tangent_index + 2], + source_tangents_ptr[tangent_index + 3]); + + DEV_ASSERT(source_tangent.w == 1.0 || source_tangent.w == -1.0); + + source_tangent_vec3 = Vector3(source_tangent.x, source_tangent.y, source_tangent.z); + } + + for (unsigned int weight_index = 0; weight_index < bones_per_vertex; weight_index++) { + float bone_weight = source_weights_ptr[vertex_index * bones_per_vertex + weight_index]; + if (bone_weight < FLT_EPSILON) { + continue; + } + int vertex_bone_index = source_bones_ptr[vertex_index * bones_per_vertex + weight_index]; + const Transform3D &bone_transform = bone_transforms[vertex_bone_index]; + const Basis bone_basis = bone_transform.basis.orthonormalized(); + + ERR_FAIL_INDEX_V(vertex_bone_index, static_cast<int>(bone_transforms.size()), Ref<ArrayMesh>()); + + lerped_vertex += source_vertex.lerp(bone_transform.xform(source_vertex), bone_weight) - source_vertex; + ; + + if (use_normal_array) { + lerped_normal += source_normal.lerp(bone_basis.xform(source_normal), bone_weight) - source_normal; + } + + if (use_tangent_array) { + lerped_tangent += source_tangent_vec3.lerp(bone_basis.xform(source_tangent_vec3), bone_weight) - source_tangent_vec3; + } + } + + lerped_vertices_ptrw[vertex_index] += lerped_vertex; + + if (use_normal_array) { + lerped_normals_ptrw[vertex_index] = (source_normal + lerped_normal).normalized(); + } + + if (use_tangent_array) { + lerped_tangent = (source_tangent_vec3 + lerped_tangent).normalized(); + lerped_tangents_ptrw[tangent_index] = lerped_tangent.x; + lerped_tangents_ptrw[tangent_index + 1] = lerped_tangent.y; + lerped_tangents_ptrw[tangent_index + 2] = lerped_tangent.z; + } + } + + new_mesh_arrays[Mesh::ARRAY_VERTEX] = lerped_vertex_array; + if (use_normal_array) { + new_mesh_arrays[Mesh::ARRAY_NORMAL] = lerped_normal_array; + } + if (use_tangent_array) { + new_mesh_arrays[Mesh::ARRAY_TANGENT] = lerped_tangent_array; + } + + bake_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, new_mesh_arrays, Array(), Dictionary(), surface_format); + } + + return bake_mesh; +} + void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshInstance3D::set_mesh); ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance3D::get_mesh); @@ -700,6 +866,7 @@ void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents); ClassDB::bind_method(D_METHOD("bake_mesh_from_current_blend_shape_mix", "existing"), &MeshInstance3D::bake_mesh_from_current_blend_shape_mix, DEFVAL(Ref<ArrayMesh>())); + ClassDB::bind_method(D_METHOD("bake_mesh_from_current_skeleton_pose", "existing"), &MeshInstance3D::bake_mesh_from_current_skeleton_pose, DEFVAL(Ref<ArrayMesh>())); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_GROUP("Skeleton", ""); diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 8a7e03c5b3..0eff12762d 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -102,6 +102,7 @@ public: virtual AABB get_aabb() const override; Ref<ArrayMesh> bake_mesh_from_current_blend_shape_mix(Ref<ArrayMesh> p_existing = Ref<ArrayMesh>()); + Ref<ArrayMesh> bake_mesh_from_current_skeleton_pose(Ref<ArrayMesh> p_existing = Ref<ArrayMesh>()); MeshInstance3D(); ~MeshInstance3D(); diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 55d6e49e6c..2eef1dbbf4 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -30,16 +30,35 @@ #include "multimesh_instance_3d.h" +void MultiMeshInstance3D::_refresh_interpolated() { + if (is_inside_tree() && multimesh.is_valid()) { + bool interpolated = is_physics_interpolated_and_enabled(); + multimesh->set_physics_interpolated(interpolated); + } +} + +void MultiMeshInstance3D::_physics_interpolated_changed() { + VisualInstance3D::_physics_interpolated_changed(); + _refresh_interpolated(); +} + void MultiMeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance3D::set_multimesh); ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance3D::get_multimesh); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh"); } +void MultiMeshInstance3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + _refresh_interpolated(); + } +} + void MultiMeshInstance3D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { multimesh = p_multimesh; if (multimesh.is_valid()) { set_base(multimesh->get_rid()); + _refresh_interpolated(); } else { set_base(RID()); } diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index 404f31d1e3..c9507b1047 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -39,9 +39,12 @@ class MultiMeshInstance3D : public GeometryInstance3D { Ref<MultiMesh> multimesh; + void _refresh_interpolated(); + protected: + virtual void _physics_interpolated_changed() override; static void _bind_methods(); - // bind helpers + void _notification(int p_what); public: void set_multimesh(const Ref<MultiMesh> &p_multimesh); diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index dc776ebea2..0cce21b9d0 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -152,6 +152,9 @@ void NavigationLink3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink3D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional); ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional); @@ -217,16 +220,7 @@ bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const { void NavigationLink3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (enabled) { - NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); - } - current_global_transform = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); - -#ifdef DEBUG_ENABLED - _update_debug_mesh(); -#endif // DEBUG_ENABLED + _link_enter_navigation_map(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -235,30 +229,11 @@ void NavigationLink3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); - if (is_inside_tree()) { - Transform3D new_global_transform = get_global_transform(); - if (current_global_transform != new_global_transform) { - current_global_transform = new_global_transform; - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform); - } -#endif // DEBUG_ENABLED - } - } + _link_update_transform(); } break; case NOTIFICATION_EXIT_TREE: { - NavigationServer3D::get_singleton()->link_set_map(link, RID()); - -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_scenario(debug_instance, RID()); - RS::get_singleton()->instance_set_visible(debug_instance, false); - } -#endif // DEBUG_ENABLED + _link_exit_navigation_map(); } break; } } @@ -320,6 +295,25 @@ void NavigationLink3D::set_enabled(bool p_enabled) { update_gizmos(); } +void NavigationLink3D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer3D::get_singleton()->link_set_map(link, map_override); +} + +RID NavigationLink3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_3d()->get_navigation_map(); + } + return RID(); +} + void NavigationLink3D::set_bidirectional(bool p_bidirectional) { if (bidirectional == p_bidirectional) { return; @@ -459,7 +453,7 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { } PackedStringArray NavigationLink3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (start_position.is_equal_approx(end_position)) { warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful.")); @@ -467,3 +461,53 @@ PackedStringArray NavigationLink3D::get_configuration_warnings() const { return warnings; } + +void NavigationLink3D::_link_enter_navigation_map() { + if (!is_inside_tree()) { + return; + } + + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->link_set_map(link, map_override); + } else { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + } + + current_global_transform = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); + +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_exit_navigation_map() { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_update_transform() { + if (!is_inside_tree()) { + return; + } + + Transform3D new_global_transform = get_global_transform(); + if (current_global_transform != new_global_transform) { + current_global_transform = new_global_transform; + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED + } +} diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1867082811..e894761f40 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -38,6 +38,7 @@ class NavigationLink3D : public Node3D { bool enabled = true; RID link; + RID map_override; bool bidirectional = true; uint32_t navigation_layers = 1; Vector3 end_position; @@ -72,6 +73,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_bidirectional(bool p_bidirectional); bool is_bidirectional() const { return bidirectional; } @@ -100,6 +104,11 @@ public: real_t get_travel_cost() const { return travel_cost; } PackedStringArray get_configuration_warnings() const override; + +private: + void _link_enter_navigation_map(); + void _link_exit_navigation_map(); + void _link_update_transform(); }; #endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 40e04f0fb4..c0c254e7ed 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -271,7 +271,7 @@ bool NavigationRegion3D::is_baking() const { } PackedStringArray NavigationRegion3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!navigation_mesh.is_valid()) { @@ -686,6 +686,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector<Vector3> vertex_array; vertex_array.resize(connections_count * 6); + Vector3 *vertex_array_ptrw = vertex_array.ptrw(); + int vertex_array_index = 0; for (int i = 0; i < connections_count; i++) { Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i); @@ -705,13 +707,12 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin); Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(left_start_pos); - vertex_array.push_back(right_start_pos); - - vertex_array.push_back(left_end_pos); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(right_start_pos); + vertex_array_ptrw[vertex_array_index++] = connection_pathway_start; + vertex_array_ptrw[vertex_array_index++] = connection_pathway_end; + vertex_array_ptrw[vertex_array_index++] = left_start_pos; + vertex_array_ptrw[vertex_array_index++] = right_start_pos; + vertex_array_ptrw[vertex_array_index++] = left_end_pos; + vertex_array_ptrw[vertex_array_index++] = right_end_pos; } if (vertex_array.size() == 0) { @@ -724,7 +725,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { mesh_array.resize(Mesh::ARRAY_MAX); mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; - debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array); + debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); debug_edge_connections_mesh->surface_set_material(0, edge_connections_material); RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid()); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 2e08afb30d..86ce8a881a 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -30,6 +30,7 @@ #include "node_3d.h" +#include "core/math/transform_interpolator.h" #include "scene/3d/visual_instance_3d.h" #include "scene/main/viewport.h" #include "scene/property_utils.h" @@ -176,6 +177,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; _update_visibility_parent(true); + _disable_client_physics_interpolation(); } break; case NOTIFICATION_ENTER_WORLD: { @@ -226,6 +228,12 @@ void Node3D::_notification(int p_what) { } #endif } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (data.client_physics_interpolation_data) { + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + } + } break; } } @@ -341,6 +349,119 @@ Transform3D Node3D::get_transform() const { return data.local_transform; } +// Return false to timeout and remove from the client interpolation list. +bool Node3D::update_client_physics_interpolation_data() { + if (!is_inside_tree() || !_is_physics_interpolated_client_side()) { + return false; + } + + ERR_FAIL_NULL_V(data.client_physics_interpolation_data, false); + ClientPhysicsInterpolationData &pid = *data.client_physics_interpolation_data; + + uint64_t tick = Engine::get_singleton()->get_physics_frames(); + + // Has this update been done already this tick? + // (For instance, get_global_transform_interpolated() could be called multiple times.) + if (pid.current_physics_tick != tick) { + // Timeout? + if (tick >= pid.timeout_physics_tick) { + return false; + } + + if (pid.current_physics_tick == (tick - 1)) { + // Normal interpolation situation, there is a continuous flow of data + // from one tick to the next... + pid.global_xform_prev = pid.global_xform_curr; + } else { + // There has been a gap, we cannot sensibly offer interpolation over + // a multitick gap, so we will teleport. + pid.global_xform_prev = get_global_transform(); + } + pid.current_physics_tick = tick; + } + + pid.global_xform_curr = get_global_transform(); + return true; +} + +void Node3D::_disable_client_physics_interpolation() { + // Disable any current client side interpolation. + // (This can always restart as normal if you later re-attach the node to the SceneTree.) + if (data.client_physics_interpolation_data) { + memdelete(data.client_physics_interpolation_data); + data.client_physics_interpolation_data = nullptr; + + SceneTree *tree = get_tree(); + if (tree && _client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_remove_node_3d(&_client_physics_interpolation_node_3d_list); + } + } + _set_physics_interpolated_client_side(false); +} + +Transform3D Node3D::_get_global_transform_interpolated(real_t p_interpolation_fraction) { + ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); + + // Set in motion the mechanisms for client side interpolation if not already active. + if (!_is_physics_interpolated_client_side()) { + _set_physics_interpolated_client_side(true); + + ERR_FAIL_COND_V(data.client_physics_interpolation_data != nullptr, Transform3D()); + data.client_physics_interpolation_data = memnew(ClientPhysicsInterpolationData); + data.client_physics_interpolation_data->global_xform_curr = get_global_transform(); + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + data.client_physics_interpolation_data->current_physics_tick = Engine::get_singleton()->get_physics_frames(); + } + + // Storing the last tick we requested client interpolation allows us to timeout + // and remove client interpolated nodes from the list to save processing. + // We use some arbitrary timeout here, but this could potentially be user defined. + + // Note: This timeout has to be larger than the number of ticks in a frame, otherwise the interpolated + // data will stop flowing before the next frame is drawn. This should only be relevant at high tick rates. + // We could alternatively do this by frames rather than ticks and avoid this problem, but then the behavior + // would be machine dependent. + data.client_physics_interpolation_data->timeout_physics_tick = Engine::get_singleton()->get_physics_frames() + 256; + + // Make sure data is up to date. + update_client_physics_interpolation_data(); + + // Interpolate the current data. + const Transform3D &xform_curr = data.client_physics_interpolation_data->global_xform_curr; + const Transform3D &xform_prev = data.client_physics_interpolation_data->global_xform_prev; + + Transform3D res; + TransformInterpolator::interpolate_transform_3d(xform_prev, xform_curr, res, p_interpolation_fraction); + + SceneTree *tree = get_tree(); + + // This should not happen, as is_inside_tree() is checked earlier. + ERR_FAIL_NULL_V(tree, res); + if (!_client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_add_node_3d(&_client_physics_interpolation_node_3d_list); + } + + return res; +} + +Transform3D Node3D::get_global_transform_interpolated() { + // Pass through if physics interpolation is switched off. + // This is a convenience, as it allows you to easy turn off interpolation + // without changing any code. + if (!is_physics_interpolated_and_enabled()) { + return get_global_transform(); + } + + // If we are in the physics frame, the interpolated global transform is meaningless. + // However, there is an exception, we may want to use this as a means of starting off the client + // interpolation pump if not already started (when _is_physics_interpolated_client_side() is false). + if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) { + return get_global_transform(); + } + + return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction()); +} + Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); @@ -1140,6 +1261,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); + ClassDB::bind_method(D_METHOD("get_global_transform_interpolated"), &Node3D::get_global_transform_interpolated); ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node3D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node3D::get_global_position); ClassDB::bind_method(D_METHOD("set_global_basis", "basis"), &Node3D::set_global_basis); @@ -1236,4 +1358,27 @@ void Node3D::_bind_methods() { } Node3D::Node3D() : - xform_change(this) {} + xform_change(this), _client_physics_interpolation_node_3d_list(this) { + // Default member initializer for bitfield is a C++20 extension, so: + + data.top_level = false; + data.inside_world = false; + + data.ignore_notification = false; + data.notify_local_transform = false; + data.notify_transform = false; + + data.visible = true; + data.disable_scale = false; + data.vi_visible = true; + +#ifdef TOOLS_ENABLED + data.gizmos_disabled = false; + data.gizmos_dirty = false; + data.transform_gizmo_visible = true; +#endif +} + +Node3D::~Node3D() { + _disable_client_physics_interpolation(); +} diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index c1667221df..217ee28cf1 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -85,7 +85,15 @@ private: DIRTY_GLOBAL_TRANSFORM = 4 }; + struct ClientPhysicsInterpolationData { + Transform3D global_xform_curr; + Transform3D global_xform_prev; + uint64_t current_physics_tick = 0; + uint64_t timeout_physics_tick = 0; + }; + mutable SelfList<Node> xform_change; + SelfList<Node3D> _client_physics_interpolation_node_3d_list; // This Data struct is to avoid namespace pollution in derived classes. @@ -101,8 +109,19 @@ private: Viewport *viewport = nullptr; - bool top_level = false; - bool inside_world = false; + bool top_level : 1; + bool inside_world : 1; + + // This is cached, and only currently kept up to date in visual instances. + // This is set if a visual instance is (a) in the tree AND (b) visible via is_visible_in_tree() call. + bool vi_visible : 1; + + bool ignore_notification : 1; + bool notify_local_transform : 1; + bool notify_transform : 1; + + bool visible : 1; + bool disable_scale : 1; RID visibility_parent; @@ -110,18 +129,13 @@ private: List<Node3D *> children; List<Node3D *>::Element *C = nullptr; - bool ignore_notification = false; - bool notify_local_transform = false; - bool notify_transform = false; - - bool visible = true; - bool disable_scale = false; + ClientPhysicsInterpolationData *client_physics_interpolation_data = nullptr; #ifdef TOOLS_ENABLED Vector<Ref<Node3DGizmo>> gizmos; - bool gizmos_disabled = false; - bool gizmos_dirty = false; - bool transform_gizmo_visible = true; + bool gizmos_disabled : 1; + bool gizmos_dirty : 1; + bool transform_gizmo_visible : 1; #endif } data; @@ -150,6 +164,11 @@ protected: _FORCE_INLINE_ void _update_local_transform() const; _FORCE_INLINE_ void _update_rotation_and_scale() const; + void _set_vi_visible(bool p_visible) { data.vi_visible = p_visible; } + bool _is_vi_visible() const { return data.vi_visible; } + Transform3D _get_global_transform_interpolated(real_t p_interpolation_fraction); + void _disable_client_physics_interpolation(); + void _notification(int p_what); static void _bind_methods(); @@ -208,6 +227,9 @@ public: Quaternion get_quaternion() const; Transform3D get_global_transform() const; + Transform3D get_global_transform_interpolated(); + bool update_client_physics_interpolation_data(); + #ifdef TOOLS_ENABLED virtual Transform3D get_global_gizmo_transform() const; virtual Transform3D get_local_gizmo_transform() const; @@ -279,6 +301,7 @@ public: NodePath get_visibility_parent() const; Node3D(); + ~Node3D(); }; VARIANT_ENUM_CAST(Node3D::RotationEditMode) diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 150771545b..6d88323c76 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -129,9 +129,6 @@ void Occluder3D::_notification(int p_what) { } } -void Occluder3D::_bind_methods() { -} - Occluder3D::Occluder3D() { occluder = RS::get_singleton()->occluder_create(); } @@ -694,7 +691,7 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, } PackedStringArray OccluderInstance3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling.")); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 91445710b3..62e9478527 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -49,7 +49,6 @@ protected: void _update(); virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0; - static void _bind_methods(); void _notification(int p_what); public: diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 1f8f7cd54c..64259a24b0 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -216,24 +216,7 @@ void Path3D::_bind_methods() { ADD_SIGNAL(MethodInfo("curve_changed")); } -// Update transform, in deferred mode by default to avoid superfluity. -void PathFollow3D::update_transform(bool p_immediate) { - transform_dirty = true; - - if (p_immediate) { - _update_transform(); - } else { - callable_mp(this, &PathFollow3D::_update_transform).call_deferred(); - } -} - -// Update transform immediately . -void PathFollow3D::_update_transform() { - if (!transform_dirty) { - return; - } - transform_dirty = false; - +void PathFollow3D::update_transform() { if (!path) { return; } @@ -286,9 +269,7 @@ void PathFollow3D::_notification(int p_what) { Node *parent = get_parent(); if (parent) { path = Object::cast_to<Path3D>(parent); - if (path) { - update_transform(); - } + update_transform(); } } break; @@ -318,7 +299,7 @@ void PathFollow3D::_validate_property(PropertyInfo &p_property) const { } PackedStringArray PathFollow3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path3D>(get_parent())) { @@ -414,6 +395,9 @@ void PathFollow3D::_bind_methods() { void PathFollow3D::set_progress(real_t p_progress) { ERR_FAIL_COND(!isfinite(p_progress)); + if (progress == p_progress) { + return; + } progress = p_progress; if (path) { @@ -435,10 +419,11 @@ void PathFollow3D::set_progress(real_t p_progress) { } void PathFollow3D::set_h_offset(real_t p_h_offset) { - h_offset = p_h_offset; - if (path) { - update_transform(); + if (h_offset == p_h_offset) { + return; } + h_offset = p_h_offset; + update_transform(); } real_t PathFollow3D::get_h_offset() const { @@ -446,10 +431,11 @@ real_t PathFollow3D::get_h_offset() const { } void PathFollow3D::set_v_offset(real_t p_v_offset) { - v_offset = p_v_offset; - if (path) { - update_transform(); + if (v_offset == p_v_offset) { + return; } + v_offset = p_v_offset; + update_transform(); } real_t PathFollow3D::get_v_offset() const { @@ -461,9 +447,10 @@ real_t PathFollow3D::get_progress() const { } void PathFollow3D::set_progress_ratio(real_t p_ratio) { - if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { - set_progress(p_ratio * path->get_curve()->get_baked_length()); - } + ERR_FAIL_NULL_MSG(path, "Can only set progress ratio on a PathFollow3D that is the child of a Path3D which is itself part of the scene tree."); + ERR_FAIL_COND_MSG(path->get_curve().is_null(), "Can't set progress ratio on a PathFollow3D that does not have a Curve."); + ERR_FAIL_COND_MSG(!path->get_curve()->get_baked_length(), "Can't set progress ratio on a PathFollow3D that has a 0 length curve."); + set_progress(p_ratio * path->get_curve()->get_baked_length()); } real_t PathFollow3D::get_progress_ratio() const { @@ -475,6 +462,9 @@ real_t PathFollow3D::get_progress_ratio() const { } void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { + if (rotation_mode == p_rotation_mode) { + return; + } rotation_mode = p_rotation_mode; update_configuration_warnings(); @@ -486,6 +476,9 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { } void PathFollow3D::set_use_model_front(bool p_use_model_front) { + if (use_model_front == p_use_model_front) { + return; + } use_model_front = p_use_model_front; update_transform(); } @@ -495,6 +488,9 @@ bool PathFollow3D::is_using_model_front() const { } void PathFollow3D::set_loop(bool p_loop) { + if (loop == p_loop) { + return; + } loop = p_loop; update_transform(); } @@ -504,6 +500,9 @@ bool PathFollow3D::has_loop() const { } void PathFollow3D::set_tilt_enabled(bool p_enabled) { + if (tilt_enabled == p_enabled) { + return; + } tilt_enabled = p_enabled; update_transform(); } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 0c9111bb8e..fb4f301375 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -90,7 +90,6 @@ protected: void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); - void _update_transform(); static void _bind_methods(); @@ -124,7 +123,7 @@ public: PackedStringArray get_configuration_warnings() const override; - void update_transform(bool p_immediate = false); + void update_transform(); static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); diff --git a/scene/3d/physical_bone_simulator_3d.cpp b/scene/3d/physical_bone_simulator_3d.cpp index ffe79e0892..8874c9cfc6 100644 --- a/scene/3d/physical_bone_simulator_3d.cpp +++ b/scene/3d/physical_bone_simulator_3d.cpp @@ -73,10 +73,15 @@ void PhysicalBoneSimulator3D::_pose_updated() { } ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); for (int i = 0; i < skeleton->get_bone_count(); i++) { - bones.write[i].global_pose = skeleton->get_bone_global_pose(i); + _bone_pose_updated(skeleton, i); } } +void PhysicalBoneSimulator3D::_bone_pose_updated(Skeleton3D *p_skeleton, int p_bone_id) { + ERR_FAIL_INDEX(p_bone_id, bones.size()); + bones.write[p_bone_id].global_pose = p_skeleton->get_bone_global_pose(p_bone_id); +} + void PhysicalBoneSimulator3D::_set_active(bool p_active) { if (!Engine::get_singleton()->is_editor_hint()) { _reset_physical_bones_state(); @@ -285,11 +290,11 @@ void _pb_start_simulation(const PhysicalBoneSimulator3D *p_simulator, Node *p_no } void PhysicalBoneSimulator3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) { + _pose_updated(); + simulating = true; _reset_physical_bones_state(); - _pose_updated(); - Vector<int> sim_bones; if (p_bones.size() > 0) { sim_bones.resize(p_bones.size()); @@ -357,47 +362,17 @@ void PhysicalBoneSimulator3D::_process_modification() { if (!skeleton) { return; } - if (!enabled) { - for (int i = 0; i < bones.size(); i++) { - if (bones[i].physical_bone) { - if (bones[i].physical_bone->is_simulating_physics() == false) { - bones[i].physical_bone->reset_to_rest_position(); - } - } + ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (!bones[i].physical_bone) { + continue; } - } else { - ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); - for (int i = 0; i < skeleton->get_bone_count(); i++) { - if (!bones[i].physical_bone) { - continue; - } + if (bones[i].physical_bone->is_simulating_physics() == false) { + _bone_pose_updated(skeleton, i); + bones[i].physical_bone->reset_to_rest_position(); + } else if (simulating) { skeleton->set_bone_global_pose(i, bones[i].global_pose); } - - // TODO: - // The above method is performance heavy and needs to be improved. - // Ideally, the processing of set_bone_global_pose within Skeleton3D should be improved, - // but the workaround available now is to convert the global pose to a local pose on the SkeletonModifier side. - // However, the follow method needs recursive processing for deformations within PhysicalBoneSimulator3D to account for update order. - /* - ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); - LocalVector<Transform3D> local_poses; - for (int i = 0; i < skeleton->get_bone_count(); i++) { - Transform3D pt; - if (skeleton->get_bone_parent(i) >= 0) { - pt = get_bone_global_pose(skeleton->get_bone_parent(i)); - } - local_poses.push_back(pt.affine_inverse() * bones[i].global_pose); - } - for (int i = 0; i < skeleton->get_bone_count(); i++) { - if (!bones[i].physical_bone) { - continue; - } - skeleton->set_bone_pose_position(i, local_poses[i].origin); - skeleton->set_bone_pose_rotation(i, local_poses[i].basis.get_rotation_quaternion()); - skeleton->set_bone_pose_scale(i, local_poses[i].basis.get_scale()); - } - */ } } diff --git a/scene/3d/physical_bone_simulator_3d.h b/scene/3d/physical_bone_simulator_3d.h index ee900e0e77..24136be2b8 100644 --- a/scene/3d/physical_bone_simulator_3d.h +++ b/scene/3d/physical_bone_simulator_3d.h @@ -41,7 +41,6 @@ class PhysicalBoneSimulator3D : public SkeletonModifier3D { GDCLASS(PhysicalBoneSimulator3D, SkeletonModifier3D); bool simulating = false; - bool enabled = true; struct SimulatedBone { int parent; @@ -74,6 +73,7 @@ protected: void _bone_list_changed(); void _pose_updated(); + void _bone_pose_updated(Skeleton3D *skeleton, int p_bone_id); virtual void _process_modification() override; diff --git a/scene/3d/physics/collision_object_3d.cpp b/scene/3d/physics/collision_object_3d.cpp index f11aa7012a..f0a5013ca2 100644 --- a/scene/3d/physics/collision_object_3d.cpp +++ b/scene/3d/physics/collision_object_3d.cpp @@ -731,7 +731,7 @@ bool CollisionObject3D::get_capture_input_on_drag() const { } PackedStringArray CollisionObject3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (shapes.is_empty()) { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); diff --git a/scene/3d/physics/collision_polygon_3d.cpp b/scene/3d/physics/collision_polygon_3d.cpp index 76cd4db779..bf8dec7b54 100644 --- a/scene/3d/physics/collision_polygon_3d.cpp +++ b/scene/3d/physics/collision_polygon_3d.cpp @@ -169,7 +169,7 @@ void CollisionPolygon3D::set_margin(real_t p_margin) { } PackedStringArray CollisionPolygon3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp index f3492a3cf3..304fa74b06 100644 --- a/scene/3d/physics/collision_shape_3d.cpp +++ b/scene/3d/physics/collision_shape_3d.cpp @@ -120,7 +120,7 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) { #endif PackedStringArray CollisionShape3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(get_parent()); if (col_object == nullptr) { diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp index ada238c7f2..8ad651fdf5 100644 --- a/scene/3d/physics/shape_cast_3d.cpp +++ b/scene/3d/physics/shape_cast_3d.cpp @@ -157,7 +157,7 @@ void ShapeCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled); - ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result); + ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast3D::get_collision_result); ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color); ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color); @@ -169,7 +169,7 @@ void ShapeCast3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result"); ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); @@ -475,7 +475,7 @@ bool ShapeCast3D::is_collide_with_bodies_enabled() const { return collide_with_bodies; } -Array ShapeCast3D::_get_collision_result() const { +Array ShapeCast3D::get_collision_result() const { Array ret; for (int i = 0; i < result.size(); ++i) { diff --git a/scene/3d/physics/shape_cast_3d.h b/scene/3d/physics/shape_cast_3d.h index 19b73e3f72..9fc5e71670 100644 --- a/scene/3d/physics/shape_cast_3d.h +++ b/scene/3d/physics/shape_cast_3d.h @@ -73,8 +73,6 @@ class ShapeCast3D : public Node3D { real_t collision_safe_fraction = 1.0; real_t collision_unsafe_fraction = 1.0; - Array _get_collision_result() const; - RID debug_instance; Ref<ArrayMesh> debug_mesh; @@ -123,6 +121,7 @@ public: Ref<StandardMaterial3D> get_debug_material(); + Array get_collision_result() const; int get_collision_count() const; Object *get_collider(int p_idx) const; RID get_collider_rid(int p_idx) const; diff --git a/scene/3d/physics/vehicle_body_3d.cpp b/scene/3d/physics/vehicle_body_3d.cpp index c23032d3b9..981e872af2 100644 --- a/scene/3d/physics/vehicle_body_3d.cpp +++ b/scene/3d/physics/vehicle_body_3d.cpp @@ -106,7 +106,7 @@ void VehicleWheel3D::_notification(int p_what) { } PackedStringArray VehicleWheel3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); @@ -219,6 +219,14 @@ bool VehicleWheel3D::is_in_contact() const { return m_raycastInfo.m_isInContact; } +Vector3 VehicleWheel3D::get_contact_point() const { + return m_raycastInfo.m_contactPointWS; +} + +Vector3 VehicleWheel3D::get_contact_normal() const { + return m_raycastInfo.m_contactNormalWS; +} + Node3D *VehicleWheel3D::get_contact_body() const { return m_raycastInfo.m_groundObject; } @@ -256,6 +264,8 @@ void VehicleWheel3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact); ClassDB::bind_method(D_METHOD("get_contact_body"), &VehicleWheel3D::get_contact_body); + ClassDB::bind_method(D_METHOD("get_contact_point"), &VehicleWheel3D::get_contact_point); + ClassDB::bind_method(D_METHOD("get_contact_normal"), &VehicleWheel3D::get_contact_normal); ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence); ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence); @@ -287,11 +297,11 @@ void VehicleWheel3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip"); ADD_GROUP("Suspension", "suspension_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_travel", "get_suspension_travel"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness", PROPERTY_HINT_NONE, U"suffix:N/mm"), "set_suspension_stiffness", "get_suspension_stiffness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_suspension_max_force", "get_suspension_max_force"); ADD_GROUP("Damping", "damping_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_compression", "get_damping_compression"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_relaxation", "get_damping_relaxation"); } void VehicleWheel3D::set_engine_force(real_t p_engine_force) { diff --git a/scene/3d/physics/vehicle_body_3d.h b/scene/3d/physics/vehicle_body_3d.h index def9984440..24f120ed26 100644 --- a/scene/3d/physics/vehicle_body_3d.h +++ b/scene/3d/physics/vehicle_body_3d.h @@ -130,6 +130,10 @@ public: bool is_in_contact() const; + Vector3 get_contact_point() const; + + Vector3 get_contact_normal() const; + Node3D *get_contact_body() const; void set_roll_influence(real_t p_value); diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 8d6e717132..f970879aa4 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -113,6 +113,16 @@ void RemoteTransform3D::_notification(int p_what) { _update_cache(); } break; + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (cache.is_valid()) { + _update_remote(); + Node3D *n = Object::cast_to<Node3D>(ObjectDB::get_instance(cache)); + if (n) { + n->reset_physics_interpolation(); + } + } + } break; + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: case NOTIFICATION_TRANSFORM_CHANGED: { if (!is_inside_tree()) { @@ -201,7 +211,7 @@ void RemoteTransform3D::force_update_cache() { } PackedStringArray RemoteTransform3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 3d24b3bbe9..db9c4db30d 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -69,13 +69,13 @@ SkinReference::~SkinReference() { /////////////////////////////////////// bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - #ifndef DISABLE_DEPRECATED - if (path.begins_with("animate_physical_bones")) { + if (p_path == SNAME("animate_physical_bones")) { set_animate_physical_bones(p_value); + return true; } #endif + String path = p_path; if (!path.begins_with("bones/")) { return false; @@ -103,6 +103,8 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_pose_rotation(which, p_value); } else if (what == "scale") { set_bone_pose_scale(which, p_value); + } else if (what == "bone_meta") { + set_bone_meta(which, path.get_slicec('/', 3), p_value); #ifndef DISABLE_DEPRECATED } else if (what == "pose" || what == "bound_children") { // Kept for compatibility from 3.x to 4.x. @@ -139,13 +141,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { } bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - #ifndef DISABLE_DEPRECATED - if (path.begins_with("animate_physical_bones")) { + if (p_path == SNAME("animate_physical_bones")) { r_ret = get_animate_physical_bones(); + return true; } #endif + String path = p_path; if (!path.begins_with("bones/")) { return false; @@ -170,6 +172,8 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_pose_rotation(which); } else if (what == "scale") { r_ret = get_bone_pose_scale(which); + } else if (what == "bone_meta") { + r_ret = get_bone_meta(which, path.get_slicec('/', 3)); } else { return false; } @@ -187,6 +191,11 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + + for (const KeyValue<StringName, Variant> &K : bones[i].metadata) { + PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + PNAME("bone_meta/") + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); + p_list->push_back(pi); + } } for (PropertyInfo &E : *p_list) { @@ -300,7 +309,7 @@ void Skeleton3D::setup_simulator() { simulator = sim; sim->is_compat = true; sim->set_active(false); // Don't run unneeded process. - add_child(simulator); + add_child(simulator, false, INTERNAL_MODE_BACK); set_animate_physical_bones(animate_physical_bones); } #endif // _DISABLE_DEPRECATED @@ -531,6 +540,57 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { version++; } +Variant Skeleton3D::get_bone_meta(int p_bone, const StringName &p_key) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Variant()); + + if (!bones[p_bone].metadata.has(p_key)) { + return Variant(); + } + return bones[p_bone].metadata[p_key]; +} + +TypedArray<StringName> Skeleton3D::_get_bone_meta_list_bind(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, TypedArray<StringName>()); + + TypedArray<StringName> _metaret; + for (const KeyValue<StringName, Variant> &K : bones[p_bone].metadata) { + _metaret.push_back(K.key); + } + return _metaret; +} + +void Skeleton3D::get_bone_meta_list(int p_bone, List<StringName> *p_list) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + for (const KeyValue<StringName, Variant> &K : bones[p_bone].metadata) { + p_list->push_back(K.key); + } +} + +bool Skeleton3D::has_bone_meta(int p_bone, const StringName &p_key) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); + + return bones[p_bone].metadata.has(p_key); +} + +void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Variant &p_value) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + if (p_value.get_type() == Variant::NIL) { + if (bones.write[p_bone].metadata.has(p_key)) { + bones.write[p_bone].metadata.erase(p_key); + } + return; + } + + bones.write[p_bone].metadata.insert(p_key, p_value, false); +} + bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { int parent_of_bone = get_bone_parent(p_bone); @@ -1014,6 +1074,11 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name); + ClassDB::bind_method(D_METHOD("get_bone_meta", "bone_idx", "key"), &Skeleton3D::get_bone_meta); + ClassDB::bind_method(D_METHOD("get_bone_meta_list", "bone_idx"), &Skeleton3D::_get_bone_meta_list_bind); + ClassDB::bind_method(D_METHOD("has_bone_meta", "bone_idx", "key"), &Skeleton3D::has_bone_meta); + ClassDB::bind_method(D_METHOD("set_bone_meta", "bone_idx", "key", "value"), &Skeleton3D::set_bone_meta); + ClassDB::bind_method(D_METHOD("get_concatenated_bone_names"), &Skeleton3D::get_concatenated_bone_names); ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index a009383f45..07bdeccf2f 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -116,6 +116,8 @@ private: } } + HashMap<StringName, Variant> metadata; + #ifndef DISABLE_DEPRECATED Transform3D pose_global_no_override; real_t global_pose_override_amount = 0.0; @@ -193,6 +195,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); + TypedArray<StringName> _get_bone_meta_list_bind(int p_bone) const; static void _bind_methods(); virtual void add_child_notify(Node *p_child) override; @@ -238,6 +241,12 @@ public: void set_motion_scale(float p_motion_scale); float get_motion_scale() const; + // bone metadata + Variant get_bone_meta(int p_bone, const StringName &p_key) const; + void get_bone_meta_list(int p_bone, List<StringName> *p_list) const; + bool has_bone_meta(int p_bone, const StringName &p_key) const; + void set_bone_meta(int p_bone, const StringName &p_key, const Variant &p_value); + // Posing API Transform3D get_bone_pose(int p_bone) const; Vector3 get_bone_pose_position(int p_bone) const; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 0d6316ee35..2476e7d5cd 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -503,7 +503,11 @@ Transform3D SkeletonIK3D::_get_target_transform() { Node3D *target_node_override = cast_to<Node3D>(target_node_override_ref.get_validated_object()); if (target_node_override && target_node_override->is_inside_tree()) { - return target_node_override->get_global_transform(); + // Make sure to use the interpolated transform as target. + // When physics interpolation is off this will pass through to get_global_transform(). + // When using interpolation, ensure that the target matches the interpolated visual position + // of the target when updating the IK each frame. + return target_node_override->get_global_transform_interpolated(); } else { return target; } diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp index 9851214194..d5c603112e 100644 --- a/scene/3d/skeleton_modifier_3d.cpp +++ b/scene/3d/skeleton_modifier_3d.cpp @@ -37,7 +37,7 @@ void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const { PackedStringArray SkeletonModifier3D::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); if (skeleton_id.is_null()) { - warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton.")); + warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D.")); } return warnings; } diff --git a/scene/3d/soft_body_3d.compat.inc b/scene/3d/soft_body_3d.compat.inc new file mode 100644 index 0000000000..0b01bfeb1f --- /dev/null +++ b/scene/3d/soft_body_3d.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* soft_body_3d.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +void SoftBody3D::_pin_point_bind_compat_94684(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { + pin_point(p_point_index, pin, p_spatial_attachment_path, -1); +} + +void SoftBody3D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::_pin_point_bind_compat_94684, DEFVAL(NodePath())); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index f02cd9b700..b0fc94d75f 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "soft_body_3d.h" +#include "soft_body_3d.compat.inc" #include "scene/3d/physics/physics_body_3d.h" @@ -200,12 +201,18 @@ bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) { int point_index; for (int i = 0; i < p_indices_size; ++i) { point_index = p_indices.get(i); - if (w[i].point_index != point_index) { - if (-1 != w[i].point_index) { + if (w[i].point_index != point_index || pinned_points.size() < p_indices_size) { + bool insert = false; + if (w[i].point_index != -1 && p_indices.find(w[i].point_index) == -1) { pin_point(w[i].point_index, false); + insert = true; } w[i].point_index = point_index; - pin_point(w[i].point_index, true); + if (insert) { + pin_point(w[i].point_index, true, NodePath(), i); + } else { + pin_point(w[i].point_index, true); + } } } return true; @@ -218,7 +225,13 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String if ("spatial_attachment_path" == p_what) { PinnedPoint *w = pinned_points.ptrw(); - callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + + if (is_inside_tree()) { + callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + } else { + pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } } else if ("offset" == p_what) { PinnedPoint *w = pinned_points.ptrw(); w[p_item].offset = p_value; @@ -350,7 +363,7 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); - ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path", "insert_at"), &SoftBody3D::pin_point, DEFVAL(NodePath()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); @@ -377,7 +390,7 @@ void SoftBody3D::_bind_methods() { } PackedStringArray SoftBody3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = MeshInstance3D::get_configuration_warnings(); if (mesh.is_null()) { warnings.push_back(RTR("This body will be ignored until you set a mesh.")); @@ -662,10 +675,11 @@ void SoftBody3D::pin_point_toggle(int p_point_index) { pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); } -void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { +void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path, int p_insert_at) { + ERR_FAIL_COND_MSG(p_insert_at < -1 || p_insert_at >= pinned_points.size(), "Invalid index for pin point insertion position."); _pin_point_on_physics_server(p_point_index, pin); if (pin) { - _add_pinned_point(p_point_index, p_spatial_attachment_path); + _add_pinned_point(p_point_index, p_spatial_attachment_path, p_insert_at); } else { _remove_pinned_point(p_point_index); } @@ -724,7 +738,7 @@ void SoftBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); } -void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { +void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path, int p_insert_at) { SoftBody3D::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { // Create new @@ -737,7 +751,11 @@ void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_ pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); } - pinned_points.push_back(pp); + if (p_insert_at != -1) { + pinned_points.insert(p_insert_at, pp); + } else { + pinned_points.push_back(pp); + } } else { pinned_point->point_index = p_point_index; diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index ab30f7e654..b01d462d9f 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -126,6 +126,11 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _pin_point_bind_compat_94684(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + static void _bind_compatibility_methods(); +#endif + PackedStringArray get_configuration_warnings() const override; public: @@ -177,7 +182,7 @@ public: Vector3 get_point_transform(int p_point_index); void pin_point_toggle(int p_point_index); - void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath(), int p_insert_at = -1); bool is_point_pinned(int p_point_index) const; void _pin_point_deferred(int p_point_index, bool pin, const NodePath p_spatial_attachment_path); @@ -193,7 +198,7 @@ private: void _update_cache_pin_points_datas(); void _pin_point_on_physics_server(int p_point_index, bool pin); - void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path, int p_insert_at = -1); void _reset_points_offsets(); void _remove_pinned_point(int p_point_index); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 50218a6d86..42460eec4c 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -132,7 +132,7 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, // Properly setup UVs for impostor textures (AtlasTexture). Ref<AtlasTexture> atlas_tex = p_texture; - if (atlas_tex != nullptr) { + if (atlas_tex.is_valid()) { src_tsize[0] = atlas_tex->get_atlas()->get_width(); src_tsize[1] = atlas_tex->get_atlas()->get_height(); } @@ -1324,7 +1324,7 @@ void AnimatedSprite3D::play(const StringName &p_name, float p_custom_scale, bool name = animation; } - ERR_FAIL_NULL_MSG(frames, vformat("There is no animation with name '%s'.", name)); + ERR_FAIL_COND_MSG(frames.is_null(), vformat("There is no animation with name '%s'.", name)); ERR_FAIL_COND_MSG(!frames->get_animation_names().has(name), vformat("There is no animation with name '%s'.", name)); if (frames->get_frame_count(name) == 0) { @@ -1402,7 +1402,7 @@ void AnimatedSprite3D::set_animation(const StringName &p_name) { emit_signal(SceneStringName(animation_changed)); - if (frames == nullptr) { + if (frames.is_null()) { animation = StringName(); stop(); ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name)); diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index f14ae3a285..a59754c8cc 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -30,6 +30,8 @@ #include "visual_instance_3d.h" +#include "core/config/project_settings.h" + AABB VisualInstance3D::get_aabb() const { AABB ret; GDVIRTUAL_CALL(_get_aabb, ret); @@ -41,7 +43,38 @@ void VisualInstance3D::_update_visibility() { return; } - RS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree()); + bool already_visible = _is_vi_visible(); + bool visible = is_visible_in_tree(); + _set_vi_visible(visible); + + // If making visible, make sure the rendering server is up to date with the transform. + if (visible && !already_visible) { + if (!_is_using_identity_transform()) { + Transform3D gt = get_global_transform(); + RS::get_singleton()->instance_set_transform(instance, gt); + } + } + + RS::get_singleton()->instance_set_visible(instance, visible); +} + +void VisualInstance3D::_physics_interpolated_changed() { + RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated()); +} + +void VisualInstance3D::set_instance_use_identity_transform(bool p_enable) { + // Prevent sending instance transforms when using global coordinates. + _set_use_identity_transform(p_enable); + + if (is_inside_tree()) { + if (p_enable) { + // Want to make sure instance is using identity transform. + RS::get_singleton()->instance_set_transform(instance, Transform3D()); + } else { + // Want to make sure instance is up to date. + RS::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + } } void VisualInstance3D::_notification(int p_what) { @@ -53,13 +86,52 @@ void VisualInstance3D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - Transform3D gt = get_global_transform(); - RenderingServer::get_singleton()->instance_set_transform(instance, gt); + if (_is_vi_visible() || is_physics_interpolated_and_enabled()) { + if (!_is_using_identity_transform()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + + // For instance when first adding to the tree, when the previous transform is + // unset, to prevent streaking from the origin. + if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) { + if (_is_vi_visible()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + } + _set_physics_interpolation_reset_requested(false); + } + } + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) { + // We must ensure the RenderingServer 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); + } +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + else if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + + String node_name = is_inside_tree() ? String(get_path()) : String(get_name()); + if (!_is_vi_visible()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with unhidden nodes: \"" + node_name + "\"."); + } + if (!is_physics_interpolated()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with interpolated nodes: \"" + node_name + "\"."); + } + } +#endif + } break; case NOTIFICATION_EXIT_WORLD: { RenderingServer::get_singleton()->instance_set_scenario(instance, RID()); RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID()); + _set_vi_visible(false); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -425,7 +497,7 @@ bool GeometryInstance3D::is_ignoring_occlusion_culling() { } PackedStringArray GeometryInstance3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) { warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance.")); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 59ede26ac1..9b02c928b7 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -45,6 +45,9 @@ class VisualInstance3D : public Node3D { protected: void _update_visibility(); + virtual void _physics_interpolated_changed() override; + void set_instance_use_identity_transform(bool p_enable); + void _notification(int p_what); static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index ffca856fba..80ff176a98 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -518,7 +518,7 @@ AABB VoxelGI::get_aabb() const { } PackedStringArray VoxelGI::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("VoxelGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 6ea1cfdbb0..08d018eee9 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -35,9 +35,8 @@ class Voxelizer { private: - enum { + enum : uint32_t { CHILD_EMPTY = 0xFFFFFFFF - }; struct Cell { diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp index baaa9eee48..3b533da701 100644 --- a/scene/3d/xr_hand_modifier_3d.cpp +++ b/scene/3d/xr_hand_modifier_3d.cpp @@ -30,6 +30,7 @@ #include "xr_hand_modifier_3d.h" +#include "core/config/project_settings.h" #include "servers/xr/xr_pose.h" #include "servers/xr_server.h" @@ -207,6 +208,11 @@ void XRHandModifier3D::_process_modification() { // Apply previous relative transforms if they are stored. for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) { + const int bone = joints[joint].bone; + if (bone == -1) { + continue; + } + if (bone_update == BONE_UPDATE_FULL) { skeleton->set_bone_pose_position(joints[joint].bone, previous_relative_transforms[joint].origin); } @@ -278,6 +284,17 @@ void XRHandModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { _get_joint_data(); } +PackedStringArray XRHandModifier3D::get_configuration_warnings() const { + PackedStringArray warnings = SkeletonModifier3D::get_configuration_warnings(); + + // Detect OpenXR without the Hand Tracking extension. + if (GLOBAL_GET("xr/openxr/enabled") && !GLOBAL_GET("xr/openxr/extensions/hand_tracking")) { + warnings.push_back("XRHandModifier3D requires the OpenXR Hand Tracking extension to be enabled."); + } + + return warnings; +} + void XRHandModifier3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/scene/3d/xr_hand_modifier_3d.h b/scene/3d/xr_hand_modifier_3d.h index 3d78f32b64..d58ccd0adf 100644 --- a/scene/3d/xr_hand_modifier_3d.h +++ b/scene/3d/xr_hand_modifier_3d.h @@ -55,6 +55,8 @@ public: void set_bone_update(BoneUpdate p_bone_update); BoneUpdate get_bone_update() const; + PackedStringArray get_configuration_warnings() const override; + void _notification(int p_what); protected: diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 3f4b962641..214c1f77ca 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -77,7 +77,7 @@ void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) { } PackedStringArray XRCamera3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Camera3D::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // Warn if the node has a parent which isn't an XROrigin3D! @@ -303,6 +303,8 @@ StringName XRNode3D::get_pose_name() const { void XRNode3D::set_show_when_tracked(bool p_show) { show_when_tracked = p_show; + + _update_visibility(); } bool XRNode3D::get_show_when_tracked() const { @@ -361,6 +363,9 @@ void XRNode3D::_bind_tracker() { if (pose.is_valid()) { set_transform(pose->get_adjusted_transform()); _set_has_tracking_data(pose->get_has_tracking_data()); + } else { + // Pose has been invalidated or was never set. + _set_has_tracking_data(false); } } } @@ -407,6 +412,10 @@ void XRNode3D::_pose_lost_tracking(const Ref<XRPose> &p_pose) { } void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { + // Always update our visibility, we may have set our tracking data + // when conditions weren't right. + _update_visibility(); + // Ignore if the has_tracking_data state isn't changing. if (p_has_tracking_data == has_tracking_data) { return; @@ -415,10 +424,19 @@ void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { // Handle change of has_tracking_data. has_tracking_data = p_has_tracking_data; emit_signal(SNAME("tracking_changed"), has_tracking_data); +} +void XRNode3D::_update_visibility() { // If configured, show or hide the node based on tracking data. if (show_when_tracked) { - set_visible(has_tracking_data); + // Only react to this if we have a primary interface. + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_valid()) { + set_visible(has_tracking_data); + } + } } } @@ -443,7 +461,7 @@ XRNode3D::~XRNode3D() { } PackedStringArray XRNode3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // Warn if the node has a parent which isn't an XROrigin3D! @@ -626,7 +644,7 @@ Plane XRAnchor3D::get_plane() const { Vector<XROrigin3D *> XROrigin3D::origin_nodes; PackedStringArray XROrigin3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { bool has_camera = false; diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index a42f6d470f..94c3923433 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -95,6 +95,8 @@ protected: void _pose_lost_tracking(const Ref<XRPose> &p_pose); void _set_has_tracking_data(bool p_has_tracking_data); + void _update_visibility(); + public: void _validate_property(PropertyInfo &p_property) const; void set_tracker(const StringName &p_tracker_name); |