diff options
Diffstat (limited to 'scene/3d')
84 files changed, 3069 insertions, 1057 deletions
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 78c968a3d4..fb0c59daa1 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -175,7 +175,7 @@ void Area3D::_initialize_wind() { Node3D *p_wind_source = Object::cast_to<Node3D>(get_node(wind_source_path)); ERR_FAIL_NULL(p_wind_source); Transform3D global_transform = p_wind_source->get_transform(); - wind_direction = -global_transform.basis.get_axis(Vector3::AXIS_Z).normalized(); + wind_direction = -global_transform.basis.get_column(Vector3::AXIS_Z).normalized(); wind_source = global_transform.origin; temp_magnitude = wind_force_magnitude; } @@ -192,14 +192,14 @@ void Area3D::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -207,13 +207,13 @@ void Area3D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -224,7 +224,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, BodyState>::Element *E = body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid); if (!body_in && !E) { return; //likely removed from the tree @@ -235,36 +235,36 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i if (body_in) { if (!E) { E = body_map.insert(objid, BodyState()); - E->get().rid = p_body; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_body; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape)); } - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - body_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + body_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree)); @@ -285,7 +285,7 @@ void Area3D::_clear_monitoring() { ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal."); { - Map<ObjectID, BodyState> bmcopy = body_map; + HashMap<ObjectID, BodyState> bmcopy = body_map; body_map.clear(); //disconnect all monitored stuff @@ -314,7 +314,7 @@ void Area3D::_clear_monitoring() { } { - Map<ObjectID, AreaState> bmcopy = area_map; + HashMap<ObjectID, AreaState> bmcopy = area_map; area_map.clear(); //disconnect all monitored stuff @@ -379,14 +379,14 @@ void Area3D::_area_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->area_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -394,13 +394,13 @@ void Area3D::_area_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->area_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -411,7 +411,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, AreaState>::Element *E = area_map.find(objid); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(objid); if (!area_in && !E) { return; //likely removed from the tree @@ -422,36 +422,36 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i if (area_in) { if (!E) { E = area_map.insert(objid, AreaState()); - E->get().rid = p_area; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_area; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); } - if (!node || E->get().in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - area_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + area_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree)); @@ -524,20 +524,20 @@ TypedArray<Area3D> Area3D::get_overlapping_areas() const { bool Area3D::overlaps_area(Node *p_area) const { ERR_FAIL_NULL_V(p_area, false); - const Map<ObjectID, AreaState>::Element *E = area_map.find(p_area->get_instance_id()); + HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } bool Area3D::overlaps_body(Node *p_body) const { ERR_FAIL_NULL_V(p_body, false); - const Map<ObjectID, BodyState>::Element *E = body_map.find(p_body->get_instance_id()); + HashMap<ObjectID, BodyState>::ConstIterator E = body_map.find(p_body->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } void Area3D::set_audio_bus_override(bool p_override) { @@ -728,9 +728,9 @@ void Area3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center"), "set_gravity_point_center", "get_gravity_point_center"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:m"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-32,32,0.001,or_lesser,or_greater,suffix:m/s\u00B2"), "set_gravity", "get_gravity"); ADD_GROUP("Linear Damp", "linear_damp_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_linear_damp_space_override_mode", "get_linear_damp_space_override_mode"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index c2399985ff..3b892baf57 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -49,7 +49,7 @@ public: private: SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED; Vector3 gravity_vec; - real_t gravity; + real_t gravity = 0.0; bool gravity_is_point = false; real_t gravity_distance_scale = 0.0; @@ -98,7 +98,7 @@ private: VSet<ShapePair> shapes; }; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; void _area_inout(int p_status, const RID &p_area, ObjectID p_instance, int p_area_shape, int p_self_shape); @@ -130,7 +130,7 @@ private: VSet<AreaShapePair> shapes; }; - Map<ObjectID, AreaState> area_map; + HashMap<ObjectID, AreaState> area_map; void _clear_monitoring(); bool audio_bus_override = false; @@ -228,4 +228,4 @@ public: VARIANT_ENUM_CAST(Area3D::SpaceOverride); -#endif // AREA__H +#endif // AREA_3D_H diff --git a/scene/3d/audio_listener_3d.cpp b/scene/3d/audio_listener_3d.cpp index 1ead9bb384..4f3f403ab7 100644 --- a/scene/3d/audio_listener_3d.cpp +++ b/scene/3d/audio_listener_3d.cpp @@ -68,7 +68,7 @@ bool AudioListener3D::_get(const StringName &p_name, Variant &r_ret) const { } void AudioListener3D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "current")); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("current"))); } void AudioListener3D::_update_listener() { diff --git a/scene/3d/audio_listener_3d.h b/scene/3d/audio_listener_3d.h index ebc37673ed..44c49f526e 100644 --- a/scene/3d/audio_listener_3d.h +++ b/scene/3d/audio_listener_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LISTENER_3D_H -#define LISTENER_3D_H +#ifndef AUDIO_LISTENER_3D_H +#define AUDIO_LISTENER_3D_H #include "scene/3d/node_3d.h" @@ -67,4 +67,4 @@ public: ~AudioListener3D(); }; -#endif +#endif // AUDIO_LISTENER_3D_H diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index b17201f86b..65b00742ee 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -30,6 +30,7 @@ #include "audio_stream_player_3d.h" +#include "core/config/project_settings.h" #include "scene/3d/area_3d.h" #include "scene/3d/audio_listener_3d.h" #include "scene/3d/camera_3d.h" @@ -273,15 +274,16 @@ void AudioStreamPlayer3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // Update anything related to position first, if possible of course. Vector<AudioFrame> volume_vector; - if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) { + if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) { + force_update_panning = false; volume_vector = _update_panning(); } if (setplay.get() >= 0 && stream.is_valid()) { active.set(); - Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); + Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); - Map<StringName, Vector<AudioFrame>> bus_map; + HashMap<StringName, Vector<AudioFrame>> bus_map; bus_map[_get_actual_bus()] = volume_vector; AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); stream_playbacks.push_back(new_playback); @@ -318,6 +320,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { } } +// Interacts with PhysicsServer3D, so can only be called during _physics_process Area3D *AudioStreamPlayer3D::_get_overriding_area() { //check if any area is diverting sound into a bus Ref<World3D> world_3d = get_world_3d(); @@ -356,6 +359,7 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() { return nullptr; } +// Interacts with PhysicsServer3D, so can only be called during _physics_process StringName AudioStreamPlayer3D::_get_actual_bus() { Area3D *overriding_area = _get_overriding_area(); if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) { @@ -364,6 +368,7 @@ StringName AudioStreamPlayer3D::_get_actual_bus() { return bus; } +// Interacts with PhysicsServer3D, so can only be called during _physics_process Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { Vector<AudioFrame> output_volume_vector; output_volume_vector.resize(4); @@ -387,7 +392,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { Ref<World3D> world_3d = get_world_3d(); ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector); - Set<Camera3D *> cameras = world_3d->get_cameras(); + HashSet<Camera3D *> cameras = world_3d->get_cameras(); cameras.insert(get_viewport()->get_camera_3d()); PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); @@ -447,7 +452,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { if (emission_angle_enabled) { Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; - float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative + float c = listenertopos.normalized().dot(get_global_transform().basis.get_column(2).normalized()); //it's z negative float angle = Math::rad2deg(Math::acos(c)); if (angle > emission_angle) { db_att -= -emission_angle_filter_attenuation_db; @@ -458,15 +463,16 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz); } - //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from - // speakers not facing the source) - this could be made distance dependent. - _calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector); + // Bake in a constant factor here to allow the project setting defaults for 2d and 3d to be normalized to 1.0. + float tightness = cached_global_panning_strength * 2.0f; + tightness *= panning_strength; + _calc_output_vol(local_pos.normalized(), tightness, output_volume_vector); for (unsigned int k = 0; k < 4; k++) { output_volume_vector.write[k] = multiplier * output_volume_vector[k]; } - Map<StringName, Vector<AudioFrame>> bus_volumes; + HashMap<StringName, Vector<AudioFrame>> bus_volumes; if (area) { if (area->is_overriding_audio_bus()) { //override audio bus @@ -537,6 +543,7 @@ float AudioStreamPlayer3D::get_unit_db() const { void AudioStreamPlayer3D::set_unit_size(float p_volume) { unit_size = p_volume; + update_gizmos(); } float AudioStreamPlayer3D::get_unit_size() const { @@ -665,6 +672,7 @@ void AudioStreamPlayer3D::_bus_layout_changed() { void AudioStreamPlayer3D::set_max_distance(float p_metres) { ERR_FAIL_COND(p_metres < 0.0); max_distance = p_metres; + update_gizmos(); } float AudioStreamPlayer3D::get_max_distance() const { @@ -725,6 +733,7 @@ float AudioStreamPlayer3D::get_attenuation_filter_db() const { void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) { ERR_FAIL_INDEX((int)p_model, 4); attenuation_model = p_model; + update_gizmos(); } AudioStreamPlayer3D::AttenuationModel AudioStreamPlayer3D::get_attenuation_model() const { @@ -785,6 +794,15 @@ int AudioStreamPlayer3D::get_max_polyphony() const { return max_polyphony; } +void AudioStreamPlayer3D::set_panning_strength(float p_panning_strength) { + ERR_FAIL_COND_MSG(p_panning_strength < 0, "Panning strength must be a positive number."); + panning_strength = p_panning_strength; +} + +float AudioStreamPlayer3D::get_panning_strength() const { + return panning_strength; +} + void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream); @@ -850,28 +868,32 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer3D::set_max_polyphony); ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer3D::get_max_polyphony); + ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer3D::set_panning_strength); + ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer3D::get_panning_strength); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Logarithmic,Disabled"), "set_attenuation_model", "get_attenuation_model"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_db", PROPERTY_HINT_RANGE, "-80,80,suffix:dB"), "set_unit_db", "get_unit_db"); 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"), "set_max_db", "get_max_db"); + 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, "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"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,or_greater,suffix:m"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "panning_strength", PROPERTY_HINT_RANGE, "0,3,0.01,or_greater"), "set_panning_strength", "get_panning_strength"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); ADD_GROUP("Emission Angle", "emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emission_angle_enabled"), "set_emission_angle_enabled", "is_emission_angle_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1,degrees"), "set_emission_angle", "get_emission_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_filter_attenuation_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_emission_angle_filter_attenuation_db", "get_emission_angle_filter_attenuation_db"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_filter_attenuation_db", PROPERTY_HINT_RANGE, "-80,0,0.1,suffix:dB"), "set_emission_angle_filter_attenuation_db", "get_emission_angle_filter_attenuation_db"); ADD_GROUP("Attenuation Filter", "attenuation_filter_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1"), "set_attenuation_filter_cutoff_hz", "get_attenuation_filter_cutoff_hz"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_attenuation_filter_db", "get_attenuation_filter_db"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_attenuation_filter_cutoff_hz", "get_attenuation_filter_cutoff_hz"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_db", PROPERTY_HINT_RANGE, "-80,0,0.1,suffix:dB"), "set_attenuation_filter_db", "get_attenuation_filter_db"); ADD_GROUP("Doppler", "doppler_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); @@ -891,6 +913,7 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { velocity_tracker.instantiate(); AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_bus_layout_changed)); set_disable_scale(true); + cached_global_panning_strength = ProjectSettings::get_singleton()->get("audio/general/3d_panning_strength"); } AudioStreamPlayer3D::~AudioStreamPlayer3D() { diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 53cdd2e630..647b18a4a7 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -82,12 +82,13 @@ private: int max_polyphony = 1; uint64_t last_mix_count = -1; + bool force_update_panning = false; static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output); void _calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol); - static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_update_panning(); } + static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->force_update_panning = true; } void _set_playing(bool p_enable); bool _is_active() const; @@ -115,6 +116,9 @@ private: float _get_attenuation_db(float p_distance) const; + float panning_strength = 1.0f; + float cached_global_panning_strength = 1.0f; + protected: void _validate_property(PropertyInfo &property) const override; void _notification(int p_what); @@ -181,6 +185,9 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + void set_panning_strength(float p_panning_strength); + float get_panning_strength() const; + Ref<AudioStreamPlayback> get_stream_playback(); AudioStreamPlayer3D(); @@ -189,4 +196,5 @@ public: VARIANT_ENUM_CAST(AudioStreamPlayer3D::AttenuationModel) VARIANT_ENUM_CAST(AudioStreamPlayer3D::DopplerTracking) + #endif // AUDIO_STREAM_PLAYER_3D_H diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 8623c7d8b6..fbd5b5b65b 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -107,17 +107,17 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { if (use_external_skeleton) { if (external_skeleton_node_cache.is_null()) { - warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node.")); + warnings.push_back(RTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node.")); } } else { Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); if (!parent) { - warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); + warnings.push_back(RTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); } } if (bone_idx == -1) { - warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node.")); + warnings.push_back(RTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node.")); } return warnings; @@ -376,6 +376,24 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { } } } +#ifdef TOOLS_ENABLED +void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map) { + const Skeleton3D *parent = nullptr; + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_valid()) { + parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } + } else { + parent = Object::cast_to<Skeleton3D>(get_parent()); + } + if (parent && parent == p_skeleton) { + StringName bn = p_bone_map->find_profile_bone_name(bone_name); + if (bn) { + set_bone_name(bn); + } + } +} +#endif // TOOLS_ENABLED BoneAttachment3D::BoneAttachment3D() { } @@ -398,6 +416,9 @@ void BoneAttachment3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton); ClassDB::bind_method(D_METHOD("set_external_skeleton", "external_skeleton"), &BoneAttachment3D::set_external_skeleton); ClassDB::bind_method(D_METHOD("get_external_skeleton"), &BoneAttachment3D::get_external_skeleton); +#ifdef TOOLS_ENABLED + ClassDB::bind_method(D_METHOD("_notify_skeleton_bones_renamed"), &BoneAttachment3D::_notify_skeleton_bones_renamed); +#endif // TOOLS_ENABLED ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx"); diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 395dfde1d7..3224361a25 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -28,10 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BONE_ATTACHMENT_H -#define BONE_ATTACHMENT_H +#ifndef BONE_ATTACHMENT_3D_H +#define BONE_ATTACHMENT_3D_H #include "scene/3d/skeleton_3d.h" +#ifdef TOOLS_ENABLED +#include "scene/resources/bone_map.h" +#endif // TOOLS_ENABLED class BoneAttachment3D : public Node3D { GDCLASS(BoneAttachment3D, Node3D); @@ -68,6 +71,9 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifdef TOOLS_ENABLED + virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map); +#endif // TOOLS_ENABLED public: virtual TypedArray<String> get_configuration_warnings() const override; @@ -93,4 +99,4 @@ public: BoneAttachment3D(); }; -#endif // BONE_ATTACHMENT_H +#endif // BONE_ATTACHMENT_3D_H diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 4eace17cc0..da4a14394d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,7 @@ #include "camera_3d.h" #include "collision_object_3d.h" -#include "core/math/camera_matrix.h" +#include "core/math/projection.h" #include "scene/main/viewport.h" void Camera3D::_update_audio_listener_state() { @@ -144,8 +144,8 @@ void Camera3D::_notification(int p_what) { Transform3D Camera3D::get_camera_transform() const { Transform3D tr = get_global_transform().orthonormalized(); - tr.origin += tr.basis.get_axis(1) * v_offset; - tr.origin += tr.basis.get_axis(0) * h_offset; + tr.origin += tr.basis.get_column(1) * v_offset; + tr.origin += tr.basis.get_column(0) * h_offset; return tr; } @@ -197,7 +197,7 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea update_gizmos(); } -void Camera3D::set_projection(Camera3D::Projection p_mode) { +void Camera3D::set_projection(ProjectionType p_mode) { if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { mode = p_mode; _update_camera_mode(); @@ -217,8 +217,6 @@ void Camera3D::make_current() { } get_viewport()->_camera_3d_set(this); - - //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); } void Camera3D::clear_current(bool p_enable_next) { @@ -267,7 +265,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { if (mode == PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); } else { - CameraMatrix cm; + Projection cm; cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); Vector2 screen_he = cm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -near).normalized(); @@ -307,7 +305,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { bool Camera3D::is_position_behind(const Vector3 &p_pos) const { Transform3D t = get_global_transform(); - Vector3 eyedir = -t.basis.get_axis(2).normalized(); + Vector3 eyedir = -t.basis.get_column(2).normalized(); return eyedir.dot(p_pos - t.origin) < near; } @@ -316,7 +314,7 @@ Vector<Vector3> Camera3D::get_near_plane_points() const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); @@ -342,7 +340,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); @@ -370,7 +368,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH); @@ -475,9 +473,9 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near); ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection); ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection); - ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset); + ClassDB::bind_method(D_METHOD("set_h_offset", "offset"), &Camera3D::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset); - ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera3D::set_v_offset); + ClassDB::bind_method(D_METHOD("set_v_offset", "offset"), &Camera3D::set_v_offset); ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera3D::get_v_offset); ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Camera3D::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask); @@ -503,16 +501,16 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_effects", "get_effects"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.01,suffix:m"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); @@ -546,7 +544,7 @@ real_t Camera3D::get_far() const { return far; } -Camera3D::Projection Camera3D::get_projection() const { +Camera3D::ProjectionType Camera3D::get_projection() const { return mode; } @@ -557,7 +555,7 @@ void Camera3D::set_fov(real_t p_fov) { } void Camera3D::set_size(real_t p_size) { - ERR_FAIL_COND(p_size < 0.1 || p_size > 16384); + ERR_FAIL_COND(p_size < 0.001 || p_size > 16384); size = p_size; _update_camera_mode(); } @@ -609,7 +607,7 @@ Vector<Plane> Camera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_PERSPECTIVE) { cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); } else { diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 9f2f8ceed1..cedd976890 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -40,7 +40,7 @@ class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); public: - enum Projection { + enum ProjectionType { PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGONAL, PROJECTION_FRUSTUM @@ -62,7 +62,7 @@ private: bool current = false; Viewport *viewport = nullptr; - Projection mode = PROJECTION_PERSPECTIVE; + ProjectionType mode = PROJECTION_PERSPECTIVE; real_t fov = 0.0; real_t size = 1.0; @@ -112,7 +112,7 @@ public: void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far); - void set_projection(Camera3D::Projection p_mode); + void set_projection(Camera3D::ProjectionType p_mode); void make_current(); void clear_current(bool p_enable_next = true); @@ -127,7 +127,7 @@ public: real_t get_near() const; Vector2 get_frustum_offset() const; - Projection get_projection() const; + ProjectionType get_projection() const; void set_fov(real_t p_fov); void set_size(real_t p_size); @@ -181,8 +181,8 @@ public: ~Camera3D(); }; -VARIANT_ENUM_CAST(Camera3D::Projection); +VARIANT_ENUM_CAST(Camera3D::ProjectionType); VARIANT_ENUM_CAST(Camera3D::KeepAspect); VARIANT_ENUM_CAST(Camera3D::DopplerTracking); -#endif +#endif // CAMERA_3D_H diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 3ab09550fa..a36357555a 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -345,9 +345,9 @@ void CollisionObject3D::_update_debug_shapes() { return; } - for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { - if (shapes.has(shapedata_idx->get())) { - ShapeData &shapedata = shapes[shapedata_idx->get()]; + for (const uint32_t &shapedata_idx : debug_shapes_to_update) { + if (shapes.has(shapedata_idx)) { + ShapeData &shapedata = shapes[shapedata_idx]; ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; @@ -459,7 +459,7 @@ void CollisionObject3D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); - ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,Make Static,Keep Active"), "set_disable_mode", "get_disable_mode"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); @@ -689,7 +689,7 @@ TypedArray<String> CollisionObject3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { - warnings.push_back(TTR("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.")); + 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.")); } return warnings; diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index e92843d784..3ec3aa0fc1 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -71,14 +71,14 @@ private: int total_subshapes = 0; - Map<uint32_t, ShapeData> shapes; + RBMap<uint32_t, ShapeData> shapes; bool only_update_transform_changes = false; // This is used for sync to physics. bool capture_input_on_drag = false; bool ray_pickable = true; - Set<uint32_t> debug_shapes_to_update; + HashSet<uint32_t> debug_shapes_to_update; int debug_shapes_count = 0; Transform3D debug_shape_old_transform; @@ -166,4 +166,4 @@ public: VARIANT_ENUM_CAST(CollisionObject3D::DisableMode); -#endif // COLLISION_OBJECT__H +#endif // COLLISION_OBJECT_3D_H diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 88ef44b71f..bd6a70e566 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -171,11 +171,11 @@ TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); } if (polygon.is_empty()) { - warnings.push_back(TTR("An empty CollisionPolygon3D has no effect on collision.")); + warnings.push_back(RTR("An empty CollisionPolygon3D has no effect on collision.")); } return warnings; @@ -200,10 +200,10 @@ void CollisionPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon3D::_is_editable_3d_polygon); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_NONE, "suffix:m"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001,suffix:m"), "set_margin", "get_margin"); } CollisionPolygon3D::CollisionPolygon3D() { diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index a24d485af2..74e5867a2f 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -79,4 +79,4 @@ public: CollisionPolygon3D(); }; -#endif // COLLISION_POLYGON_H +#endif // COLLISION_POLYGON_3D_H diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index e1a0e1427b..759997de7b 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -110,7 +110,7 @@ void CollisionShape3D::_notification(int p_what) { } } -void CollisionShape3D::resource_changed(RES res) { +void CollisionShape3D::resource_changed(Ref<Resource> res) { update_gizmos(); } @@ -118,17 +118,17 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { - warnings.push_back(TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.")); + warnings.push_back(RTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.")); } if (shape.is_valid() && Object::cast_to<RigidDynamicBody3D>(get_parent()) && Object::cast_to<ConcavePolygonShape3D>(*shape)) { - warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); + warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); } return warnings; diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index bd5595f974..124c0d166d 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef COLLISION_SHAPE_H -#define COLLISION_SHAPE_H +#ifndef COLLISION_SHAPE_3D_H +#define COLLISION_SHAPE_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" @@ -37,14 +37,13 @@ class CollisionObject3D; class CollisionShape3D : public Node3D { GDCLASS(CollisionShape3D, Node3D); - OBJ_CATEGORY("3D Physics Nodes"); Ref<Shape3D> shape; uint32_t owner_id = 0; CollisionObject3D *parent = nullptr; - void resource_changed(RES res); + void resource_changed(Ref<Resource> res); bool disabled = false; protected: @@ -69,4 +68,4 @@ public: ~CollisionShape3D(); }; -#endif // BODY_VOLUME_H +#endif // COLLISION_SHAPE_3D_H diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index a02b5f48a1..4df0c37ad6 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -39,10 +39,6 @@ AABB CPUParticles3D::get_aabb() const { return AABB(); } -Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_particle_flags) const { - return Vector<Face3>(); -} - void CPUParticles3D::set_emitting(bool p_emitting) { if (emitting == p_emitting) { return; @@ -168,6 +164,8 @@ void CPUParticles3D::set_mesh(const Ref<Mesh> &p_mesh) { } else { RS::get_singleton()->multimesh_set_mesh(multimesh, RID()); } + + update_configuration_warnings(); } Ref<Mesh> CPUParticles3D::get_mesh() const { @@ -191,7 +189,7 @@ bool CPUParticles3D::get_fractional_delta() const { } TypedArray<String> CPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; @@ -210,11 +208,11 @@ TypedArray<String> CPUParticles3D::get_configuration_warnings() const { anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); if (!mesh_found) { - warnings.push_back(TTR("Nothing is visible because no mesh has been assigned.")); + warnings.push_back(RTR("Nothing is visible because no mesh has been assigned.")); } if (!anim_material_found && (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { - warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + warnings.push_back(RTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } return warnings; @@ -270,6 +268,8 @@ void CPUParticles3D::set_param_min(Parameter p_param, real_t p_value) { if (parameters_min[p_param] > parameters_max[p_param]) { set_param_max(p_param, p_value); } + + update_configuration_warnings(); } real_t CPUParticles3D::get_param_min(Parameter p_param) const { @@ -280,10 +280,13 @@ real_t CPUParticles3D::get_param_min(Parameter p_param) const { void CPUParticles3D::set_param_max(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); + parameters_max[p_param] = p_value; if (parameters_min[p_param] > parameters_max[p_param]) { set_param_min(p_param, p_value); } + + update_configuration_warnings(); } real_t CPUParticles3D::get_param_max(Parameter p_param) const { @@ -344,6 +347,8 @@ void CPUParticles3D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv default: { } } + + update_configuration_warnings(); } Ref<Curve> CPUParticles3D::get_param_curve(Parameter p_param) const { @@ -512,7 +517,7 @@ bool CPUParticles3D::get_split_scale() { } void CPUParticles3D::_validate_property(PropertyInfo &property) const { - if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { + if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { property.usage = PROPERTY_USAGE_NONE; } @@ -808,6 +813,13 @@ void CPUParticles3D::_particles_process(double p_delta) { case EMISSION_SHAPE_SPHERE: { real_t s = 2.0 * Math::randf() - 1.0; real_t t = Math_TAU * Math::randf(); + real_t x = Math::randf(); + real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); + p.transform.origin = Vector3(0, 0, 0).lerp(Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s), x); + } break; + case EMISSION_SHAPE_SPHERE_SURFACE: { + real_t s = 2.0 * Math::randf() - 1.0; + real_t t = Math_TAU * Math::randf(); real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform.origin = Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s); } break; @@ -830,8 +842,8 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 normal = emission_normals.get(random_idx); Vector2 normal_2d(normal.x, normal.y); Transform2D m2; - m2.set_axis(0, normal_2d); - m2.set_axis(1, normal_2d.orthogonal()); + m2.columns[0] = normal_2d; + m2.columns[1] = normal_2d.orthogonal(); Vector2 velocity_2d(p.velocity.x, p.velocity.y); velocity_2d = m2.basis_xform(velocity_2d); p.velocity.x = velocity_2d.x; @@ -842,9 +854,9 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 tangent = v0.cross(normal).normalized(); Vector3 bitangent = tangent.cross(normal).normalized(); Basis m3; - m3.set_axis(0, tangent); - m3.set_axis(1, bitangent); - m3.set_axis(2, normal); + m3.set_column(0, tangent); + m3.set_column(1, bitangent); + m3.set_column(2, normal); p.velocity = m3.xform(p.velocity); } } @@ -1065,33 +1077,33 @@ void CPUParticles3D::_particles_process(double p_delta) { if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { - p.transform.basis.set_axis(1, p.velocity.normalized()); + p.transform.basis.set_column(1, p.velocity.normalized()); } else { - p.transform.basis.set_axis(1, p.transform.basis.get_axis(1)); + p.transform.basis.set_column(1, p.transform.basis.get_column(1)); } - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); - p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); + p.transform.basis.set_column(2, Vector3(0, 0, 1)); } else { - p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0)); - p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0)); - p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + p.transform.basis.set_column(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0)); + p.transform.basis.set_column(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0)); + p.transform.basis.set_column(2, Vector3(0, 0, 1)); } } else { //orient particle Y towards velocity if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { - p.transform.basis.set_axis(1, p.velocity.normalized()); + p.transform.basis.set_column(1, p.velocity.normalized()); } else { - p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized()); + p.transform.basis.set_column(1, p.transform.basis.get_column(1).normalized()); } - if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) { - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); - p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); + if (p.transform.basis.get_column(1) == p.transform.basis.get_column(0)) { + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); + p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized()); } else { - p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); + p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized()); + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); } } else { p.transform.basis.orthonormalize(); @@ -1156,7 +1168,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ERR_FAIL_NULL(get_viewport()); Camera3D *c = get_viewport()->get_camera_3d(); if (c) { - Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close + Vector3 dir = c->get_global_transform().basis.get_column(2); //far away to close if (local_coords) { // will look different from Particles in editor as this is based on the camera in the scenetree @@ -1184,17 +1196,17 @@ void CPUParticles3D::_update_particle_data_buffer() { } if (r[idx].active) { - ptr[0] = t.basis.elements[0][0]; - ptr[1] = t.basis.elements[0][1]; - ptr[2] = t.basis.elements[0][2]; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; ptr[3] = t.origin.x; - ptr[4] = t.basis.elements[1][0]; - ptr[5] = t.basis.elements[1][1]; - ptr[6] = t.basis.elements[1][2]; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; ptr[7] = t.origin.y; - ptr[8] = t.basis.elements[2][0]; - ptr[9] = t.basis.elements[2][1]; - ptr[10] = t.basis.elements[2][2]; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; ptr[11] = t.origin.z; } else { memset(ptr, 0, sizeof(Transform3D)); @@ -1290,17 +1302,17 @@ void CPUParticles3D::_notification(int p_what) { Transform3D t = inv_emission_transform * r[i].transform; if (r[i].active) { - ptr[0] = t.basis.elements[0][0]; - ptr[1] = t.basis.elements[0][1]; - ptr[2] = t.basis.elements[0][2]; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; ptr[3] = t.origin.x; - ptr[4] = t.basis.elements[1][0]; - ptr[5] = t.basis.elements[1][1]; - ptr[6] = t.basis.elements[1][2]; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; ptr[7] = t.origin.y; - ptr[8] = t.basis.elements[2][0]; - ptr[9] = t.basis.elements[2][1]; - ptr[10] = t.basis.elements[2][2]; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; ptr[11] = t.origin.z; } else { memset(ptr, 0, sizeof(float) * 12); @@ -1436,14 +1448,14 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Drawing", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); @@ -1534,7 +1546,7 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles); ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); @@ -1631,6 +1643,7 @@ void CPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE_SURFACE); BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index bd736bdf24..4cb693f494 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CPU_PARTICLES_H -#define CPU_PARTICLES_H +#ifndef CPU_PARTICLES_3D_H +#define CPU_PARTICLES_3D_H #include "scene/3d/visual_instance_3d.h" @@ -71,6 +71,7 @@ public: enum EmissionShape { EMISSION_SHAPE_POINT, EMISSION_SHAPE_SPHERE, + EMISSION_SHAPE_SPHERE_SURFACE, EMISSION_SHAPE_BOX, EMISSION_SHAPE_POINTS, EMISSION_SHAPE_DIRECTED_POINTS, @@ -173,9 +174,9 @@ private: Vector<Color> emission_colors; int emission_point_count = 0; Vector3 emission_ring_axis; - real_t emission_ring_height; - real_t emission_ring_radius; - real_t emission_ring_inner_radius; + real_t emission_ring_height = 0.0; + real_t emission_ring_radius = 0.0; + real_t emission_ring_inner_radius = 0.0; Ref<Curve> scale_curve_x; Ref<Curve> scale_curve_y; @@ -201,7 +202,6 @@ protected: public: AABB get_aabb() const override; - Vector<Face3> get_faces(uint32_t p_usage_flags) const override; void set_emitting(bool p_emitting); void set_amount(int p_amount); @@ -317,4 +317,4 @@ VARIANT_ENUM_CAST(CPUParticles3D::Parameter) VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags) VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape) -#endif // CPU_PARTICLES_H +#endif // CPU_PARTICLES_3D_H diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index dfac9055f5..0112f24e0c 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -72,7 +72,7 @@ real_t Decal::get_albedo_mix() const { } void Decal::set_upper_fade(real_t p_fade) { - upper_fade = p_fade; + upper_fade = MAX(p_fade, 0.0); RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } @@ -81,7 +81,7 @@ real_t Decal::get_upper_fade() const { } void Decal::set_lower_fade(real_t p_fade) { - lower_fade = p_fade; + lower_fade = MAX(p_fade, 0.0); RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } @@ -152,10 +152,6 @@ AABB Decal::get_aabb() const { return aabb; } -Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void Decal::_validate_property(PropertyInfo &property) const { if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) { property.usage = PROPERTY_USAGE_NO_EDITOR; @@ -167,15 +163,15 @@ TypedArray<String> Decal::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) { - warnings.push_back(TTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible.")); + warnings.push_back(RTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible.")); } if ((textures[TEXTURE_NORMAL].is_valid() || textures[TEXTURE_ORM].is_valid()) && textures[TEXTURE_ALBEDO].is_null()) { - warnings.push_back(TTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0.")); + warnings.push_back(RTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0.")); } if (cull_mask == 0) { - warnings.push_back(TTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property.")); + warnings.push_back(RTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property.")); } return warnings; @@ -218,12 +214,14 @@ void Decal::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Decal::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_extents", "get_extents"); + ADD_GROUP("Textures", "texture_"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION); + ADD_GROUP("Parameters", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); @@ -231,13 +229,16 @@ void Decal::_bind_methods() { // A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface. // Due to this, limit Normal Fade to 0.999. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade"); + ADD_GROUP("Vertical Fade", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade"); + ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin"), "set_distance_fade_begin", "get_distance_fade_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_GROUP("Cull Mask", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); diff --git a/scene/3d/decal.h b/scene/3d/decal.h index 740dd2c407..38da4c14e3 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -57,8 +57,8 @@ private: real_t upper_fade = 0.3; real_t lower_fade = 0.3; bool distance_fade_enabled = false; - real_t distance_fade_begin = 10.0; - real_t distance_fade_length = 1.0; + real_t distance_fade_begin = 40.0; + real_t distance_fade_length = 10.0; protected: static void _bind_methods(); @@ -104,7 +104,6 @@ public: uint32_t get_cull_mask() const; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; Decal(); ~Decal(); diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 8d05254a25..1b329143b6 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -40,8 +40,8 @@ void FogVolume::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material", "material"), &FogVolume::set_material); ClassDB::bind_method(D_METHOD("get_material"), &FogVolume::get_material); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Ellipsoid,Box,World"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Ellipsoid (Local),Cone (Local),Cylinder (Local),Box (Local),World (Global)"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "FogMaterial,ShaderMaterial"), "set_material", "get_material"); } diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h index 68d5c58169..556a92ad3f 100644 --- a/scene/3d/fog_volume.h +++ b/scene/3d/fog_volume.h @@ -62,7 +62,6 @@ public: Ref<Material> get_material() const; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } TypedArray<String> get_configuration_warnings() const override; FogVolume(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 4e35f37291..b352114c7f 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -36,10 +36,6 @@ AABB GPUParticles3D::get_aabb() const { return AABB(); } -Vector<Face3> GPUParticles3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void GPUParticles3D::set_emitting(bool p_emitting) { RS::get_singleton()->particles_set_emitting(particles, p_emitting); @@ -274,10 +270,10 @@ bool GPUParticles3D::get_interpolate() const { } TypedArray<String> GPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); + warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); } bool meshes_found = false; @@ -304,17 +300,17 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { } if (!meshes_found) { - warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes.")); + warnings.push_back(RTR("Nothing is visible because meshes have not been assigned to draw passes.")); } if (process_material.is_null()) { - warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted.")); + warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); if (!anim_material_found && process && (process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + warnings.push_back(RTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } } @@ -356,15 +352,15 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { } if (dp_count && skin.is_valid()) { - warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin.")); + warnings.push_back(RTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin.")); } else if (dp_count == 0 && skin.is_null()) { - warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found.")); + warnings.push_back(RTR("Trails active, but neither Trail meshes or a Skin were found.")); } else if (dp_count > 1) { - warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation).")); + warnings.push_back(RTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation).")); } if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) { - warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering.")); + warnings.push_back(RTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering.")); } } @@ -571,25 +567,25 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,suffix:m"), "set_collision_base_size", "get_collision_base_size"); ADD_GROUP("Drawing", ""); - ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_visibility_aabb", "get_visibility_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,Reverse Lifetime,View Depth"), "set_draw_order", "get_draw_order"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,ZBillboard,YToVelocity,ZBillboardYToVelocity"), "set_transform_align", "get_transform_align"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,Z-Billboard,Y to Velocity,Z-Billboard + Y to Velocity"), "set_transform_align", "get_transform_align"); ADD_GROUP("Trails", "trail_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_trail_length", "get_trail_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length"); ADD_GROUP("Process Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 54ae84628a..0c745dd734 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PARTICLES_H -#define PARTICLES_H +#ifndef GPU_PARTICLES_3D_H +#define GPU_PARTICLES_3D_H #include "scene/3d/visual_instance_3d.h" #include "scene/resources/skin.h" @@ -61,16 +61,16 @@ private: RID particles; bool one_shot; - int amount; - double lifetime; - double pre_process_time; - real_t explosiveness_ratio; - real_t randomness_ratio; - double speed_scale; + int amount = 0; + double lifetime = 0.0; + double pre_process_time = 0.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; + double speed_scale = 0.0; AABB visibility_aabb; - bool local_coords; - int fixed_fps; - bool fractional_delta; + bool local_coords = false; + int fixed_fps = 0; + bool fractional_delta = false; bool interpolate = true; NodePath sub_emitter; real_t collision_base_size = 0.01; @@ -98,7 +98,6 @@ protected: public: AABB get_aabb() const override; - Vector<Face3> get_faces(uint32_t p_usage_flags) const override; void set_emitting(bool p_emitting); void set_amount(int p_amount); @@ -180,4 +179,4 @@ VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder) VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign) VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags) -#endif // PARTICLES_H +#endif // GPU_PARTICLES_3D_H diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 6f94df284a..1fcd5160f6 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -30,6 +30,7 @@ #include "gpu_particles_collision_3d.h" +#include "core/object/worker_thread_pool.h" #include "mesh_instance_3d.h" #include "scene/3d/camera_3d.h" #include "scene/main/viewport.h" @@ -66,7 +67,7 @@ void GPUParticlesCollisionSphere3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere3D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere3D::get_radius); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_radius", "get_radius"); } void GPUParticlesCollisionSphere3D::set_radius(real_t p_radius) { @@ -96,7 +97,7 @@ void GPUParticlesCollisionBox3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox3D::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox3D::get_extents); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); } void GPUParticlesCollisionBox3D::set_extents(const Vector3 &p_extents) { @@ -339,15 +340,12 @@ void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams * } void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) { - ThreadWorkPool work_pool; - work_pool.init(); - work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params); - while (!work_pool.is_done_dispatching()) { + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z); + while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) { OS::get_singleton()->delay_usec(10000); - bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF"); + bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF"); } - work_pool.end_work(); - work_pool.finish(); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); } Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const { @@ -514,9 +512,9 @@ void GPUParticlesCollisionSDF3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF3D::set_thickness); ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF3D::get_thickness); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512,suffix:px"), "set_resolution", "get_resolution"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); BIND_ENUM_CONSTANT(RESOLUTION_16); @@ -594,8 +592,8 @@ void GPUParticlesCollisionHeightField3D::_notification(int p_what) { Camera3D *cam = get_viewport()->get_camera_3d(); if (cam) { Transform3D xform = get_global_transform(); - Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized(); - Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 x_axis = xform.basis.get_column(Vector3::AXIS_X).normalized(); + Vector3 z_axis = xform.basis.get_column(Vector3::AXIS_Z).normalized(); float x_len = xform.basis.get_scale().x; float z_len = xform.basis.get_scale().z; @@ -640,18 +638,13 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode); ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode); - ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_mode); - ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled); + ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled); + ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled); - ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio); - ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode"); - ADD_GROUP("Follow Camera", "follow_camera_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)"), "set_resolution", "get_resolution"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled"); BIND_ENUM_CONSTANT(RESOLUTION_256); BIND_ENUM_CONSTANT(RESOLUTION_512); @@ -665,14 +658,6 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS); } -void GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) { - follow_camera_push_ratio = p_follow_camera_push_ratio; -} - -float GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio() const { - return follow_camera_push_ratio; -} - void GPUParticlesCollisionHeightField3D::set_extents(const Vector3 &p_extents) { extents = p_extents; RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); @@ -704,12 +689,12 @@ GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3 return update_mode; } -void GPUParticlesCollisionHeightField3D::set_follow_camera_mode(bool p_enabled) { +void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) { follow_camera_mode = p_enabled; set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); } -bool GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled() const { +bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const { return follow_camera_mode; } @@ -798,7 +783,7 @@ void GPUParticlesAttractorSphere3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere3D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere3D::get_radius); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_radius", "get_radius"); } void GPUParticlesAttractorSphere3D::set_radius(real_t p_radius) { @@ -828,7 +813,7 @@ void GPUParticlesAttractorBox3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox3D::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox3D::get_extents); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); } void GPUParticlesAttractorBox3D::set_extents(const Vector3 &p_extents) { @@ -861,7 +846,7 @@ void GPUParticlesAttractorVectorField3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField3D::set_texture); ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField3D::get_texture); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); } diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index b6de1d83fc..4b2cb930fa 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -50,8 +50,6 @@ public: void set_cull_mask(uint32_t p_cull_mask); uint32_t get_cull_mask() const; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } - ~GPUParticlesCollision3D(); }; @@ -213,7 +211,6 @@ private: Vector3 extents = Vector3(1, 1, 1); Resolution resolution = RESOLUTION_1024; bool follow_camera_mode = false; - float follow_camera_push_ratio = 0.1; UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED; @@ -231,11 +228,8 @@ public: void set_update_mode(UpdateMode p_update_mode); UpdateMode get_update_mode() const; - void set_follow_camera_mode(bool p_enabled); - bool is_follow_camera_mode_enabled() const; - - void set_follow_camera_push_ratio(float p_ratio); - float get_follow_camera_push_ratio() const; + void set_follow_camera_enabled(bool p_enabled); + bool is_follow_camera_enabled() const; virtual AABB get_aabb() const override; @@ -274,8 +268,6 @@ public: void set_directionality(real_t p_directionality); real_t get_directionality() const; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } - ~GPUParticlesAttractor3D(); }; diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h index 3daf06771d..223b6fc80a 100644 --- a/scene/3d/importer_mesh_instance_3d.h +++ b/scene/3d/importer_mesh_instance_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H -#define SCENE_IMPORTER_MESH_INSTANCE_3D_H +#ifndef IMPORTER_MESH_INSTANCE_3D_H +#define IMPORTER_MESH_INSTANCE_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/immediate_mesh.h" @@ -61,4 +61,5 @@ public: void set_skeleton_path(const NodePath &p_path); NodePath get_skeleton_path() const; }; -#endif + +#endif // IMPORTER_MESH_INSTANCE_3D_H diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 36abd0a5c5..b0509475a7 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -49,6 +49,7 @@ void Joint3D::_disconnect_signals() { void Joint3D::_body_exit_tree() { _disconnect_signals(); _update_joint(true); + update_configuration_warnings(); } void Joint3D::_update_joint(bool p_only_free) { @@ -65,7 +66,6 @@ void Joint3D::_update_joint(bool p_only_free) { if (p_only_free || !is_inside_tree()) { PhysicsServer3D::get_singleton()->joint_clear(joint); warning = String(); - update_configuration_warnings(); return; } @@ -76,15 +76,15 @@ void Joint3D::_update_joint(bool p_only_free) { PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); if (node_a && !body_a && node_b && !body_b) { - warning = TTR("Node A and Node B must be PhysicsBody3Ds"); + warning = RTR("Node A and Node B must be PhysicsBody3Ds"); } else if (node_a && !body_a) { - warning = TTR("Node A must be a PhysicsBody3D"); + warning = RTR("Node A must be a PhysicsBody3D"); } else if (node_b && !body_b) { - warning = TTR("Node B must be a PhysicsBody3D"); + warning = RTR("Node B must be a PhysicsBody3D"); } else if (!body_a && !body_b) { - warning = TTR("Joint is not connected to any PhysicsBody3Ds"); + warning = RTR("Joint is not connected to any PhysicsBody3Ds"); } else if (body_a == body_b) { - warning = TTR("Node A and Node B must be different PhysicsBody3Ds"); + warning = RTR("Node A and Node B must be different PhysicsBody3Ds"); } else { warning = String(); } @@ -124,7 +124,7 @@ void Joint3D::set_node_a(const NodePath &p_node_a) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -141,7 +141,7 @@ void Joint3D::set_node_b(const NodePath &p_node_b) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -166,15 +166,18 @@ int Joint3D::get_solver_priority() const { void Joint3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { + case NOTIFICATION_POST_ENTER_TREE: { + if (is_configured()) { + _disconnect_signals(); + } _update_joint(); } break; case NOTIFICATION_EXIT_TREE: { - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); - _update_joint(true); } + _update_joint(true); } break; } } @@ -183,6 +186,10 @@ void Joint3D::set_exclude_nodes_from_collision(bool p_enable) { if (exclude_from_collision == p_enable) { return; } + if (is_configured()) { + _disconnect_signals(); + } + _update_joint(true); exclude_from_collision = p_enable; _update_joint(); } @@ -324,7 +331,7 @@ void HingeJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser,suffix:m/s"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE); BIND_ENUM_CONSTANT(PARAM_BIAS); @@ -441,8 +448,8 @@ void SliderJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_lower_limit_angular", "lower_limit_angular"), &SliderJoint3D::_set_lower_limit_angular); ClassDB::bind_method(D_METHOD("_get_lower_limit_angular"), &SliderJoint3D::_get_lower_limit_angular); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/upper_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_UPPER); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/lower_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_LOWER); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/upper_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01,suffix:m"), "set_param", "get_param", PARAM_LINEAR_LIMIT_UPPER); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/lower_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01,suffix:m"), "set_param", "get_param", PARAM_LINEAR_LIMIT_LOWER); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_DAMPING); @@ -730,20 +737,62 @@ void Generic6DOFJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z); ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z); + ADD_GROUP("Linear Limit", "linear_limit_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING); + + ADD_GROUP("Linear Motor", "linear_motor_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + + ADD_GROUP("Linear Spring", "linear_spring_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + + ADD_GROUP("Angular Limit", "angular_limit_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x"); @@ -752,27 +801,7 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/erp"), "set_param_x", "get_param_x", PARAM_ANGULAR_ERP); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y"); @@ -781,27 +810,7 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/erp"), "set_param_y", "get_param_y", PARAM_ANGULAR_ERP); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z"); @@ -810,9 +819,33 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_ANGULAR_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/erp"), "set_param_z", "get_param_z", PARAM_ANGULAR_ERP); + + ADD_GROUP("Angular Motor", "angular_motor_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_MOTOR); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + + ADD_GROUP("Angular Spring", "angular_spring_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_SPRING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_STIFFNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp new file mode 100644 index 0000000000..bc435c5451 --- /dev/null +++ b/scene/3d/label_3d.cpp @@ -0,0 +1,947 @@ +/*************************************************************************/ +/* label_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "label_3d.h" + +#include "core/core_string_names.h" +#include "scene/resources/theme.h" +#include "scene/scene_string_names.h" + +void Label3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label3D::get_horizontal_alignment); + + ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label3D::set_vertical_alignment); + ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label3D::get_vertical_alignment); + + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &Label3D::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &Label3D::get_modulate); + + ClassDB::bind_method(D_METHOD("set_outline_modulate", "modulate"), &Label3D::set_outline_modulate); + ClassDB::bind_method(D_METHOD("get_outline_modulate"), &Label3D::get_outline_modulate); + + ClassDB::bind_method(D_METHOD("set_text", "text"), &Label3D::set_text); + ClassDB::bind_method(D_METHOD("get_text"), &Label3D::get_text); + + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label3D::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &Label3D::get_text_direction); + + ClassDB::bind_method(D_METHOD("set_language", "language"), &Label3D::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &Label3D::get_language); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &Label3D::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &Label3D::get_structured_text_bidi_override); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label3D::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label3D::get_structured_text_bidi_override_options); + + ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label3D::set_uppercase); + ClassDB::bind_method(D_METHOD("is_uppercase"), &Label3D::is_uppercase); + + ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Label3D::set_render_priority); + ClassDB::bind_method(D_METHOD("get_render_priority"), &Label3D::get_render_priority); + + ClassDB::bind_method(D_METHOD("set_outline_render_priority", "priority"), &Label3D::set_outline_render_priority); + ClassDB::bind_method(D_METHOD("get_outline_render_priority"), &Label3D::get_outline_render_priority); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &Label3D::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &Label3D::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "size"), &Label3D::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &Label3D::get_font_size); + + ClassDB::bind_method(D_METHOD("set_outline_size", "outline_size"), &Label3D::set_outline_size); + ClassDB::bind_method(D_METHOD("get_outline_size"), &Label3D::get_outline_size); + + ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &Label3D::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &Label3D::get_line_spacing); + + ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label3D::set_autowrap_mode); + ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label3D::get_autowrap_mode); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &Label3D::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &Label3D::get_width); + + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &Label3D::set_pixel_size); + ClassDB::bind_method(D_METHOD("get_pixel_size"), &Label3D::get_pixel_size); + + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Label3D::set_offset); + ClassDB::bind_method(D_METHOD("get_offset"), &Label3D::get_offset); + + ClassDB::bind_method(D_METHOD("set_draw_flag", "flag", "enabled"), &Label3D::set_draw_flag); + ClassDB::bind_method(D_METHOD("get_draw_flag", "flag"), &Label3D::get_draw_flag); + + ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &Label3D::set_billboard_mode); + ClassDB::bind_method(D_METHOD("get_billboard_mode"), &Label3D::get_billboard_mode); + + ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &Label3D::set_alpha_cut_mode); + ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &Label3D::get_alpha_cut_mode); + + ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &Label3D::set_alpha_scissor_threshold); + ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &Label3D::get_alpha_scissor_threshold); + + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &Label3D::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &Label3D::get_texture_filter); + + ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &Label3D::generate_triangle_mesh); + + ClassDB::bind_method(D_METHOD("_queue_update"), &Label3D::_queue_update); + ClassDB::bind_method(D_METHOD("_font_changed"), &Label3D::_font_changed); + ClassDB::bind_method(D_METHOD("_im_update"), &Label3D::_im_update); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); + + ADD_GROUP("Flags", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority"); + + ADD_GROUP("Text", ""); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate"), "set_outline_modulate", "get_outline_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,suffix:px"), "set_outline_size", "get_outline_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); + + BIND_ENUM_CONSTANT(FLAG_SHADED); + BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); + BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); + BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); + BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); + BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); +} + +void Label3D::_validate_property(PropertyInfo &property) const { + if (property.name == "material_override" || property.name == "material_overlay") { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + +void Label3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (!pending_update) { + _im_update(); + } + } break; + case NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = tr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + dirty_text = true; + _queue_update(); + } break; + } +} + +void Label3D::_im_update() { + _shape(); + + triangle_mesh.unref(); + update_gizmos(); + + pending_update = false; +} + +void Label3D::_queue_update() { + if (pending_update) { + return; + } + + pending_update = true; + call_deferred(SceneStringNames::get_singleton()->_im_update); +} + +AABB Label3D::get_aabb() const { + return aabb; +} + +Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { + if (triangle_mesh.is_valid()) { + return triangle_mesh; + } + + Ref<Font> font = _get_font_or_default(); + if (font.is_null()) { + return Ref<TriangleMesh>(); + } + + Vector<Vector3> faces; + faces.resize(6); + Vector3 *facesw = faces.ptrw(); + + float total_h = 0.0; + float max_line_w = 0.0; + for (int i = 0; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + max_line_w = MAX(max_line_w, TS->shaped_text_get_width(lines_rid[i])); + } + + float vbegin = 0; + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_FILL: + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (total_h - line_spacing) / 2.0; + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = (total_h - line_spacing); + } break; + } + + Vector2 offset = Vector2(0, vbegin); + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -max_line_w / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -max_line_w; + } break; + } + + Rect2 final_rect = Rect2(offset + lbl_offset, Size2(max_line_w, total_h)); + + if (final_rect.size.x == 0 || final_rect.size.y == 0) { + return Ref<TriangleMesh>(); + } + + real_t pixel_size = get_pixel_size(); + + Vector2 vertices[4] = { + + (final_rect.position + Vector2(0, -final_rect.size.y)) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, -final_rect.size.y)) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, + final_rect.position * pixel_size, + + }; + + static const int indices[6] = { + 0, 1, 2, + 0, 2, 3 + }; + + for (int j = 0; j < 6; j++) { + int i = indices[j]; + Vector3 vtx; + vtx[0] = vertices[i][0]; + vtx[1] = vertices[i][1]; + facesw[j] = vtx; + } + + triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh->create(faces); + + return triangle_mesh; +} + +void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority, int p_outline_size) { + for (int j = 0; j < p_glyph.repeat; j++) { + Vector2 gl_of; + Vector2 gl_sz; + Rect2 gl_uv; + Size2 texs; + RID tex; + + if (p_glyph.font_rid != RID()) { + tex = TS->font_get_glyph_texture_rid(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + if (tex != RID()) { + gl_of = (TS->font_get_glyph_offset(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) + Vector2(p_glyph.x_off, p_glyph.y_off)) * pixel_size; + gl_sz = TS->font_get_glyph_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) * pixel_size; + gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + } + } else { + gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size; + gl_of = Vector2(0, -gl_sz.y); + } + + bool msdf = TS->font_is_multichannel_signed_distance_field(p_glyph.font_rid); + + SurfaceKey key = SurfaceKey(tex.get_id(), p_priority, p_outline_size); + if (!surfaces.has(key)) { + SurfaceData surf; + surf.material = RenderingServer::get_singleton()->material_create(); + // Set defaults for material, names need to match up those in StandardMaterial3D + RS::get_singleton()->material_set_param(surf.material, "albedo", Color(1, 1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "specular", 0.5); + RS::get_singleton()->material_set_param(surf.material, "metallic", 0.0); + RS::get_singleton()->material_set_param(surf.material, "roughness", 1.0); + RS::get_singleton()->material_set_param(surf.material, "uv1_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(surf.material, "uv1_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "uv2_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold); + if (msdf) { + RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range", TS->font_get_msdf_pixel_range(p_glyph.font_rid)); + RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size", p_outline_size); + } + + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), true, get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, &shader_rid); + + RS::get_singleton()->material_set_shader(surf.material, shader_rid); + RS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex); + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(surf.material, p_priority); + } else { + surf.z_shift = p_priority * pixel_size; + } + + surfaces[key] = surf; + } + SurfaceData &s = surfaces[key]; + + s.mesh_vertices.resize((s.offset + 1) * 4); + s.mesh_normals.resize((s.offset + 1) * 4); + s.mesh_tangents.resize((s.offset + 1) * 16); + s.mesh_colors.resize((s.offset + 1) * 4); + s.mesh_uvs.resize((s.offset + 1) * 4); + + s.mesh_vertices.write[(s.offset * 4) + 3] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 2] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 1] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 0] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y, s.z_shift); + + for (int i = 0; i < 4; i++) { + s.mesh_normals.write[(s.offset * 4) + i] = Vector3(0.0, 0.0, 1.0); + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 0] = 1.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 1] = 0.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 2] = 0.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 3] = 1.0; + s.mesh_colors.write[(s.offset * 4) + i] = p_modulate; + s.mesh_uvs.write[(s.offset * 4) + i] = Vector2(); + + if (aabb == AABB()) { + aabb.position = s.mesh_vertices[(s.offset * 4) + i]; + } else { + aabb.expand_to(s.mesh_vertices[(s.offset * 4) + i]); + } + } + + if (tex != RID()) { + s.mesh_uvs.write[(s.offset * 4) + 3] = Vector2(gl_uv.position.x / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 2] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 1] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, gl_uv.position.y / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 0] = Vector2(gl_uv.position.x / texs.x, gl_uv.position.y / texs.y); + } + + s.indices.resize((s.offset + 1) * 6); + s.indices.write[(s.offset * 6) + 0] = (s.offset * 4) + 0; + s.indices.write[(s.offset * 6) + 1] = (s.offset * 4) + 1; + s.indices.write[(s.offset * 6) + 2] = (s.offset * 4) + 2; + s.indices.write[(s.offset * 6) + 3] = (s.offset * 4) + 0; + s.indices.write[(s.offset * 6) + 4] = (s.offset * 4) + 2; + s.indices.write[(s.offset * 6) + 5] = (s.offset * 4) + 3; + + s.offset++; + r_offset.x += p_glyph.advance * pixel_size; + } +} + +void Label3D::_shape() { + // Clear mesh. + RS::get_singleton()->mesh_clear(mesh); + aabb = AABB(); + + // Clear materials. + for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { + RenderingServer::get_singleton()->free(E.value.material); + } + surfaces.clear(); + + Ref<Font> font = _get_font_or_default(); + ERR_FAIL_COND(font.is_null()); + + // Update text buffer. + if (dirty_text) { + TS->shaped_text_clear(text_rid); + TS->shaped_text_set_direction(text_rid, text_direction); + + String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } + + Array stt; + if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { + GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt); + } else { + stt = TS->parse_structured_text(st_parser, st_args, text); + } + TS->shaped_text_set_bidi_override(text_rid, stt); + + dirty_text = false; + dirty_font = false; + dirty_lines = true; + } else if (dirty_font) { + int spans = TS->shaped_get_span_count(text_rid); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } + + dirty_font = false; + dirty_lines = true; + } + + if (dirty_lines) { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; + switch (autowrap_mode) { + case TextServer::AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_WORD: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_ARBITRARY: + autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_OFF: + break; + } + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + + float max_line_w = 0.0; + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); + max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line)); + lines_rid.push_back(line); + } + + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + for (int i = 0; i < lines_rid.size() - 1; i++) { + TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } + } + dirty_lines = false; + } + + // Generate surfaces and materials. + float total_h = 0.0; + for (int i = 0; i < lines_rid.size(); i++) { + total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size; + } + + float vbegin = 0.0; + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_FILL: + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (total_h - line_spacing * pixel_size) / 2.0; + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = (total_h - line_spacing * pixel_size); + } break; + } + + Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size); + for (int i = 0; i < lines_rid.size(); i++) { + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + offset.x = 0.0; + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -line_width / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -line_width; + } break; + } + offset.x += lbl_offset.x * pixel_size; + offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size; + + if (outline_modulate.a != 0.0 && outline_size > 0) { + // Outline surfaces. + Vector2 ol_offset = offset; + for (int j = 0; j < gl_size; j++) { + _generate_glyph_surfaces(glyphs[j], ol_offset, outline_modulate, outline_render_priority, outline_size); + } + } + + // Main text surfaces. + for (int j = 0; j < gl_size; j++) { + _generate_glyph_surfaces(glyphs[j], offset, modulate, render_priority); + } + offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size; + } + + for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { + Array mesh_array; + mesh_array.resize(RS::ARRAY_MAX); + mesh_array[RS::ARRAY_VERTEX] = E.value.mesh_vertices; + mesh_array[RS::ARRAY_NORMAL] = E.value.mesh_normals; + mesh_array[RS::ARRAY_TANGENT] = E.value.mesh_tangents; + mesh_array[RS::ARRAY_COLOR] = E.value.mesh_colors; + mesh_array[RS::ARRAY_TEX_UV] = E.value.mesh_uvs; + mesh_array[RS::ARRAY_INDEX] = E.value.indices; + + RS::SurfaceData sd; + RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); + + sd.material = E.value.material; + + RS::get_singleton()->mesh_add_surface(mesh, sd); + } +} + +void Label3D::set_text(const String &p_string) { + text = p_string; + xl_text = tr(p_string); + dirty_text = true; + _queue_update(); +} + +String Label3D::get_text() const { + return text; +} + +void Label3D::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (horizontal_alignment != p_alignment) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + dirty_lines = true; // Reshape lines. + } + horizontal_alignment = p_alignment; + _queue_update(); + } +} + +HorizontalAlignment Label3D::get_horizontal_alignment() const { + return horizontal_alignment; +} + +void Label3D::set_vertical_alignment(VerticalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (vertical_alignment != p_alignment) { + vertical_alignment = p_alignment; + _queue_update(); + } +} + +VerticalAlignment Label3D::get_vertical_alignment() const { + return vertical_alignment; +} + +void Label3D::set_text_direction(TextServer::Direction p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + dirty_text = true; + _queue_update(); + } +} + +TextServer::Direction Label3D::get_text_direction() const { + return text_direction; +} + +void Label3D::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + dirty_text = true; + _queue_update(); + } +} + +String Label3D::get_language() const { + return language; +} + +void Label3D::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + dirty_text = true; + _queue_update(); + } +} + +TextServer::StructuredTextParser Label3D::get_structured_text_bidi_override() const { + return st_parser; +} + +void Label3D::set_structured_text_bidi_override_options(Array p_args) { + if (st_args != p_args) { + st_args = p_args; + dirty_text = true; + _queue_update(); + } +} + +Array Label3D::get_structured_text_bidi_override_options() const { + return st_args; +} + +void Label3D::set_uppercase(bool p_uppercase) { + if (uppercase != p_uppercase) { + uppercase = p_uppercase; + dirty_text = true; + _queue_update(); + } +} + +bool Label3D::is_uppercase() const { + return uppercase; +} + +void Label3D::set_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + if (render_priority != p_priority) { + render_priority = p_priority; + _queue_update(); + } +} + +int Label3D::get_render_priority() const { + return render_priority; +} + +void Label3D::set_outline_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + if (outline_render_priority != p_priority) { + outline_render_priority = p_priority; + _queue_update(); + } +} + +int Label3D::get_outline_render_priority() const { + return outline_render_priority; +} + +void Label3D::_font_changed() { + dirty_font = true; + _queue_update(); +} + +void Label3D::set_font(const Ref<Font> &p_font) { + if (font_override != p_font) { + if (font_override.is_valid()) { + font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + font_override = p_font; + dirty_font = true; + if (font_override.is_valid()) { + font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + _queue_update(); + } +} + +Ref<Font> Label3D::get_font() const { + return font_override; +} + +Ref<Font> Label3D::_get_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); + theme_font.unref(); + } + + if (font_override.is_valid()) { + return font_override; + } + + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); + } + return f; + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); + } + return f; + } + } + } + + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); + } + return f; +} + +void Label3D::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = p_size; + dirty_font = true; + _queue_update(); + } +} + +int Label3D::get_font_size() const { + return font_size; +} + +void Label3D::set_outline_size(int p_size) { + if (outline_size != p_size) { + outline_size = p_size; + _queue_update(); + } +} + +int Label3D::get_outline_size() const { + return outline_size; +} + +void Label3D::set_modulate(const Color &p_color) { + if (modulate != p_color) { + modulate = p_color; + _queue_update(); + } +} + +Color Label3D::get_modulate() const { + return modulate; +} + +void Label3D::set_outline_modulate(const Color &p_color) { + if (outline_modulate != p_color) { + outline_modulate = p_color; + _queue_update(); + } +} + +Color Label3D::get_outline_modulate() const { + return outline_modulate; +} + +void Label3D::set_autowrap_mode(TextServer::AutowrapMode p_mode) { + if (autowrap_mode != p_mode) { + autowrap_mode = p_mode; + dirty_lines = true; + _queue_update(); + } +} + +TextServer::AutowrapMode Label3D::get_autowrap_mode() const { + return autowrap_mode; +} + +void Label3D::set_width(float p_width) { + if (width != p_width) { + width = p_width; + dirty_lines = true; + _queue_update(); + } +} + +float Label3D::get_width() const { + return width; +} + +void Label3D::set_pixel_size(real_t p_amount) { + if (pixel_size != p_amount) { + pixel_size = p_amount; + _queue_update(); + } +} + +real_t Label3D::get_pixel_size() const { + return pixel_size; +} + +void Label3D::set_offset(const Point2 &p_offset) { + if (lbl_offset != p_offset) { + lbl_offset = p_offset; + _queue_update(); + } +} + +Point2 Label3D::get_offset() const { + return lbl_offset; +} + +void Label3D::set_line_spacing(float p_line_spacing) { + if (line_spacing != p_line_spacing) { + line_spacing = p_line_spacing; + _queue_update(); + } +} + +float Label3D::get_line_spacing() const { + return line_spacing; +} + +void Label3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + if (flags[p_flag] != p_enable) { + flags[p_flag] = p_enable; + _queue_update(); + } +} + +bool Label3D::get_draw_flag(DrawFlags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + return flags[p_flag]; +} + +void Label3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { + ERR_FAIL_INDEX(p_mode, 3); + if (billboard_mode != p_mode) { + billboard_mode = p_mode; + _queue_update(); + } +} + +StandardMaterial3D::BillboardMode Label3D::get_billboard_mode() const { + return billboard_mode; +} + +void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) { + ERR_FAIL_INDEX(p_mode, 3); + if (alpha_cut != p_mode) { + alpha_cut = p_mode; + _queue_update(); + } +} + +void Label3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { + if (texture_filter != p_filter) { + texture_filter = p_filter; + _queue_update(); + } +} + +StandardMaterial3D::TextureFilter Label3D::get_texture_filter() const { + return texture_filter; +} + +Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const { + return alpha_cut; +} + +void Label3D::set_alpha_scissor_threshold(float p_threshold) { + if (alpha_scissor_threshold != p_threshold) { + alpha_scissor_threshold = p_threshold; + _queue_update(); + } +} + +float Label3D::get_alpha_scissor_threshold() const { + return alpha_scissor_threshold; +} + +Label3D::Label3D() { + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = (i == FLAG_DOUBLE_SIDED); + } + + text_rid = TS->create_shaped_text(); + + mesh = RenderingServer::get_singleton()->mesh_create(); + + set_cast_shadows_setting(SHADOW_CASTING_SETTING_OFF); + set_base(mesh); +} + +Label3D::~Label3D() { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + + TS->free_rid(text_rid); + + RenderingServer::get_singleton()->free(mesh); + for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) { + RenderingServer::get_singleton()->free(E.value.material); + } + surfaces.clear(); +} diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h new file mode 100644 index 0000000000..4498e89517 --- /dev/null +++ b/scene/3d/label_3d.h @@ -0,0 +1,247 @@ +/*************************************************************************/ +/* label_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef LABEL_3D_H +#define LABEL_3D_H + +#include "scene/3d/visual_instance_3d.h" +#include "scene/resources/font.h" + +#include "servers/text_server.h" + +class Label3D : public GeometryInstance3D { + GDCLASS(Label3D, GeometryInstance3D); + +public: + enum DrawFlags { + FLAG_SHADED, + FLAG_DOUBLE_SIDED, + FLAG_DISABLE_DEPTH_TEST, + FLAG_FIXED_SIZE, + FLAG_MAX + }; + + enum AlphaCutMode { + ALPHA_CUT_DISABLED, + ALPHA_CUT_DISCARD, + ALPHA_CUT_OPAQUE_PREPASS + }; + +private: + real_t pixel_size = 0.01; + bool flags[FLAG_MAX] = {}; + AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; + float alpha_scissor_threshold = 0.5; + + AABB aabb; + + mutable Ref<TriangleMesh> triangle_mesh; + RID mesh; + struct SurfaceData { + PackedVector3Array mesh_vertices; + PackedVector3Array mesh_normals; + PackedFloat32Array mesh_tangents; + PackedColorArray mesh_colors; + PackedVector2Array mesh_uvs; + PackedInt32Array indices; + int offset = 0; + float z_shift = 0.0; + RID material; + }; + + struct SurfaceKey { + uint64_t texture_id; + int32_t priority; + int32_t outline_size; + + bool operator==(const SurfaceKey &p_b) const { + return (texture_id == p_b.texture_id) && (priority == p_b.priority) && (outline_size == p_b.outline_size); + } + + SurfaceKey(uint64_t p_texture_id, int p_priority, int p_outline_size) { + texture_id = p_texture_id; + priority = p_priority; + outline_size = p_outline_size; + } + }; + + struct SurfaceKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const SurfaceKey &p_a) { + return hash_murmur3_buffer(&p_a, sizeof(SurfaceKey)); + } + }; + + HashMap<SurfaceKey, SurfaceData, SurfaceKeyHasher> surfaces; + + HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; + VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER; + String text; + String xl_text; + bool uppercase = false; + + TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; + float width = 500.0; + + int font_size = 16; + Ref<Font> font_override; + mutable Ref<Font> theme_font; + Color modulate = Color(1, 1, 1, 1); + Point2 lbl_offset; + int outline_render_priority = -1; + int render_priority = 0; + + int outline_size = 0; + Color outline_modulate = Color(0, 0, 0, 1); + + float line_spacing = 0.f; + + String language; + TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; + Array st_args; + + RID text_rid; + Vector<RID> lines_rid; + + RID base_material; + StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; + StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; + + bool pending_update = false; + + bool dirty_lines = true; + bool dirty_font = true; + bool dirty_text = true; + + void _generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority = 0, int p_outline_size = 0); + +protected: + GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + + void _notification(int p_what); + + static void _bind_methods(); + + void _validate_property(PropertyInfo &property) const override; + + void _im_update(); + void _font_changed(); + void _queue_update(); + + void _shape(); + +public: + void set_horizontal_alignment(HorizontalAlignment p_alignment); + HorizontalAlignment get_horizontal_alignment() const; + + void set_vertical_alignment(VerticalAlignment p_alignment); + VerticalAlignment get_vertical_alignment() const; + + void set_render_priority(int p_priority); + int get_render_priority() const; + + void set_outline_render_priority(int p_priority); + int get_outline_render_priority() const; + + void set_text(const String &p_string); + String get_text() const; + + void set_text_direction(TextServer::Direction p_text_direction); + TextServer::Direction get_text_direction() const; + + void set_language(const String &p_language); + String get_language() const; + + void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser); + TextServer::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + + void set_uppercase(bool p_uppercase); + bool is_uppercase() const; + + void set_font(const Ref<Font> &p_font); + Ref<Font> get_font() const; + Ref<Font> _get_font_or_default() const; + + void set_font_size(int p_size); + int get_font_size() const; + + void set_outline_size(int p_size); + int get_outline_size() const; + + void set_line_spacing(float p_size); + float get_line_spacing() const; + + void set_modulate(const Color &p_color); + Color get_modulate() const; + + void set_outline_modulate(const Color &p_color); + Color get_outline_modulate() const; + + void set_autowrap_mode(TextServer::AutowrapMode p_mode); + TextServer::AutowrapMode get_autowrap_mode() const; + + void set_width(float p_width); + float get_width() const; + + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; + + void set_offset(const Point2 &p_offset); + Point2 get_offset() const; + + void set_draw_flag(DrawFlags p_flag, bool p_enable); + bool get_draw_flag(DrawFlags p_flag) const; + + void set_alpha_cut_mode(AlphaCutMode p_mode); + AlphaCutMode get_alpha_cut_mode() const; + + void set_alpha_scissor_threshold(float p_threshold); + float get_alpha_scissor_threshold() const; + + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); + StandardMaterial3D::BillboardMode get_billboard_mode() const; + + void set_texture_filter(StandardMaterial3D::TextureFilter p_filter); + StandardMaterial3D::TextureFilter get_texture_filter() const; + + virtual AABB get_aabb() const override; + Ref<TriangleMesh> generate_triangle_mesh() const; + + Label3D(); + ~Label3D(); +}; + +VARIANT_ENUM_CAST(Label3D::DrawFlags); +VARIANT_ENUM_CAST(Label3D::AlphaCutMode); + +#endif // LABEL_3D_H diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index b2e605a262..678c217676 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -74,6 +74,43 @@ bool Light3D::is_negative() const { return negative; } +void Light3D::set_enable_distance_fade(bool p_enable) { + distance_fade_enabled = p_enable; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); + notify_property_list_changed(); +} + +bool Light3D::is_distance_fade_enabled() const { + return distance_fade_enabled; +} + +void Light3D::set_distance_fade_begin(real_t p_distance) { + distance_fade_begin = p_distance; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_begin() const { + return distance_fade_begin; +} + +void Light3D::set_distance_fade_shadow(real_t p_distance) { + distance_fade_shadow = p_distance; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_shadow() const { + return distance_fade_shadow; +} + +void Light3D::set_distance_fade_length(real_t p_length) { + distance_fade_length = p_length; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_length() const { + return distance_fade_length; +} + void Light3D::set_cull_mask(uint32_t p_cull_mask) { cull_mask = p_cull_mask; RS::get_singleton()->light_set_cull_mask(light, p_cull_mask); @@ -94,15 +131,6 @@ Color Light3D::get_color() const { return color; } -void Light3D::set_shadow_color(const Color &p_shadow_color) { - shadow_color = p_shadow_color; - RS::get_singleton()->light_set_shadow_color(light, p_shadow_color); -} - -Color Light3D::get_shadow_color() const { - return shadow_color; -} - void Light3D::set_shadow_reverse_cull_face(bool p_enable) { reverse_cull = p_enable; RS::get_singleton()->light_set_reverse_cull_face_mode(light, reverse_cull); @@ -128,10 +156,6 @@ AABB Light3D::get_aabb() const { return AABB(); } -Vector<Face3> Light3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void Light3D::set_bake_mode(BakeMode p_mode) { bake_mode = p_mode; RS::get_singleton()->light_set_bake_mode(light, RS::LightBakeMode(p_mode)); @@ -152,6 +176,11 @@ Ref<Texture2D> Light3D::get_projector() const { return projector; } +void Light3D::owner_changed_notify() { + // For cases where owner changes _after_ entering tree (as example, editor editing). + _update_visibility(); +} + void Light3D::_update_visibility() { if (!is_inside_tree()) { return; @@ -195,7 +224,7 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &property) const { - if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) { + if (!shadow && (property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) { property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -203,6 +232,11 @@ void Light3D::_validate_property(PropertyInfo &property) const { // Angular distance is only used in DirectionalLight3D. property.usage = PROPERTY_USAGE_NONE; } + + if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length")) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + VisualInstance3D::_validate_property(property); } @@ -222,15 +256,24 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cull_mask", "cull_mask"), &Light3D::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &Light3D::get_cull_mask); + ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &Light3D::set_enable_distance_fade); + ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &Light3D::is_distance_fade_enabled); + + ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &Light3D::set_distance_fade_begin); + ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &Light3D::get_distance_fade_begin); + + ClassDB::bind_method(D_METHOD("set_distance_fade_shadow", "distance"), &Light3D::set_distance_fade_shadow); + ClassDB::bind_method(D_METHOD("get_distance_fade_shadow"), &Light3D::get_distance_fade_shadow); + + ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &Light3D::set_distance_fade_length); + ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &Light3D::get_distance_fade_length); + ClassDB::bind_method(D_METHOD("set_color", "color"), &Light3D::set_color); ClassDB::bind_method(D_METHOD("get_color"), &Light3D::get_color); ClassDB::bind_method(D_METHOD("set_shadow_reverse_cull_face", "enable"), &Light3D::set_shadow_reverse_cull_face); ClassDB::bind_method(D_METHOD("get_shadow_reverse_cull_face"), &Light3D::get_shadow_reverse_cull_face); - ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light3D::set_shadow_color); - ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light3D::get_shadow_color); - ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode); ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode); @@ -239,26 +282,34 @@ void Light3D::_bind_methods() { ADD_GROUP("Light", "light_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_ENERGY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_param", "get_param", PARAM_SIZE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_SPECULAR); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); + ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_shadow_color", "get_shadow_color"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "set_param", "get_param", PARAM_SHADOW_BLUR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR); + + ADD_GROUP("Distance Fade", "distance_fade_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_shadow", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_shadow", "get_distance_fade_shadow"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); + ADD_GROUP("", ""); BIND_ENUM_CONSTANT(PARAM_ENERGY); @@ -366,13 +417,13 @@ bool DirectionalLight3D::is_blend_splits_enabled() const { return blend_splits; } -void DirectionalLight3D::set_sky_only(bool p_sky_only) { - sky_only = p_sky_only; - RS::get_singleton()->light_directional_set_sky_only(light, p_sky_only); +void DirectionalLight3D::set_sky_mode(SkyMode p_mode) { + sky_mode = p_mode; + RS::get_singleton()->light_directional_set_sky_mode(light, RS::LightDirectionalSkyMode(p_mode)); } -bool DirectionalLight3D::is_sky_only() const { - return sky_only; +DirectionalLight3D::SkyMode DirectionalLight3D::get_sky_mode() const { + return sky_mode; } void DirectionalLight3D::_validate_property(PropertyInfo &property) const { @@ -391,6 +442,12 @@ void DirectionalLight3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } + if (property.name == "distance_fade_enabled" || property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length") { + // Not relevant for DirectionalLight3D, as the light LOD system only pertains to point lights. + // For DirectionalLight3D, `directional_shadow_max_distance` can be used instead. + property.usage = PROPERTY_USAGE_NONE; + } + Light3D::_validate_property(property); } @@ -401,8 +458,8 @@ void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits); ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled); - ClassDB::bind_method(D_METHOD("set_sky_only", "priority"), &DirectionalLight3D::set_sky_only); - ClassDB::bind_method(D_METHOD("is_sky_only"), &DirectionalLight3D::is_sky_only); + ClassDB::bind_method(D_METHOD("set_sky_mode", "mode"), &DirectionalLight3D::set_sky_mode); + ClassDB::bind_method(D_METHOD("get_sky_mode"), &DirectionalLight3D::get_sky_mode); ADD_GROUP("Directional Shadow", "directional_shadow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode"); @@ -410,15 +467,19 @@ void DirectionalLight3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_FADE_START); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,8192,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_RANGE, "0,1024,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sky_mode", PROPERTY_HINT_ENUM, "Light and Sky,Light Only,Sky Only"), "set_sky_mode", "get_sky_mode"); BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS); + + BIND_ENUM_CONSTANT(SKY_MODE_LIGHT_AND_SKY); + BIND_ENUM_CONSTANT(SKY_MODE_LIGHT_ONLY); + BIND_ENUM_CONSTANT(SKY_MODE_SKY_ONLY); } DirectionalLight3D::DirectionalLight3D() : @@ -429,6 +490,7 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_BIAS, 0.1); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); blend_splits = false; + set_sky_mode(SKY_MODE_LIGHT_AND_SKY); } void OmniLight3D::set_shadow_mode(ShadowMode p_mode) { @@ -444,7 +506,7 @@ TypedArray<String> OmniLight3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_shadow() && get_projector().is_valid()) { - warnings.push_back(TTR("Projector texture only works with shadows active.")); + warnings.push_back(RTR("Projector texture only works with shadows active.")); } return warnings; @@ -455,7 +517,7 @@ void OmniLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shadow_mode"), &OmniLight3D::get_shadow_mode); ADD_GROUP("Omni", "omni_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_RANGE, "0,4096,0.001,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode"); @@ -474,11 +536,11 @@ TypedArray<String> SpotLight3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { - warnings.push_back(TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); + warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); } if (!has_shadow() && get_projector().is_valid()) { - warnings.push_back(TTR("Projector texture only works with shadows active.")); + warnings.push_back(RTR("Projector texture only works with shadows active.")); } return warnings; @@ -486,8 +548,8 @@ TypedArray<String> SpotLight3D::get_configuration_warnings() const { void SpotLight3D::_bind_methods() { ADD_GROUP("Spot", "spot_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_RANGE, "0,4096,0.001,or_greater,exp,suffix:m"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_param", "get_param", PARAM_SPOT_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.01,degrees"), "set_param", "get_param", PARAM_SPOT_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index d5d2aee43d..6ff332df5a 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -35,7 +35,6 @@ class Light3D : public VisualInstance3D { GDCLASS(Light3D, VisualInstance3D); - OBJ_CATEGORY("3D Light Nodes"); public: enum Param { @@ -70,11 +69,14 @@ public: private: Color color; real_t param[PARAM_MAX] = {}; - Color shadow_color; bool shadow = false; bool negative = false; bool reverse_cull = false; uint32_t cull_mask = 0; + bool distance_fade_enabled = false; + real_t distance_fade_begin = 40.0; + real_t distance_fade_shadow = 50.0; + real_t distance_fade_length = 10.0; RS::LightType type = RenderingServer::LIGHT_DIRECTIONAL; bool editor_only = false; void _update_visibility(); @@ -83,6 +85,8 @@ private: // bind helpers + virtual void owner_changed_notify() override; + protected: RID light; @@ -107,15 +111,24 @@ public: void set_negative(bool p_enable); bool is_negative() const; + void set_enable_distance_fade(bool p_enable); + bool is_distance_fade_enabled() const; + + void set_distance_fade_begin(real_t p_distance); + real_t get_distance_fade_begin() const; + + void set_distance_fade_shadow(real_t p_distance); + real_t get_distance_fade_shadow() const; + + void set_distance_fade_length(real_t p_length); + real_t get_distance_fade_length() const; + void set_cull_mask(uint32_t p_cull_mask); uint32_t get_cull_mask() const; void set_color(const Color &p_color); Color get_color() const; - void set_shadow_color(const Color &p_shadow_color); - Color get_shadow_color() const; - void set_shadow_reverse_cull_face(bool p_enable); bool get_shadow_reverse_cull_face() const; @@ -126,7 +139,6 @@ public: Ref<Texture2D> get_projector() const; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; Light3D(); ~Light3D(); @@ -145,10 +157,16 @@ public: SHADOW_PARALLEL_4_SPLITS, }; + enum SkyMode { + SKY_MODE_LIGHT_AND_SKY, + SKY_MODE_LIGHT_ONLY, + SKY_MODE_SKY_ONLY, + }; + private: bool blend_splits; ShadowMode shadow_mode; - bool sky_only = false; + SkyMode sky_mode = SKY_MODE_LIGHT_AND_SKY; protected: static void _bind_methods(); @@ -161,13 +179,14 @@ public: void set_blend_splits(bool p_enable); bool is_blend_splits_enabled() const; - void set_sky_only(bool p_sky_only); - bool is_sky_only() const; + void set_sky_mode(SkyMode p_mode); + SkyMode get_sky_mode() const; DirectionalLight3D(); }; VARIANT_ENUM_CAST(DirectionalLight3D::ShadowMode) +VARIANT_ENUM_CAST(DirectionalLight3D::SkyMode) class OmniLight3D : public Light3D { GDCLASS(OmniLight3D, Light3D); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 68d2b91fad..e805d28ed3 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -73,7 +73,7 @@ void LightmapGIData::clear_users() { } void LightmapGIData::_set_user_data(const Array &p_data) { - ERR_FAIL_COND(p_data.size() <= 0); + ERR_FAIL_COND(p_data.is_empty()); ERR_FAIL_COND((p_data.size() % 4) != 0); for (int i = 0; i < p_data.size(); i += 4) { @@ -92,6 +92,95 @@ Array LightmapGIData::_get_user_data() const { return ret; } +void LightmapGIData::_set_light_textures_data(const Array &p_data) { + ERR_FAIL_COND(p_data.is_empty()); + + if (p_data.size() == 1) { + set_light_texture(p_data[0]); + } else { + Vector<Ref<Image>> images; + for (int i = 0; i < p_data.size(); i++) { + Ref<TextureLayered> texture = p_data[i]; + for (int j = 0; j < texture->get_layers(); j++) { + images.push_back(texture->get_layer_data(j)); + } + } + + Ref<Texture2DArray> combined_texture; + combined_texture.instantiate(); + + combined_texture->create_from_images(images); + set_light_texture(combined_texture); + } +} + +Array LightmapGIData::_get_light_textures_data() const { + Array ret; + if (light_texture.is_null() || light_texture->get_layers() == 0) { + return ret; + } + + Vector<Ref<Image>> images; + for (int i = 0; i < light_texture->get_layers(); i++) { + images.push_back(light_texture->get_layer_data(i)); + } + + int slice_count = images.size(); + int slice_width = images[0]->get_width(); + int slice_height = images[0]->get_height(); + + int slices_per_texture = Image::MAX_HEIGHT / slice_height; + int texture_count = Math::ceil(slice_count / (float)slices_per_texture); + + ret.resize(texture_count); + + String base_name = get_path().get_basename(); + + int last_count = slice_count % slices_per_texture; + for (int i = 0; i < texture_count; i++) { + int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; + + Ref<Image> texture_image; + texture_image.instantiate(); + + texture_image->create(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); + + for (int j = 0; j < texture_slice_count; j++) { + texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); + } + + String texture_path = texture_count > 1 ? base_name + "_" + itos(i) + ".exr" : base_name + ".exr"; + + Ref<ConfigFile> config; + config.instantiate(); + + if (FileAccess::exists(texture_path + ".import")) { + config->load(texture_path + ".import"); + } + + config->set_value("remap", "importer", "2d_array_texture"); + config->set_value("remap", "type", "CompressedTexture2DArray"); + if (!config->has_section_key("params", "compress/mode")) { + config->set_value("params", "compress/mode", 2); //user may want another compression, so leave it be + } + config->set_value("params", "compress/channel_pack", 1); + config->set_value("params", "mipmaps/generate", false); + config->set_value("params", "slices/horizontal", 1); + config->set_value("params", "slices/vertical", texture_slice_count); + + config->save(texture_path + ".import"); + + Error err = texture_image->save_exr(texture_path, false); + ERR_FAIL_COND_V(err, ret); + ResourceLoader::import(texture_path); + Ref<TextureLayered> t = ResourceLoader::load(texture_path); //if already loaded, it will be updated on refocus? + ERR_FAIL_COND_V(t.is_null(), ret); + ret[i] = t; + } + + return ret; +} + RID LightmapGIData::get_rid() const { return lightmap; } @@ -188,6 +277,9 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &LightmapGIData::set_light_texture); ClassDB::bind_method(D_METHOD("get_light_texture"), &LightmapGIData::get_light_texture); + ClassDB::bind_method(D_METHOD("_set_light_textures_data", "data"), &LightmapGIData::_set_light_textures_data); + ClassDB::bind_method(D_METHOD("_get_light_textures_data"), &LightmapGIData::_get_light_textures_data); + 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); @@ -199,7 +291,8 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data); ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered"), "set_light_texture", "get_light_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR), "set_light_texture", "get_light_texture"); // property usage default but no save + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data"); 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"); @@ -318,12 +411,11 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { int over = 0; int under = 0; - int coplanar = 0; const BSPSimplex &s = p_simplices[p_simplex]; for (int i = 0; i < 4; i++) { const Vector3 v = p_points[s.vertices[i]]; - if (p_plane.has_point(v)) { //coplanar - coplanar++; + if (p_plane.has_point(v)) { + // Coplanar. } else if (p_plane.is_point_over(v)) { over++; } else { @@ -573,7 +665,7 @@ void LightmapGI::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cel } } -void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool, Vector3iHash> &positions_used, const AABB &p_bounds) { +void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds) { for (int i = 0; i < 8; i++) { Vector3i pos = p_cell->offset; if (i & 1) { @@ -635,7 +727,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa bsud.to_percent = 0.8; if (p_bake_step) { - p_bake_step(0.0, TTR("Finding meshes, lights and probes"), p_bake_userdata, true); + p_bake_step(0.0, RTR("Finding meshes, lights and probes"), p_bake_userdata, true); } /* STEP 1, FIND MESHES, LIGHTS AND PROBES */ Vector<Lightmapper::MeshData> mesh_data; @@ -655,7 +747,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa for (int m_i = 0; m_i < meshes_found.size(); m_i++) { if (p_bake_step) { float p = (float)(m_i) / meshes_found.size(); - p_bake_step(p * 0.1, vformat(TTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false); + p_bake_step(p * 0.1, vformat(RTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false); } MeshesFound &mf = meshes_found.write[m_i]; @@ -790,7 +882,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa /* STEP 2, CREATE PROBES */ if (p_bake_step) { - p_bake_step(0.3, TTR("Creating probes"), p_bake_userdata, true); + p_bake_step(0.3, RTR("Creating probes"), p_bake_userdata, true); } //bounds need to include the user probes @@ -832,7 +924,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa for (int i = 0; i < mesh_data.size(); i++) { if (p_bake_step) { float p = (float)(i) / mesh_data.size(); - p_bake_step(0.3 + p * 0.1, vformat(TTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false); + p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false); } for (int j = 0; j < mesh_data[i].points.size(); j += 3) { @@ -842,7 +934,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } LocalVector<Vector3> new_probe_positions; - HashMap<Vector3i, bool, Vector3iHash> positions_used; + HashMap<Vector3i, bool> positions_used; for (uint32_t i = 0; i < 8; i++) { //insert bounding endpoints Vector3i pos; if (i & 1) { @@ -873,7 +965,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa // Add everything to lightmapper if (p_bake_step) { - p_bake_step(0.4, TTR("Preparing Lightmapper"), p_bake_userdata, true); + p_bake_step(0.4, RTR("Preparing Lightmapper"), p_bake_userdata, true); } { @@ -884,16 +976,16 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Light3D *light = lights_found[i].light; Transform3D xf = lights_found[i].xform; - Color linear_color = light->get_color().to_linear(); + Color linear_color = light->get_color().srgb_to_linear(); if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE)); + lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<OmniLight3D>(light)) { OmniLight3D *l = Object::cast_to<OmniLight3D>(light); - lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); + lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<SpotLight3D>(light)) { SpotLight3D *l = Object::cast_to<SpotLight3D>(light); - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); + lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -907,7 +999,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa // Add everything to lightmapper if (environment_mode != ENVIRONMENT_MODE_DISABLED) { if (p_bake_step) { - p_bake_step(4.1, TTR("Preparing Environment"), p_bake_userdata, true); + p_bake_step(4.1, RTR("Preparing Environment"), p_bake_userdata, true); } environment_transform = get_global_transform().basis; @@ -954,53 +1046,6 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa return BAKE_ERROR_MESHES_INVALID; } - /* POSTBAKE: Save Textures */ - - Ref<TextureLayered> texture; - { - Vector<Ref<Image>> images; - for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { - images.push_back(lightmapper->get_bake_texture(i)); - } - //we assume they are all the same, so let's create a large one for saving - Ref<Image> large_image; - large_image.instantiate(); - - large_image->create(images[0]->get_width(), images[0]->get_height() * images.size(), false, images[0]->get_format()); - - for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { - large_image->blit_rect(images[i], Rect2(0, 0, images[i]->get_width(), images[i]->get_height()), Point2(0, images[i]->get_height() * i)); - } - - String base_path = p_image_data_path.get_basename() + ".exr"; - - Ref<ConfigFile> config; - - config.instantiate(); - if (FileAccess::exists(base_path + ".import")) { - config->load(base_path + ".import"); - } - - config->set_value("remap", "importer", "2d_array_texture"); - config->set_value("remap", "type", "StreamTexture2DArray"); - if (!config->has_section_key("params", "compress/mode")) { - config->set_value("params", "compress/mode", 2); //user may want another compression, so leave it be - } - config->set_value("params", "compress/channel_pack", 1); - config->set_value("params", "mipmaps/generate", false); - config->set_value("params", "slices/horizontal", 1); - config->set_value("params", "slices/vertical", images.size()); - - config->save(base_path + ".import"); - - Error err = large_image->save_exr(base_path, false); - ERR_FAIL_COND_V(err, BAKE_ERROR_CANT_CREATE_IMAGE); - ResourceLoader::import(base_path); - Ref<Texture> t = ResourceLoader::load(base_path); //if already loaded, it will be updated on refocus? - ERR_FAIL_COND_V(t.is_null(), BAKE_ERROR_CANT_CREATE_IMAGE); - texture = t; - } - /* POSTBAKE: Save Light Data */ Ref<LightmapGIData> data; @@ -1012,6 +1057,17 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa data.instantiate(); } + Ref<Texture2DArray> texture; + { + Vector<Ref<Image>> images; + for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { + images.push_back(lightmapper->get_bake_texture(i)); + } + + texture.instantiate(); + texture->create_from_images(images); + } + data->set_light_texture(texture); data->set_uses_spherical_harmonics(directional); @@ -1046,7 +1102,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa //Obtain solved simplices if (p_bake_step) { - p_bake_step(0.8, TTR("Generating Probe Volumes"), p_bake_userdata, true); + p_bake_step(0.8, RTR("Generating Probe Volumes"), p_bake_userdata, true); } Vector<Delaunay3D::OutputSimplex> solved_simplices = Delaunay3D::tetrahedralize(points); @@ -1101,7 +1157,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa //#define DEBUG_SIMPLICES_AS_OBJ_FILE #ifdef DEBUG_SIMPLICES_AS_OBJ_FILE { - FileAccessRef f = FileAccess::open("res://bsp.obj", FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open("res://bsp.obj", FileAccess::WRITE); for (uint32_t i = 0; i < bsp_simplices.size(); i++) { f->store_line("o Simplex" + itos(i)); for (int j = 0; j < 4; j++) { @@ -1118,7 +1174,6 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa f->store_line(vformat("f %d %d %d", 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2])); } } - f->close(); } #endif @@ -1130,7 +1185,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } if (p_bake_step) { - p_bake_step(0.9, TTR("Generating Probe Acceleration Structures"), p_bake_userdata, true); + p_bake_step(0.9, RTR("Generating Probe Acceleration Structures"), p_bake_userdata, true); } _compute_bsp_tree(points, bsp_planes, planes_tested, bsp_simplices, bsp_simplex_indices, bsp_nodes); @@ -1150,7 +1205,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } //#define DEBUG_BSP_TREE #ifdef DEBUG_BSP_TREE - FileAccessRef f = FileAccess::open("res://bsp.txt", FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open("res://bsp.txt", FileAccess::WRITE); for (uint32_t i = 0; i < bsp_nodes.size(); i++) { f->store_line(itos(i) + " - plane: " + bsp_nodes[i].plane + " over: " + itos(bsp_nodes[i].over) + " under: " + itos(bsp_nodes[i].under)); } @@ -1163,8 +1218,8 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa /* Compute a BSP tree of the simplices, so it's easy to find the exact one */ } - Error err = ResourceSaver::save(p_image_data_path, data); data->set_path(p_image_data_path); + Error err = ResourceSaver::save(p_image_data_path, data); if (err != OK) { return BAKE_ERROR_CANT_CREATE_IMAGE; @@ -1263,10 +1318,6 @@ AABB LightmapGI::get_aabb() const { return AABB(); } -Vector<Face3> LightmapGI::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void LightmapGI::set_use_denoiser(bool p_enable) { use_denoiser = p_enable; } diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 66acf4f487..85150b833f 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -61,6 +61,8 @@ class LightmapGIData : public Resource { Array _get_user_data() const; void _set_probe_data(const Dictionary &p_data); Dictionary _get_probe_data() const; + void _set_light_textures_data(const Array &p_data); + Array _get_light_textures_data() const; protected: static void _bind_methods(); @@ -210,16 +212,8 @@ private: } }; - struct Vector3iHash { - _FORCE_INLINE_ static uint32_t hash(const Vector3i &p_vtx) { - uint32_t h = hash_djb2_one_32(p_vtx.x); - h = hash_djb2_one_32(p_vtx.y, h); - return hash_djb2_one_32(p_vtx.z, h); - } - }; - void _plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle); - void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool, Vector3iHash> &positions_used, const AABB &p_bounds); + void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds); protected: void _validate_property(PropertyInfo &property) const override; @@ -267,7 +261,6 @@ public: GenerateProbes get_generate_probes() const; AABB get_aabb() const override; - Vector<Face3> get_faces(uint32_t p_usage_flags) const override; BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); LightmapGI(); @@ -278,4 +271,4 @@ VARIANT_ENUM_CAST(LightmapGI::GenerateProbes); VARIANT_ENUM_CAST(LightmapGI::BakeError); VARIANT_ENUM_CAST(LightmapGI::EnvironmentMode); -#endif // BAKED_LIGHTMAP_H +#endif // LIGHTMAP_GI_H diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index f641c99ec1..9b973fd6bc 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -88,7 +88,9 @@ public: instID(INVALID_GEOMETRY_ID) {} /*! Tests if we hit something. */ - _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + _FORCE_INLINE_ explicit operator bool() const { + return geomID != INVALID_GEOMETRY_ID; + } public: Vector3 org; //!< Ray origin + tnear @@ -116,7 +118,7 @@ public: virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) = 0; virtual void commit() = 0; - virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; + virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) = 0; virtual void clear_mesh_filter() = 0; static Ref<LightmapRaycaster> create(); @@ -174,9 +176,9 @@ public: }; virtual void add_mesh(const MeshData &p_mesh) = 0; - virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) = 0; - virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) = 0; - virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) = 0; + virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) = 0; + virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; + virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_probe(const Vector3 &p_position) = 0; virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index fddfa17dbb..31993f898d 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -42,9 +42,9 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { return false; } - Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); + HashMap<StringName, int>::Iterator E = blend_shape_properties.find(p_name); if (E) { - set_blend_shape_value(E->get(), p_value); + set_blend_shape_value(E->value, p_value); return true; } @@ -66,9 +66,9 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { return false; } - const Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); + HashMap<StringName, int>::ConstIterator E = blend_shape_properties.find(p_name); if (E) { - r_ret = get_blend_shape_value(E->get()); + r_ret = get_blend_shape_value(E->value); return true; } @@ -97,7 +97,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { if (mesh.is_valid()) { for (int i = 0; i < mesh->get_surface_count(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("%s/%d", PNAME("surface_material_override"), i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } } @@ -219,18 +219,6 @@ AABB MeshInstance3D::get_aabb() const { return AABB(); } -Vector<Face3> MeshInstance3D::get_faces(uint32_t p_usage_flags) const { - if (!(p_usage_flags & (FACES_SOLID | FACES_ENCLOSING))) { - return Vector<Face3>(); - } - - if (mesh.is_null()) { - return Vector<Face3>(); - } - - return mesh->get_faces(); -} - Node *MeshInstance3D::create_trimesh_collision_node() { if (mesh.is_null()) { return nullptr; diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 03ee3cd608..48d76b9a88 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MESH_INSTANCE_H -#define MESH_INSTANCE_H +#ifndef MESH_INSTANCE_3D_H +#define MESH_INSTANCE_3D_H #include "core/templates/local_vector.h" #include "scene/3d/visual_instance_3d.h" @@ -47,7 +47,7 @@ protected: NodePath skeleton_path = NodePath(".."); LocalVector<float> blend_shape_tracks; - Map<StringName, int> blend_shape_properties; + HashMap<StringName, int> blend_shape_properties; Vector<Ref<Material>> surface_override_materials; void _mesh_changed(); @@ -93,10 +93,9 @@ public: void create_debug_tangents(); virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; MeshInstance3D(); ~MeshInstance3D(); }; -#endif +#endif // MESH_INSTANCE_3D_H diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 34b1e86435..d197388fad 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -49,10 +49,6 @@ Ref<MultiMesh> MultiMeshInstance3D::get_multimesh() const { return multimesh; } -Vector<Face3> MultiMeshInstance3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - AABB MultiMeshInstance3D::get_aabb() const { if (multimesh.is_null()) { return AABB(); diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index d03b24eb4e..2fa8dd965f 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -44,8 +44,6 @@ protected: // bind helpers public: - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - void set_multimesh(const Ref<MultiMesh> &p_multimesh); Ref<MultiMesh> get_multimesh() const; @@ -55,4 +53,4 @@ public: ~MultiMeshInstance3D(); }; -#endif // MULTIMESH_INSTANCE_H +#endif // MULTIMESH_INSTANCE_3D_H diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index c4f062f0f9..3752713d6a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -35,6 +35,12 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rid"), &NavigationAgent3D::get_rid); + ClassDB::bind_method(D_METHOD("set_avoidance_enabled", "enabled"), &NavigationAgent3D::set_avoidance_enabled); + ClassDB::bind_method(D_METHOD("get_avoidance_enabled"), &NavigationAgent3D::get_avoidance_enabled); + + ClassDB::bind_method(D_METHOD("set_path_desired_distance", "desired_distance"), &NavigationAgent3D::set_path_desired_distance); + ClassDB::bind_method(D_METHOD("get_path_desired_distance"), &NavigationAgent3D::get_path_desired_distance); + ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent3D::set_target_desired_distance); ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent3D::get_target_desired_distance); @@ -62,6 +68,15 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent3D::set_path_max_distance); ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent3D::get_path_max_distance); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent3D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent3D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent3D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent3D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location); ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location); ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location); @@ -76,14 +91,20 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_target_desired_distance", "get_target_desired_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01"), "set_agent_height_offset", "get_agent_height_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_neighbor_dist", "get_neighbor_dist"); + ADD_GROUP("Pathfinding", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_path_desired_distance", "get_path_desired_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_target_desired_distance", "get_target_desired_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + + ADD_GROUP("Avoidance", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m"), "set_neighbor_dist", "get_neighbor_dist"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_time_horizon", "get_time_horizon"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m/s"), "set_max_speed", "get_max_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y"); ADD_SIGNAL(MethodInfo("path_changed")); @@ -94,24 +115,62 @@ void NavigationAgent3D::_bind_methods() { void NavigationAgent3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { - agent_parent = Object::cast_to<Node3D>(get_parent()); - if (agent_parent != nullptr) { - // place agent on navigation map first or else the RVO agent callback creation fails silently later - NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map()); - NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); - } + case NOTIFICATION_POST_ENTER_TREE: { + // need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready. + // cannot use READY as ready does not get called if Node is readded to SceneTree + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; + case NOTIFICATION_PARENTED: { + if (is_inside_tree() && (get_parent() != agent_parent)) { + // only react to PARENTED notifications when already inside_tree and parent changed, e.g. users switch nodes around + // PARENTED notification fires also when Node is added in scripts to a parent + // this would spam transforms fails and world fails while Node is outside SceneTree + // when node gets reparented when joining the tree POST_ENTER_TREE takes care of this + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } + } break; + + case NOTIFICATION_UNPARENTED: { + // if agent has no parent no point in processing it until reparented + set_agent_parent(nullptr); + set_physics_process_internal(false); + } break; + case NOTIFICATION_EXIT_TREE: { - agent_parent = nullptr; + set_agent_parent(nullptr); set_physics_process_internal(false); } break; + case NOTIFICATION_PAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { - NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); + if (avoidance_enabled) { + // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding + // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used + NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); + } _check_distance_to_target(); } } break; @@ -133,6 +192,87 @@ NavigationAgent3D::~NavigationAgent3D() { agent = RID(); // Pointless } +void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) { + avoidance_enabled = p_enabled; + if (avoidance_enabled) { + NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); + } else { + NavigationServer3D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + } +} + +bool NavigationAgent3D::get_avoidance_enabled() const { + return avoidance_enabled; +} + +void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { + // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map + NavigationServer3D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + agent_parent = Object::cast_to<Node3D>(p_agent_parent); + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map()); + } + // create new avoidance callback if enabled + set_avoidance_enabled(avoidance_enabled); + } else { + agent_parent = nullptr; + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationAgent3D::set_navigation_layers(uint32_t p_navigation_layers) { + bool navigation_layers_changed = navigation_layers != p_navigation_layers; + navigation_layers = p_navigation_layers; + if (navigation_layers_changed) { + _request_repath(); + } +} + +uint32_t NavigationAgent3D::get_navigation_layers() const { + return navigation_layers; +} + +void NavigationAgent3D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + set_navigation_layers(_navigation_layers); +} + +bool NavigationAgent3D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationAgent3D::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); + _request_repath(); +} + +RID NavigationAgent3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (agent_parent != nullptr) { + return agent_parent->get_world_3d()->get_navigation_map(); + } + return RID(); +} + +void NavigationAgent3D::set_path_desired_distance(real_t p_dd) { + path_desired_distance = p_dd; +} + void NavigationAgent3D::set_target_desired_distance(real_t p_dd) { target_desired_distance = p_dd; } @@ -181,10 +321,7 @@ real_t NavigationAgent3D::get_path_max_distance() { void NavigationAgent3D::set_target_location(Vector3 p_location) { target_location = p_location; - navigation_path.clear(); - target_reached = false; - navigation_finished = false; - update_frame_id = 0; + _request_repath(); } Vector3 NavigationAgent3D::get_target_location() const { @@ -250,7 +387,7 @@ TypedArray<String> NavigationAgent3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - warnings.push_back(TTR("The NavigationAgent3D can be used only under a spatial node.")); + warnings.push_back(RTR("The NavigationAgent3D can be used only under a Node3D inheriting parent node.")); } return warnings; @@ -294,7 +431,11 @@ void NavigationAgent3D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true); + if (map_override.is_valid()) { + navigation_path = NavigationServer3D::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); + } else { + navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigation_layers); + } navigation_finished = false; nav_path_index = 0; emit_signal(SNAME("path_changed")); @@ -307,7 +448,7 @@ void NavigationAgent3D::update_navigation() { // Check if we can advance the navigation path if (navigation_finished == false) { // Advances to the next far away location. - while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < target_desired_distance) { + while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { _check_distance_to_target(); @@ -320,6 +461,13 @@ void NavigationAgent3D::update_navigation() { } } +void NavigationAgent3D::_request_repath() { + navigation_path.clear(); + target_reached = false; + navigation_finished = false; + update_frame_id = 0; +} + void NavigationAgent3D::_check_distance_to_target() { if (!target_reached) { if (distance_to_target() < target_desired_distance) { diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index aebd5be7e4..e05f0287f7 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_AGENT_H -#define NAVIGATION_AGENT_H +#ifndef NAVIGATION_AGENT_3D_H +#define NAVIGATION_AGENT_3D_H #include "scene/main/node.h" @@ -41,21 +41,27 @@ class NavigationAgent3D : public Node { Node3D *agent_parent = nullptr; RID agent; + RID map_before_pause; + RID map_override; + bool avoidance_enabled = false; + uint32_t navigation_layers = 1; + + real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; - real_t radius; + real_t radius = 0.0; real_t navigation_height_offset = 0.0; - bool ignore_y; - real_t neighbor_dist; - int max_neighbors; - real_t time_horizon; - real_t max_speed; + bool ignore_y = false; + real_t neighbor_dist = 0.0; + int max_neighbors = 0; + real_t time_horizon = 0.0; + real_t max_speed = 0.0; real_t path_max_distance = 3.0; Vector3 target_location; Vector<Vector3> navigation_path; - int nav_path_index; + int nav_path_index = 0; bool velocity_submitted = false; Vector3 prev_safe_velocity; /// The submitted target velocity @@ -63,7 +69,7 @@ class NavigationAgent3D : public Node { bool target_reached = false; bool navigation_finished = true; // No initialized on purpose - uint32_t update_frame_id; + uint32_t update_frame_id = 0; protected: static void _bind_methods(); @@ -77,6 +83,25 @@ public: return agent; } + void set_avoidance_enabled(bool p_enabled); + bool get_avoidance_enabled() const; + + void set_agent_parent(Node *p_agent_parent); + + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const; + + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + + void set_path_desired_distance(real_t p_dd); + real_t get_path_desired_distance() const { + return path_desired_distance; + } + void set_target_desired_distance(real_t p_dd); real_t get_target_desired_distance() const { return target_desired_distance; @@ -146,7 +171,8 @@ public: private: void update_navigation(); + void _request_repath(); void _check_distance_to_target(); }; -#endif +#endif // NAVIGATION_AGENT_3D_H diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 308545b2cc..c6eda1f9cd 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -35,6 +35,8 @@ #include "servers/navigation_server_3d.h" void NavigationObstacle3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid); + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius); ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius); @@ -78,8 +80,28 @@ void NavigationObstacle3D::_notification(int p_what) { parent_node3d = nullptr; } break; + case NOTIFICATION_PAUSED: { + if (parent_node3d && !parent_node3d->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (parent_node3d && !parent_node3d->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (parent_node3d) { + if (parent_node3d && parent_node3d->is_inside_tree()) { NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin); PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); @@ -107,7 +129,12 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - warnings.push_back(TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.")); + warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object.")); + } + + if (Object::cast_to<StaticBody3D>(get_parent())) { + warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidDynamicBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." + "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail")); } return warnings; @@ -129,13 +156,13 @@ void NavigationObstacle3D::reevaluate_agent_radius() { } real_t NavigationObstacle3D::estimate_agent_radius() const { - if (parent_node3d) { + if (parent_node3d && parent_node3d->is_inside_tree()) { // Estimate the radius of this physics body real_t radius = 0.0; for (int i(0); i < parent_node3d->get_child_count(); i++) { // For each collision shape CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i)); - if (cs) { + if (cs && cs->is_inside_tree()) { // Take the distance between the Body center to the shape center real_t r = cs->get_transform().origin.length(); if (cs->get_shape().is_valid()) { @@ -146,6 +173,9 @@ real_t NavigationObstacle3D::estimate_agent_radius() const { r *= MAX(s.x, MAX(s.y, s.z)); // Takes the biggest radius radius = MAX(radius, r); + } else if (cs && !cs->is_inside_tree()) { + WARN_PRINT("A CollisionShape3D of the NavigationObstacle3D parent node was not inside the SceneTree when estimating the obstacle radius." + "\nMove the NavigationObstacle3D to a child position below any CollisionShape3D node of the parent node so the CollisionShape3D is already inside the SceneTree."); } } diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index 542d603a0a..0316fc37a8 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_OBSTACLE_H -#define NAVIGATION_OBSTACLE_H +#ifndef NAVIGATION_OBSTACLE_3D_H +#define NAVIGATION_OBSTACLE_3D_H #include "scene/3d/node_3d.h" @@ -38,6 +38,7 @@ class NavigationObstacle3D : public Node { Node3D *parent_node3d = nullptr; RID agent; + RID map_before_pause; bool estimate_radius = true; real_t radius = 1.0; @@ -72,4 +73,4 @@ private: real_t estimate_agent_radius() const; }; -#endif +#endif // NAVIGATION_OBSTACLE_3D_H diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 8f0fd8706d..2a8149c6f6 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -65,12 +65,54 @@ bool NavigationRegion3D::is_enabled() const { return enabled; } -void NavigationRegion3D::set_layers(uint32_t p_layers) { - NavigationServer3D::get_singleton()->region_set_layers(region, p_layers); +void NavigationRegion3D::set_navigation_layers(uint32_t p_navigation_layers) { + NavigationServer3D::get_singleton()->region_set_navigation_layers(region, p_navigation_layers); } -uint32_t NavigationRegion3D::get_layers() const { - return NavigationServer3D::get_singleton()->region_get_layers(region); +uint32_t NavigationRegion3D::get_navigation_layers() const { + return NavigationServer3D::get_singleton()->region_get_navigation_layers(region); +} + +void NavigationRegion3D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + set_navigation_layers(_navigation_layers); +} + +bool NavigationRegion3D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationRegion3D::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + enter_cost = MAX(p_enter_cost, 0.0); + NavigationServer3D::get_singleton()->region_set_enter_cost(region, p_enter_cost); +} + +real_t NavigationRegion3D::get_enter_cost() const { + return enter_cost; +} + +void NavigationRegion3D::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + travel_cost = MAX(p_travel_cost, 0.0); + NavigationServer3D::get_singleton()->region_set_enter_cost(region, travel_cost); +} + +real_t NavigationRegion3D::get_travel_cost() const { + return travel_cost; +} + +RID NavigationRegion3D::get_region_rid() const { + return region; } ///////////////////////////// @@ -127,6 +169,17 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes NavigationServer3D::get_singleton()->region_set_navmesh(region, p_navmesh); + if (debug_view == nullptr && is_inside_tree() && navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { + MeshInstance3D *dm = memnew(MeshInstance3D); + dm->set_mesh(navmesh->get_debug_mesh()); + if (is_enabled()) { + dm->set_material_override(get_tree()->get_debug_navigation_material()); + } else { + dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); + } + add_child(dm); + debug_view = dm; + } if (debug_view && navmesh.is_valid()) { Object::cast_to<MeshInstance3D>(debug_view)->set_mesh(navmesh->get_debug_mesh()); } @@ -161,13 +214,22 @@ void _bake_navigation_mesh(void *p_user_data) { } } -void NavigationRegion3D::bake_navigation_mesh() { +void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh."); BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - bake_thread.start(_bake_navigation_mesh, args); + if (p_on_thread && !OS::get_singleton()->can_use_threads()) { + WARN_PRINT("NavigationMesh bake 'on_thread' will be disabled as the current OS does not support multiple threads." + "\nAs a fallback the navigation mesh will bake on the main thread which can cause framerate issues."); + } + + if (p_on_thread && OS::get_singleton()->can_use_threads()) { + bake_thread.start(_bake_navigation_mesh, args); + } else { + _bake_navigation_mesh(args); + } } void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { @@ -181,7 +243,7 @@ TypedArray<String> NavigationRegion3D::get_configuration_warnings() const { if (is_visible_in_tree() && is_inside_tree()) { if (!navmesh.is_valid()) { - warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work.")); + warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work.")); } } @@ -195,15 +257,28 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion3D::is_enabled); - ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion3D::set_layers); - ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion3D::get_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationRegion3D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationRegion3D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationRegion3D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationRegion3D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion3D::get_region_rid); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationRegion3D::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationRegion3D::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion3D::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion3D::get_travel_cost); - ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh); + ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_layers", "get_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); ADD_SIGNAL(MethodInfo("navigation_mesh_changed")); ADD_SIGNAL(MethodInfo("bake_finished")); @@ -217,6 +292,8 @@ void NavigationRegion3D::_navigation_changed() { NavigationRegion3D::NavigationRegion3D() { set_notify_transform(true); region = NavigationServer3D::get_singleton()->region_create(); + NavigationServer3D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); + NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); } NavigationRegion3D::~NavigationRegion3D() { diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 1a50bb5f64..b15e97de6a 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_REGION_H -#define NAVIGATION_REGION_H +#ifndef NAVIGATION_REGION_3D_H +#define NAVIGATION_REGION_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/navigation_mesh.h" @@ -41,6 +41,9 @@ class NavigationRegion3D : public Node3D { RID region; Ref<NavigationMesh> navmesh; + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; + Node *debug_view = nullptr; Thread bake_thread; @@ -54,15 +57,26 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const; - void set_layers(uint32_t p_layers); - uint32_t get_layers() const; + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const; + + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + RID get_region_rid() const; + + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const; + + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const; void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; - /// Bakes the navigation mesh in a dedicated thread; once done, automatically + /// Bakes the navigation mesh; once done, automatically /// sets the new navigation mesh and emits a signal - void bake_navigation_mesh(); + void bake_navigation_mesh(bool p_on_thread); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); TypedArray<String> get_configuration_warnings() const override; @@ -71,4 +85,4 @@ public: ~NavigationRegion3D(); }; -#endif // NAVIGATION_REGION_H +#endif // NAVIGATION_REGION_3D_H diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 62cc7c143b..1de85d57a3 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -85,12 +85,20 @@ void Node3D::_notify_dirty() { } void Node3D::_update_local_transform() const { - if (this->get_rotation_edit_mode() != ROTATION_EDIT_MODE_BASIS) { - data.local_transform = data.local_transform.orthogonalized(); - } - data.local_transform.basis.set_euler_scale(data.rotation, data.scale); + // This function is called when the local transform (data.local_transform) is dirty and the right value is contained in the Euler rotation and scale. + + data.local_transform.basis.set_euler_scale(data.euler_rotation, data.scale, data.euler_rotation_order); + + data.dirty &= ~DIRTY_LOCAL_TRANSFORM; +} + +void Node3D::_update_rotation_and_scale() const { + // This function is called when the Euler rotation (data.euler_rotation) is dirty and the right value is contained in the local transform + + data.scale = data.local_transform.basis.get_scale(); + data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); - data.dirty &= ~DIRTY_LOCAL; + data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; } void Node3D::_propagate_transform_changed(Node3D *p_origin) { @@ -113,7 +121,7 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { #endif get_tree()->xform_change_list.add(&xform_change); } - data.dirty |= DIRTY_GLOBAL; + data.dirty |= DIRTY_GLOBAL_TRANSFORM; data.children_lock--; } @@ -137,12 +145,12 @@ void Node3D::_notification(int p_what) { if (data.top_level && !Engine::get_singleton()->is_editor_hint()) { if (data.parent) { data.local_transform = data.parent->get_global_transform() * get_transform(); - data.dirty = DIRTY_VECTORS; //global is always dirty upon entering a scene + data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // As local transform was updated, rot/scale should be dirty. } data.top_level_active = true; } - data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene + data.dirty |= DIRTY_GLOBAL_TRANSFORM; // Global is always dirty upon entering a scene. _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); @@ -180,16 +188,7 @@ void Node3D::_notification(int p_what) { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); - if (!data.gizmos_disabled) { - for (int i = 0; i < data.gizmos.size(); i++) { - data.gizmos.write[i]->create(); - if (is_visible_in_tree()) { - data.gizmos.write[i]->redraw(); - } - data.gizmos.write[i]->transform(); - } - } + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); } #endif } break; @@ -221,12 +220,49 @@ void Node3D::set_basis(const Basis &p_basis) { set_transform(Transform3D(p_basis, data.local_transform.origin)); } void Node3D::set_quaternion(const Quaternion &p_quaternion) { - set_transform(Transform3D(Basis(p_quaternion), data.local_transform.origin)); + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + // We need the scale part, so if these are dirty, update it + data.scale = data.local_transform.basis.get_scale(); + data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + } + data.local_transform.basis = Basis(p_quaternion, data.scale); + // Rotscale should not be marked dirty because that would cause precision loss issues with the scale. Instead reconstruct rotation now. + data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); + + data.dirty = DIRTY_NONE; + + _propagate_transform_changed(this); + if (data.notify_local_transform) { + notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); + } +} + +Vector3 Node3D::get_global_position() const { + return get_global_transform().get_origin(); +} + +void Node3D::set_global_position(const Vector3 &p_position) { + Transform3D transform = get_global_transform(); + transform.set_origin(p_position); + set_global_transform(transform); +} + +Vector3 Node3D::get_global_rotation() const { + return get_global_transform().get_basis().get_euler(); +} + +void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { + Transform3D transform = get_global_transform(); + Basis new_basis = transform.get_basis(); + new_basis.set_euler(p_euler_rad); + transform.set_basis(new_basis); + set_global_transform(transform); } void Node3D::set_transform(const Transform3D &p_transform) { data.local_transform = p_transform; - data.dirty |= DIRTY_VECTORS; + data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty. + _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -236,8 +272,13 @@ void Node3D::set_transform(const Transform3D &p_transform) { Basis Node3D::get_basis() const { return get_transform().basis; } + Quaternion Node3D::get_quaternion() const { - return Quaternion(get_transform().basis); + if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + _update_local_transform(); + } + + return data.local_transform.basis.get_rotation_quaternion(); } void Node3D::set_global_transform(const Transform3D &p_transform) { @@ -249,7 +290,7 @@ void Node3D::set_global_transform(const Transform3D &p_transform) { } Transform3D Node3D::get_transform() const { - if (data.dirty & DIRTY_LOCAL) { + if (data.dirty & DIRTY_LOCAL_TRANSFORM) { _update_local_transform(); } @@ -258,8 +299,8 @@ Transform3D Node3D::get_transform() const { Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); - if (data.dirty & DIRTY_GLOBAL) { - if (data.dirty & DIRTY_LOCAL) { + if (data.dirty & DIRTY_GLOBAL_TRANSFORM) { + if (data.dirty & DIRTY_LOCAL_TRANSFORM) { _update_local_transform(); } @@ -273,7 +314,7 @@ Transform3D Node3D::get_global_transform() const { data.global_transform.basis.orthonormalize(); } - data.dirty &= ~DIRTY_GLOBAL; + data.dirty &= ~DIRTY_GLOBAL_TRANSFORM; } return data.global_transform; @@ -323,13 +364,27 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { if (data.rotation_edit_mode == p_mode) { return; } + + bool transform_changed = false; + if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty & DIRTY_LOCAL_TRANSFORM)) { + data.local_transform.orthogonalize(); + transform_changed = true; + } + data.rotation_edit_mode = p_mode; - // Shearing is not allowed except in ROTATION_EDIT_MODE_BASIS. - data.dirty |= DIRTY_LOCAL; - _propagate_transform_changed(this); - if (data.notify_local_transform) { - notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); + if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE)) { + // If going to Euler mode, ensure that vectors are _not_ dirty, else the retrieved value may be wrong. + // Otherwise keep what is there, so switching back and forth between modes does not break the vectors. + + _update_rotation_and_scale(); + } + + if (transform_changed) { + _propagate_transform_changed(this); + if (data.notify_local_transform) { + notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); + } } notify_property_list_changed(); @@ -342,38 +397,47 @@ Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const { void Node3D::set_rotation_order(RotationOrder p_order) { Basis::EulerOrder order = Basis::EulerOrder(p_order); - if (data.rotation_order == order) { + if (data.euler_rotation_order == order) { return; } ERR_FAIL_INDEX(int32_t(order), 6); + bool transform_changed = false; - if (data.dirty & DIRTY_VECTORS) { - data.rotation = data.local_transform.basis.get_euler_normalized(order); - data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_VECTORS; + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + _update_rotation_and_scale(); + } else if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(order); + transform_changed = true; } else { - data.rotation = Basis::from_euler(data.rotation, data.rotation_order).get_euler_normalized(order); + data.dirty |= DIRTY_LOCAL_TRANSFORM; + transform_changed = true; } - data.rotation_order = order; - //changing rotation order should not affect transform + data.euler_rotation_order = order; - notify_property_list_changed(); //will change rotation + if (transform_changed) { + _propagate_transform_changed(this); + if (data.notify_local_transform) { + notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); + } + } + notify_property_list_changed(); // Will change the rotation property. } Node3D::RotationOrder Node3D::get_rotation_order() const { - return RotationOrder(data.rotation_order); + return RotationOrder(data.euler_rotation_order); } void Node3D::set_rotation(const Vector3 &p_euler_rad) { - if (data.dirty & DIRTY_VECTORS) { + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + // Update scale only if rotation and scale are dirty, as rotation will be overridden. data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_VECTORS; + data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; } - data.rotation = p_euler_rad; - data.dirty |= DIRTY_LOCAL; + data.euler_rotation = p_euler_rad; + data.dirty = DIRTY_LOCAL_TRANSFORM; _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -381,13 +445,14 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { } void Node3D::set_scale(const Vector3 &p_scale) { - if (data.dirty & DIRTY_VECTORS) { - data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); - data.dirty &= ~DIRTY_VECTORS; + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + // Update rotation only if rotation and scale are dirty, as scale will be overridden. + data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); + data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; } data.scale = p_scale; - data.dirty |= DIRTY_LOCAL; + data.dirty = DIRTY_LOCAL_TRANSFORM; _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -399,22 +464,16 @@ Vector3 Node3D::get_position() const { } Vector3 Node3D::get_rotation() const { - if (data.dirty & DIRTY_VECTORS) { - data.scale = data.local_transform.basis.get_scale(); - data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); - - data.dirty &= ~DIRTY_VECTORS; + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + _update_rotation_and_scale(); } - return data.rotation; + return data.euler_rotation; } Vector3 Node3D::get_scale() const { - if (data.dirty & DIRTY_VECTORS) { - data.scale = data.local_transform.basis.get_scale(); - data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order); - - data.dirty &= ~DIRTY_VECTORS; + if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + _update_rotation_and_scale(); } return data.scale; @@ -427,6 +486,7 @@ void Node3D::update_gizmos() { } if (data.gizmos.is_empty()) { + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); return; } if (data.gizmos_dirty) { @@ -444,7 +504,7 @@ void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transfor } if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); } #endif } @@ -460,7 +520,7 @@ void Node3D::clear_subgizmo_selection() { } if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); } #endif } @@ -673,7 +733,7 @@ void Node3D::rotate_z(real_t p_angle) { void Node3D::translate(const Vector3 &p_offset) { Transform3D t = get_transform(); - t.translate(p_offset); + t.translate_local(p_offset); set_transform(t); } @@ -681,7 +741,7 @@ void Node3D::translate_object_local(const Vector3 &p_offset) { Transform3D t = get_transform(); Transform3D s; - s.translate(p_offset); + s.translate_local(p_offset); set_transform(t * s); } @@ -873,14 +933,14 @@ Variant Node3D::property_get_revert(const String &p_name) { } else if (p_name == "quaternion") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Quaternion(Transform3D(variant).get_basis()); + r_ret = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion()); } else { return Quaternion(); } } else if (p_name == "rotation") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.rotation_order); + r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order); } else { return Vector3(); } @@ -912,8 +972,14 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_quaternion"), &Node3D::get_quaternion); ClassDB::bind_method(D_METHOD("set_basis", "basis"), &Node3D::set_basis); ClassDB::bind_method(D_METHOD("get_basis"), &Node3D::get_basis); + 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("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_rotation", "radians"), &Node3D::set_global_rotation); + ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node3D::get_global_rotation); + ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d); ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification); ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level); @@ -985,18 +1051,20 @@ void Node3D::_bind_methods() { BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY); BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX); - //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM3D,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ; ADD_GROUP("Transform", ""); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_lesser,no_slider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion"); ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_LINK, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 65d0e071cf..b1e129798d 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -50,9 +50,11 @@ public: class Node3D : public Node { GDCLASS(Node3D, Node); - OBJ_CATEGORY("3D"); public: + // Edit mode for the rotation. + // THIS MODE ONLY AFFECTS HOW DATA IS EDITED AND SAVED + // IT DOES _NOT_ AFFECT THE TRANSFORM LOGIC (see comment in TransformDirty). enum RotationEditMode { ROTATION_EDIT_MODE_EULER, ROTATION_EDIT_MODE_QUATERNION, @@ -69,11 +71,27 @@ public: }; private: + // For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaterinon/Scale and Euler Rotation/Scale. + // Transform and Quaterinon are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand). + // Euler needs to be kept separate because converting to Basis and back may result in a different vector (which is troublesome for users + // editing in the inspector, not only because of the numerical precision loss but because they expect these rotations to be consistent, or support + // "redundant" rotations for animation interpolation, like going from 0 to 720 degrees). + // + // As such, the system works in a way where if the local transform is set (via transform/basis/quaternion), the EULER rotation and scale becomes dirty. + // It will remain dirty until reading back is attempted (for performance reasons). Likewise, if the Euler rotation scale are set, the local transform + // will become dirty (and again, will not become valid again until read). + // + // All this is transparent from outside the Node3D API, which allows everything to works by calling these functions in exchange. + // + // Additionally, setting either transform, quaternion, Euler rotation or scale makes the global transform dirty, which will be updated when read again. + // + // NOTE: Again, RotationEditMode is _independent_ of this mechanism, it is only meant to expose the right set of properties for editing (editor) and saving + // (to scene, in order to keep the same values and avoid data loss on conversions). It has zero influence in the logic described above. enum TransformDirty { DIRTY_NONE = 0, - DIRTY_VECTORS = 1, - DIRTY_LOCAL = 2, - DIRTY_GLOBAL = 4 + DIRTY_EULER_ROTATION_AND_SCALE = 1, + DIRTY_LOCAL_TRANSFORM = 2, + DIRTY_GLOBAL_TRANSFORM = 4 }; mutable SelfList<Node> xform_change; @@ -81,8 +99,8 @@ private: struct Data { mutable Transform3D global_transform; mutable Transform3D local_transform; - mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ; - mutable Vector3 rotation; + mutable Basis::EulerOrder euler_rotation_order = Basis::EULER_ORDER_YXZ; + mutable Vector3 euler_rotation; mutable Vector3 scale = Vector3(1, 1, 1); mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER; @@ -132,6 +150,7 @@ protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } _FORCE_INLINE_ void _update_local_transform() const; + _FORCE_INLINE_ void _update_rotation_and_scale() const; void _notification(int p_what); static void _bind_methods(); @@ -163,12 +182,18 @@ public: void set_rotation(const Vector3 &p_euler_rad); void set_scale(const Vector3 &p_scale); + void set_global_position(const Vector3 &p_position); + void set_global_rotation(const Vector3 &p_euler_rad); + Vector3 get_position() const; RotationOrder get_rotation_order() const; Vector3 get_rotation() const; Vector3 get_scale() const; + Vector3 get_global_position() const; + Vector3 get_global_rotation() const; + void set_transform(const Transform3D &p_transform); void set_basis(const Basis &p_basis); void set_quaternion(const Quaternion &p_quaternion); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 855922c341..66d0a8c4e2 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -219,7 +219,7 @@ void QuadOccluder3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadOccluder3D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &QuadOccluder3D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); } QuadOccluder3D::QuadOccluder3D() { @@ -285,7 +285,7 @@ void BoxOccluder3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxOccluder3D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &BoxOccluder3D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); } BoxOccluder3D::BoxOccluder3D() { @@ -354,7 +354,7 @@ void SphereOccluder3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereOccluder3D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &SphereOccluder3D::get_radius); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_NONE, "suffix:m"), "set_radius", "get_radius"); } SphereOccluder3D::SphereOccluder3D() { @@ -433,10 +433,6 @@ AABB OccluderInstance3D::get_aabb() const { return AABB(); } -Vector<Face3> OccluderInstance3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { if (occluder == p_occluder) { return; @@ -690,25 +686,25 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { - warnings.push_back(TTR("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.")); + 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.")); } if (bake_mask == 0) { - warnings.push_back(TTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); + warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); } if (occluder.is_null()) { - warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport.")); + warnings.push_back(RTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport.")); } else { Ref<ArrayOccluder3D> arr_occluder = occluder; if (arr_occluder.is_valid() && arr_occluder->get_indices().size() < 3) { // Setting a new ArrayOccluder3D from the inspector will create an empty occluder, // so warn the user about this. - warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); + warnings.push_back(RTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); } Ref<PolygonOccluder3D> poly_occluder = occluder; if (poly_occluder.is_valid() && poly_occluder->get_polygon().size() < 3) { - warnings.push_back(TTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport.")); + warnings.push_back(RTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport.")); } } @@ -740,7 +736,7 @@ void OccluderInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder"); ADD_GROUP("Bake", "bake_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_bake_simplification_distance", "get_bake_simplification_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_bake_simplification_distance", "get_bake_simplification_distance"); } OccluderInstance3D::OccluderInstance3D() { diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 669ddba775..11d731b989 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -194,7 +194,6 @@ public: Ref<Occluder3D> get_occluder() const; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; void set_bake_mask(uint32_t p_mask); uint32_t get_bake_mask() const; @@ -212,4 +211,4 @@ public: ~OccluderInstance3D(); }; -#endif +#endif // OCCLUDER_INSTANCE_3D_H diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 5fd28a6ff3..25226ad384 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -30,6 +30,92 @@ #include "path_3d.h" +Path3D::Path3D() { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + debug_instance = RS::get_singleton()->instance_create(); + set_notify_transform(true); + _update_debug_mesh(); + } +} + +Path3D::~Path3D() { + if (debug_instance.is_valid()) { + RS::get_singleton()->free(debug_instance); + } + if (debug_mesh.is_valid()) { + RS::get_singleton()->free(debug_mesh->get_rid()); + } +} + +void Path3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + _update_debug_mesh(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_inside_tree() && debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + } + } break; + } +} + +void Path3D::_update_debug_mesh() { + SceneTree *st = SceneTree::get_singleton(); + if (!(st && st->is_debugging_paths_hint())) { + return; + } + + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + if (!(curve.is_valid())) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + return; + } + if (curve->get_point_count() < 2) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + return; + } + + Vector<Vector3> vertex_array; + + for (int i = 1; i < curve->get_point_count(); i++) { + Vector3 line_end = curve->get_point_position(i); + Vector3 line_start = curve->get_point_position(i - 1); + vertex_array.push_back(line_start); + vertex_array.push_back(line_end); + } + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; + + debug_mesh->clear_surfaces(); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); + + RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); + RS::get_singleton()->mesh_surface_set_material(debug_mesh->get_rid(), 0, st->get_debug_paths_material()->get_rid()); + if (is_inside_tree()) { + RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree()); + } +} + void Path3D::_curve_changed() { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { update_gizmos(); @@ -48,6 +134,10 @@ void Path3D::_curve_changed() { } } } + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + _update_debug_mesh(); + } } void Path3D::set_curve(const Ref<Curve3D> &p_curve) { @@ -146,7 +236,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { Vector3 sideways = up.cross(forward).normalized(); up = forward.cross(sideways).normalized(); - t.basis.set(sideways, up, forward); + t.basis.set_columns(sideways, up, forward); t.basis.scale_local(scale); t.origin = pos + sideways * h_offset + up * v_offset; @@ -157,10 +247,14 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { // for a discussion about why not Frenet frame. t.origin = pos; - - if (p_update_xyz_rot && delta_offset != 0) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. - Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized(); - Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); + if (p_update_xyz_rot && prev_offset != offset) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. + real_t sample_distance = bi * 0.01; + Vector3 t_prev_pos_a = c->interpolate_baked(prev_offset - sample_distance, cubic); + Vector3 t_prev_pos_b = c->interpolate_baked(prev_offset + sample_distance, cubic); + Vector3 t_cur_pos_a = c->interpolate_baked(offset - sample_distance, cubic); + Vector3 t_cur_pos_b = c->interpolate_baked(offset + sample_distance, cubic); + Vector3 t_prev = (t_prev_pos_a - t_prev_pos_b).normalized(); + Vector3 t_cur = (t_cur_pos_a - t_cur_pos_b).normalized(); Vector3 axis = t_prev.cross(t_cur); real_t dot = t_prev.dot(t_cur); @@ -202,7 +296,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } } - t.translate(Vector3(h_offset, v_offset, 0)); + t.translate_local(Vector3(h_offset, v_offset, 0)); } else { t.origin = pos + Vector3(h_offset, v_offset, 0); } @@ -253,11 +347,11 @@ TypedArray<String> PathFollow3D::get_configuration_warnings() const { if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path3D>(get_parent())) { - warnings.push_back(TTR("PathFollow3D only works when set as a child of a Path3D node.")); + warnings.push_back(RTR("PathFollow3D only works when set as a child of a Path3D node.")); } else { Path3D *path = Object::cast_to<Path3D>(get_parent()); if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { - warnings.push_back(TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.")); + warnings.push_back(RTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.")); } } } @@ -287,10 +381,10 @@ void PathFollow3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow3D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_loop); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser,or_greater"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser,or_greater,suffix:m"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); @@ -303,14 +397,15 @@ void PathFollow3D::_bind_methods() { } void PathFollow3D::set_offset(real_t p_offset) { - delta_offset = p_offset - offset; + ERR_FAIL_COND(!isfinite(p_offset)); + prev_offset = offset; offset = p_offset; if (path) { if (path->get_curve().is_valid()) { real_t path_length = path->get_curve()->get_baked_length(); - if (loop) { + if (loop && path_length) { offset = Math::fposmod(offset, path_length); if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) { offset = path_length; diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index e9ab557693..b4cc6db7e3 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PATH_H -#define PATH_H +#ifndef PATH_3D_H +#define PATH_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/curve.h" @@ -41,14 +41,23 @@ class Path3D : public Node3D { void _curve_changed(); + RID debug_instance; + Ref<ArrayMesh> debug_mesh; + +private: + void _update_debug_mesh(); + protected: + void _notification(int p_what); + static void _bind_methods(); public: void set_curve(const Ref<Curve3D> &p_curve); Ref<Curve3D> get_curve() const; - Path3D() {} + Path3D(); + ~Path3D(); }; class PathFollow3D : public Node3D { @@ -65,7 +74,7 @@ public: private: Path3D *path = nullptr; - real_t delta_offset = 0.0; // Change in offset since last _update_transform. + real_t prev_offset = 0.0; // Offset during the last _update_transform. real_t offset = 0.0; real_t h_offset = 0.0; real_t v_offset = 0.0; @@ -110,4 +119,4 @@ public: VARIANT_ENUM_CAST(PathFollow3D::RotationMode); -#endif // PATH_H +#endif // PATH_3D_H diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index c6d7e1df86..30f7a025fa 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -94,8 +94,10 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); parameters.max_collisions = p_max_collisions; + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. PhysicsServer3D::MotionResult result; + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { @@ -180,15 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distan } PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); - - if (colliding) { - // Don't report collision when the whole motion is done. - return (r->collision_safe_fraction < 1.0); - } else { - return false; - } + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -265,8 +261,8 @@ void StaticBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_constant_linear_velocity", "get_constant_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity", PROPERTY_HINT_NONE, U"radians,suffix:\u00B0/s"), "set_constant_angular_velocity", "get_constant_angular_velocity"); } StaticBody3D::StaticBody3D(PhysicsServer3D::BodyMode p_mode) : @@ -386,18 +382,18 @@ void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; contact_monitor->locked = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -408,17 +404,17 @@ void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; contact_monitor->locked = true; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -432,43 +428,43 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid); ERR_FAIL_COND(!body_in && !E); if (body_in) { if (!E) { E = contact_monitor->body_map.insert(objid, BodyState()); - E->get().rid = p_body; - //E->get().rc=0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_body; + //E->value.rc=0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } } - //E->get().rc++; + //E->value.rc++; if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape)); } - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape); } } else { - //E->get().rc--; + //E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape)); } - bool in_tree = E->get().in_tree; + bool in_tree = E->value.in_tree; - if (E->get().shapes.is_empty()) { + if (E->value.shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); @@ -477,7 +473,7 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p } } - contact_monitor->body_map.erase(E); + contact_monitor->body_map.remove(E); } if (node && in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_local_shape); @@ -543,7 +539,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) //bool found=false; - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj); if (!E) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -554,7 +550,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) } ShapePair sp(shape, local_shape); - int idx = E->get().shapes.find(sp); + int idx = E->value.shapes.find(sp); if (idx == -1) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -564,7 +560,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) continue; } - E->get().shapes[idx].tagged = true; + E->value.shapes[idx].tagged = true; } //put the ones to remove @@ -987,8 +983,8 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) { - warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05) { + warnings.push_back(RTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; @@ -1084,10 +1080,10 @@ void RigidDynamicBody3D::_bind_methods() { GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp,suffix:kg"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, U"0,1000,0.01,or_greater,exp,suffix:kg\u22C5m\u00B2"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater,suffix:m"), "set_center_of_mass", "get_center_of_mass"); ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); @@ -1101,11 +1097,11 @@ void RigidDynamicBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_GROUP("Angular", "angular_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity", PROPERTY_HINT_NONE, U"radians,suffix:\u00B0/s"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); ADD_GROUP("Constant Forces", "constant_"); @@ -1169,7 +1165,7 @@ bool CharacterBody3D::move_and_slide() { for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { - motion_velocity[i] = 0.0; + velocity[i] = 0.0; } } @@ -1210,6 +1206,8 @@ bool CharacterBody3D::move_and_slide() { if (!current_platform_velocity.is_equal_approx(Vector3())) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1239,7 +1237,7 @@ bool CharacterBody3D::move_and_slide() { if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } - motion_velocity += current_platform_velocity; + velocity += current_platform_velocity; } } @@ -1247,7 +1245,7 @@ bool CharacterBody3D::move_and_slide() { } void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { - Vector3 motion = motion_velocity * p_delta; + Vector3 motion = velocity * p_delta; Vector3 motion_slide_up = motion.slide(up_direction); Vector3 prev_floor_normal = floor_normal; @@ -1267,12 +1265,13 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // If the platform's ceiling push down the body. bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = velocity.dot(up_direction) > 0; Vector3 total_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, !sliding_enabled); @@ -1287,26 +1286,26 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo CollisionState result_state; _set_collision_direction(result, result_state); - // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + // If we hit a ceiling platform, we set the vertical velocity to at least the platform one. if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) { // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) { apply_ceiling_velocity = true; Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity); - Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + Vector3 motion_vertical_velocity = up_direction * up_direction.dot(velocity); if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { - motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + velocity = ceiling_vertical_velocity + velocity.slide(up_direction); } } } - if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { + if (collision_state.floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); - motion_velocity = Vector3(); + velocity = Vector3(); motion = Vector3(); last_motion = Vector3(); break; @@ -1367,11 +1366,11 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Scales the horizontal velocity according to the wall slope. if (vel_dir_facing_up) { - Vector3 slide_motion = motion_velocity.slide(result.collisions[0].normal); - // Keeps the vertical motion from motion_velocity and add the horizontal motion of the projection. - motion_velocity = up_direction * up_direction.dot(motion_velocity) + slide_motion.slide(up_direction); + Vector3 slide_motion = velocity.slide(result.collisions[0].normal); + // Keeps the vertical motion from velocity and add the horizontal motion of the projection. + velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction); } else { - motion_velocity = motion_velocity.slide(forward); + velocity = velocity.slide(forward); } // Allow only lateral motion along previous floor when already on floor. @@ -1401,7 +1400,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (stop_all_motion) { motion = Vector3(); - motion_velocity = Vector3(); + velocity = Vector3(); } } } @@ -1412,7 +1411,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized()))); if (motion_angle < wall_min_slide_angle) { motion = up_direction * motion.dot(up_direction); - motion_velocity = up_direction * motion_velocity.dot(up_direction); + velocity = up_direction * velocity.dot(up_direction); apply_default_sliding = false; } @@ -1425,7 +1424,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; Vector3 slide_motion = result.remainder.slide(collision.normal); - if (collision_state.floor && !collision_state.wall) { + if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_equal_approx(Vector3())) { // Slide using the intersection between the motion plane and the floor plane, // in order to keep the direction intact. real_t motion_length = slide_motion.length(); @@ -1437,7 +1436,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo slide_motion *= motion_length; } - if (slide_motion.dot(motion_velocity) > 0.0) { + if (slide_motion.dot(velocity) > 0.0) { motion = slide_motion; } else { motion = Vector3(); @@ -1446,10 +1445,10 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (slide_on_ceiling && result_state.ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(collision.normal); + velocity = velocity.slide(collision.normal); } else { // Avoid acceleration in slope when falling. - motion_velocity = up_direction * up_direction.dot(motion_velocity); + velocity = up_direction * up_direction.dot(velocity); } } } @@ -1457,7 +1456,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); motion = motion.slide(up_direction); } } @@ -1502,12 +1501,12 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Reset the gravity accumulation when touching the ground. if (collision_state.floor && !vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); } } void CharacterBody3D::_move_and_slide_floating(double p_delta) { - Vector3 motion = motion_velocity * p_delta; + Vector3 motion = velocity * p_delta; platform_rid = RID(); platform_object_id = ObjectID(); @@ -1517,6 +1516,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1534,7 +1534,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { break; } - if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector3(); if (result.travel.length() < margin + CMP_EPSILON) { Transform3D gt = get_global_transform(); @@ -1548,7 +1548,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { motion = result.remainder.slide(wall_normal); } - if (motion.dot(motion_velocity) <= 0.0) { + if (motion.dot(velocity) <= 0.0) { motion = Vector3(); } } @@ -1561,8 +1561,8 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { } } -void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (collision_state.floor || !was_on_floor || vel_dir_facing_up) { +void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up) { + if (collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) { return; } @@ -1571,6 +1571,7 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1596,8 +1597,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } } -bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { +bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) { + if (up_direction == Vector3() || collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) { return false; } @@ -1606,6 +1607,7 @@ bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1723,12 +1725,12 @@ real_t CharacterBody3D::get_safe_margin() const { return margin; } -const Vector3 &CharacterBody3D::get_motion_velocity() const { - return motion_velocity; +const Vector3 &CharacterBody3D::get_velocity() const { + return velocity; } -void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) { - motion_velocity = p_velocity; +void CharacterBody3D::set_velocity(const Vector3 &p_velocity) { + velocity = p_velocity; } bool CharacterBody3D::is_on_floor() const { @@ -1943,8 +1945,8 @@ void CharacterBody3D::_notification(int p_what) { void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity); - ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody3D::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody3D::get_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); @@ -1997,7 +1999,7 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "suffix:m/s", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); ADD_GROUP("Floor", "floor_"); @@ -2005,12 +2007,12 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); - ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater,suffix:m"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_GROUP("Moving Platform", "moving_platform"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:m"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING); @@ -2159,6 +2161,37 @@ void PhysicalBone3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_po PhysicsServer3D::get_singleton()->body_apply_impulse(get_rid(), p_impulse, p_position); } +void PhysicalBone3D::set_linear_velocity(const Vector3 &p_velocity) { + linear_velocity = p_velocity; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); +} + +Vector3 PhysicalBone3D::get_linear_velocity() const { + return linear_velocity; +} + +void PhysicalBone3D::set_angular_velocity(const Vector3 &p_velocity) { + angular_velocity = p_velocity; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); +} + +Vector3 PhysicalBone3D::get_angular_velocity() const { + return angular_velocity; +} + +void PhysicalBone3D::set_use_custom_integrator(bool p_enable) { + if (custom_integrator == p_enable) { + return; + } + + custom_integrator = p_enable; + PhysicsServer3D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); +} + +bool PhysicalBone3D::is_using_custom_integrator() { + return custom_integrator; +} + void PhysicalBone3D::reset_physics_simulation_state() { if (simulate_physics) { _start_physics_simulation(); @@ -2228,9 +2261,9 @@ bool PhysicalBone3D::PinJointData::_get(const StringName &p_name, Variant &r_ret void PhysicalBone3D::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/bias"), PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/damping"), PROPERTY_HINT_RANGE, "0.01,8.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/impulse_clamp"), PROPERTY_HINT_RANGE, "0.0,64.0,0.01")); } bool PhysicalBone3D::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2300,11 +2333,11 @@ bool PhysicalBone3D::ConeJointData::_get(const StringName &p_name, Variant &r_re void PhysicalBone3D::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/swing_span"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/twist_span"), PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/bias"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/relaxation"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); } bool PhysicalBone3D::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2382,12 +2415,12 @@ bool PhysicalBone3D::HingeJointData::_get(const StringName &p_name, Variant &r_r void PhysicalBone3D::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("joint_constraints/angular_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_bias"), PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_relaxation"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); } bool PhysicalBone3D::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2497,17 +2530,17 @@ bool PhysicalBone3D::SliderJointData::_get(const StringName &p_name, Variant &r_ void PhysicalBone3D::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_upper")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_lower")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_upper"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_lower"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_restitution"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_damping"), PROPERTY_HINT_RANGE, "0,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_restitution"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_damping"), PROPERTY_HINT_RANGE, "0,16.0,0.01")); } bool PhysicalBone3D::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2747,29 +2780,30 @@ bool PhysicalBone3D::SixDOFJointData::_get(const StringName &p_name, Variant &r_ } void PhysicalBone3D::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const { - const StringName axis_names[] = { "x", "y", "z" }; + const StringName axis_names[] = { PNAME("x"), PNAME("y"), PNAME("z") }; for (int i = 0; i < 3; ++i) { - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_upper")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_lower")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_spring_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_spring_stiffness")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_spring_damping")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_equilibrium_point")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/erp")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_spring_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_spring_stiffness")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_spring_damping")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_equilibrium_point")); + const String prefix = vformat("%s/%s/", PNAME("joint_constraints"), axis_names[i]); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("linear_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_upper"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_lower"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("linear_spring_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_spring_stiffness"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_spring_damping"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_equilibrium_point"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_restitution"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_damping"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("angular_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_restitution"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_damping"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("erp"))); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("angular_spring_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_spring_stiffness"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_spring_damping"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_equilibrium_point"))); } } @@ -2816,9 +2850,9 @@ void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const { names += parent->get_bone_name(i); } - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_name", PROPERTY_HINT_ENUM, names)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names)); } else { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_name")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"))); } if (joint_data) { @@ -2867,6 +2901,11 @@ void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { return; } + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + GDVIRTUAL_CALL(_integrate_forces, p_state); + /// Update bone transform. Transform3D global_transform(p_state->get_transform()); @@ -2929,24 +2968,38 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &PhysicalBone3D::set_angular_damp); ClassDB::bind_method(D_METHOD("get_angular_damp"), &PhysicalBone3D::get_angular_damp); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &PhysicalBone3D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &PhysicalBone3D::get_linear_velocity); + + ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &PhysicalBone3D::set_angular_velocity); + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &PhysicalBone3D::get_angular_velocity); + + ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &PhysicalBone3D::set_use_custom_integrator); + ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &PhysicalBone3D::is_using_custom_integrator); + ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &PhysicalBone3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &PhysicalBone3D::is_able_to_sleep); + GDVIRTUAL_BIND(_integrate_forces, "state"); + ADD_GROUP("Joint", "joint_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset"), "set_joint_offset", "get_joint_offset"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_joint_offset", "get_joint_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_RANGE, "-360,360,0.01,or_lesser,or_greater,radians"), "set_joint_rotation", "get_joint_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset"), "set_body_offset", "get_body_offset"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_body_offset", "get_body_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp,suffix:kg"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity", PROPERTY_HINT_NONE, U"radians,suffix:\u00B0/s"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 67dc7382c3..e4a41be6c0 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -212,7 +212,7 @@ private: struct ContactMonitor { bool locked = false; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; }; ContactMonitor *contact_monitor = nullptr; @@ -354,8 +354,8 @@ public: }; bool move_and_slide(); - const Vector3 &get_motion_velocity() const; - void set_motion_velocity(const Vector3 &p_velocity); + const Vector3 &get_velocity() const; + void set_velocity(const Vector3 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -416,7 +416,7 @@ private: real_t floor_max_angle = Math::deg2rad((real_t)45.0); real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); Vector3 up_direction = Vector3(0.0, 1.0, 0.0); - Vector3 motion_velocity; + Vector3 velocity; Vector3 floor_normal; Vector3 wall_normal; Vector3 ceiling_normal; @@ -474,11 +474,11 @@ private: Ref<KinematicCollision3D> _get_slide_collision(int p_bounce); Ref<KinematicCollision3D> _get_last_slide_collision(); const Vector3 &get_up_direction() const; - bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up); + bool _on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up); void set_up_direction(const Vector3 &p_up_direction); void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result, CollisionState &r_state, CollisionState p_apply_state = CollisionState(true, true, true)); void _set_platform_data(const PhysicsServer3D::MotionCollision &p_collision); - void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up); + void _snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up); protected: void _notification(int p_what); @@ -662,9 +662,13 @@ private: real_t bounce = 0.0; real_t mass = 1.0; real_t friction = 1.0; + Vector3 linear_velocity; + Vector3 angular_velocity; real_t gravity_scale = 1.0; bool can_sleep = true; + bool custom_integrator = false; + DampMode linear_damp_mode = DAMP_MODE_COMBINE; DampMode angular_damp_mode = DAMP_MODE_COMBINE; @@ -676,6 +680,7 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); void _body_state_changed(PhysicsDirectBodyState3D *p_state); @@ -691,6 +696,15 @@ private: public: void _on_bone_parent_changed(); + void set_linear_velocity(const Vector3 &p_velocity); + Vector3 get_linear_velocity() const override; + + void set_angular_velocity(const Vector3 &p_velocity); + Vector3 get_angular_velocity() const override; + + void set_use_custom_integrator(bool p_enable); + bool is_using_custom_integrator(); + #ifdef TOOLS_ENABLED void _set_gizmo_move_joint(bool p_move_joint); virtual Transform3D get_global_gizmo_transform() const override; @@ -700,7 +714,9 @@ public: const JointData *get_joint_data() const; Skeleton3D *find_skeleton_parent(); - int get_bone_id() const { return bone_id; } + int get_bone_id() const { + return bone_id; + } void set_joint_type(JointType p_joint_type); JointType get_joint_type() const; @@ -768,4 +784,4 @@ private: VARIANT_ENUM_CAST(PhysicalBone3D::JointType); VARIANT_ENUM_CAST(PhysicalBone3D::DampMode); -#endif // PHYSICS_BODY__H +#endif // PHYSICS_BODY_3D_H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index f5e08b92ca..2db5ab2d4e 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -340,7 +340,7 @@ void RayCast3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "suffix:m"), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_from_inside"), "set_hit_from_inside", "is_hit_from_inside_enabled"); diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index ad85001591..aa62f6927e 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -46,7 +46,7 @@ class RayCast3D : public Node3D { Vector3 collision_normal; Vector3 target_position = Vector3(0, -1, 0); - Set<RID> exclude; + HashSet<RID> exclude; uint32_t collision_mask = 1; bool exclude_parent_body = true; @@ -126,4 +126,4 @@ public: RayCast3D(); }; -#endif // RAY_CAST_H +#endif // RAY_CAST_3D_H diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index be655e71db..0a9d6cbbeb 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -178,10 +178,6 @@ AABB ReflectionProbe::get_aabb() const { return aabb; } -Vector<Face3> ReflectionProbe::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - void ReflectionProbe::_validate_property(PropertyInfo &property) const { if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") { if (ambient_mode != AMBIENT_COLOR) { @@ -233,9 +229,9 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1,or_greater,exp"), "set_max_distance", "get_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1,or_greater,exp,suffix:m"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled"); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index d0643496a4..a161717ece 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REFLECTIONPROBE_H -#define REFLECTIONPROBE_H +#ifndef REFLECTION_PROBE_H +#define REFLECTION_PROBE_H #include "scene/3d/visual_instance_3d.h" @@ -113,7 +113,6 @@ public: UpdateMode get_update_mode() const; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; ReflectionProbe(); ~ReflectionProbe(); @@ -122,4 +121,4 @@ public: VARIANT_ENUM_CAST(ReflectionProbe::AmbientMode); VARIANT_ENUM_CAST(ReflectionProbe::UpdateMode); -#endif // REFLECTIONPROBE_H +#endif // REFLECTION_PROBE_H diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 8b714850d6..9979052385 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -182,7 +182,7 @@ TypedArray<String> RemoteTransform3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { - warnings.push_back(TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); + warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); } return warnings; diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 03bb253578..ab134c1261 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REMOTETRANSFORM_H -#define REMOTETRANSFORM_H +#ifndef REMOTE_TRANSFORM_3D_H +#define REMOTE_TRANSFORM_3D_H #include "scene/3d/node_3d.h" @@ -75,4 +75,4 @@ public: RemoteTransform3D(); }; -#endif // REMOTETRANSFORM_H +#endif // REMOTE_TRANSFORM_3D_H diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 598897456d..4c38fccc8b 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -32,7 +32,6 @@ #include "core/object/message_queue.h" #include "core/variant/type_info.h" -#include "editor/plugins/skeleton_3d_editor_plugin.h" #include "scene/3d/physics_body_3d.h" #include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/surface_tool.h" @@ -154,14 +153,14 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < bones.size(); i++) { - String prep = "bones/" + itos(i) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + const String prep = vformat("%s/%d/", PNAME("bones"), i); + p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + 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)); } #ifndef _3D_DISABLED @@ -264,19 +263,19 @@ void Skeleton3D::_notification(int p_what) { force_update_all_bone_transforms(); // Update skins. - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - const Skin *skin = E->get()->skin.operator->(); - RID skeleton = E->get()->skeleton; + for (SkinReference *E : skin_bindings) { + const Skin *skin = E->skin.operator->(); + RID skeleton = E->skeleton; uint32_t bind_count = skin->get_bind_count(); - if (E->get()->bind_count != bind_count) { + if (E->bind_count != bind_count) { RS::get_singleton()->skeleton_allocate_data(skeleton, bind_count); - E->get()->bind_count = bind_count; - E->get()->skin_bone_indices.resize(bind_count); - E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); + E->bind_count = bind_count; + E->skin_bone_indices.resize(bind_count); + E->skin_bone_indices_ptrs = E->skin_bone_indices.ptrw(); } - if (E->get()->skeleton_version != version) { + if (E->skeleton_version != version) { for (uint32_t i = 0; i < bind_count; i++) { StringName bind_name = skin->get_bind_name(i); @@ -285,7 +284,7 @@ void Skeleton3D::_notification(int p_what) { bool found = false; for (int j = 0; j < len; j++) { if (bonesptr[j].name == bind_name) { - E->get()->skin_bone_indices_ptrs[i] = j; + E->skin_bone_indices_ptrs[i] = j; found = true; break; } @@ -293,27 +292,27 @@ void Skeleton3D::_notification(int p_what) { if (!found) { ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton3D has no bone by that name."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } } else if (skin->get_bind_bone(i) >= 0) { int bind_index = skin->get_bind_bone(i); if (bind_index >= len) { ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + "."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } else { - E->get()->skin_bone_indices_ptrs[i] = bind_index; + E->skin_bone_indices_ptrs[i] = bind_index; } } else { ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } } - E->get()->skeleton_version = version; + E->skeleton_version = version; } for (uint32_t i = 0; i < bind_count; i++) { - uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; + uint32_t bone_index = E->skin_bone_indices_ptrs[i]; ERR_CONTINUE(bone_index >= (uint32_t)len); rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); } @@ -327,13 +326,11 @@ void Skeleton3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // This is active only if the skeleton animates the physical bones // and the state of the bone is not active. - if (Engine::get_singleton()->is_editor_hint()) { - if (animate_physical_bones) { - for (int i = 0; i < bones.size(); i += 1) { - if (bones[i].physical_bone) { - if (bones[i].physical_bone->is_simulating_physics() == false) { - bones[i].physical_bone->reset_to_rest_position(); - } + if (animate_physical_bones) { + for (int i = 0; i < bones.size(); i += 1) { + if (bones[i].physical_bone) { + if (bones[i].physical_bone->is_simulating_physics() == false) { + bones[i].physical_bone->reset_to_rest_position(); } } } @@ -496,6 +493,19 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) { return bones[p_bone].rest_bone_forward_axis; } +void Skeleton3D::set_motion_scale(float p_motion_scale) { + if (p_motion_scale <= 0) { + motion_scale = 1; + ERR_FAIL_MSG("Motion scale must be larger than 0."); + } + motion_scale = p_motion_scale; +} + +float Skeleton3D::get_motion_scale() const { + ERR_FAIL_COND_V(motion_scale <= 0, 1); + return motion_scale; +} + // Skeleton creation api void Skeleton3D::add_bone(const String &p_name) { @@ -510,6 +520,7 @@ void Skeleton3D::add_bone(const String &p_name) { bones.push_back(b); process_order_dirty = true; version++; + rest_dirty = true; _make_dirty(); update_gizmos(); } @@ -568,6 +579,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { bones.write[p_bone].parent = p_parent; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -586,6 +598,7 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { bones.write[p_bone].parent = -1; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -608,6 +621,7 @@ void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) { bones.write[p_bone].child_bones = p_children; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -617,6 +631,7 @@ void Skeleton3D::add_bone_child(int p_bone, int p_child) { bones.write[p_bone].child_bones.push_back(p_child); process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -632,10 +647,12 @@ void Skeleton3D::remove_bone_child(int p_bone, int p_child) { } process_order_dirty = true; + rest_dirty = true; _make_dirty(); } Vector<int> Skeleton3D::get_parentless_bones() { + _update_process_order(); return parentless_bones; } @@ -644,6 +661,7 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].rest = p_rest; + rest_dirty = true; _make_dirty(); } Transform3D Skeleton3D::get_bone_rest(int p_bone) const { @@ -652,6 +670,14 @@ Transform3D Skeleton3D::get_bone_rest(int p_bone) const { return bones[p_bone].rest; } +Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + if (rest_dirty) { + const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } + return bones[p_bone].global_rest; +} void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { const int bone_size = bones.size(); @@ -753,8 +779,6 @@ void Skeleton3D::_make_dirty() { } void Skeleton3D::localize_rests() { - _update_process_order(); - Vector<int> bones_to_process = get_parentless_bones(); while (bones_to_process.size() > 0) { int current_bone_idx = bones_to_process[0]; @@ -946,7 +970,6 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { skin.instantiate(); skin->set_bind_count(bones.size()); - _update_process_order(); // Just in case. // Pose changed, rebuild cache of inverses. const Bone *bonesptr = bones.ptr(); @@ -986,9 +1009,9 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - if (E->get()->skin == p_skin) { - return Ref<SkinReference>(E->get()); + for (const SkinReference *E : skin_bindings) { + if (E->skin == p_skin) { + return Ref<SkinReference>(E); } } @@ -1059,6 +1082,9 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { b.pose_global_no_override = b.pose_global; } } + if (rest_dirty) { + b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest; + } if (b.local_pose_override_amount >= CMP_EPSILON) { Transform3D override_local_pose; @@ -1089,6 +1115,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx); } + rest_dirty = false; } // Helper functions @@ -1207,6 +1234,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); + ClassDB::bind_method(D_METHOD("get_bone_global_rest", "bone_idx"), &Skeleton3D::get_bone_global_rest); ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms); ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin); @@ -1240,6 +1268,9 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); + ClassDB::bind_method(D_METHOD("set_motion_scale", "motion_scale"), &Skeleton3D::set_motion_scale); + ClassDB::bind_method(D_METHOD("get_motion_scale"), &Skeleton3D::get_motion_scale); + // Helper functions ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform); ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose); @@ -1263,15 +1294,13 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack); ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); -#ifndef _3D_DISABLED + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only"); +#ifndef _3D_DISABLED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); #endif // _3D_DISABLED -#ifdef TOOLS_ENABLED ADD_SIGNAL(MethodInfo("pose_updated")); -#endif // TOOLS_ENABLED - ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); ADD_SIGNAL(MethodInfo("show_rest_only_changed")); @@ -1284,7 +1313,7 @@ Skeleton3D::Skeleton3D() { Skeleton3D::~Skeleton3D() { // Some skins may remain bound. - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - E->get()->skeleton_node = nullptr; + for (SkinReference *E : skin_bindings) { + E->skeleton_node = nullptr; } } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 80ff2a1f79..8b69410a39 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -44,13 +44,13 @@ class SkinReference : public RefCounted { GDCLASS(SkinReference, RefCounted) friend class Skeleton3D; - Skeleton3D *skeleton_node; + Skeleton3D *skeleton_node = nullptr; RID skeleton; Ref<Skin> skin; uint32_t bind_count = 0; uint64_t skeleton_version = 0; Vector<uint32_t> skin_bone_indices; - uint32_t *skin_bone_indices_ptrs; + uint32_t *skin_bone_indices_ptrs = nullptr; void _skin_changed(); protected: @@ -77,6 +77,7 @@ private: int parent; Transform3D rest; + Transform3D global_rest; _FORCE_INLINE_ void update_pose_cache() { if (pose_cache_dirty) { @@ -130,20 +131,22 @@ private: } }; - Set<SkinReference *> skin_bindings; + HashSet<SkinReference *> skin_bindings; void _skin_changed(); bool animate_physical_bones = true; Vector<Bone> bones; - bool process_order_dirty; + bool process_order_dirty = false; Vector<int> parentless_bones; void _make_dirty(); bool dirty = false; + bool rest_dirty = false; bool show_rest_only = false; + float motion_scale = 1.0; uint64_t version = 1; @@ -198,6 +201,7 @@ public: void set_bone_rest(int p_bone, const Transform3D &p_rest); Transform3D get_bone_rest(int p_bone) const; + Transform3D get_bone_global_rest(int p_bone) const; Transform3D get_bone_global_pose(int p_bone) const; Transform3D get_bone_global_pose_no_override(int p_bone) const; @@ -208,6 +212,9 @@ public: bool is_show_rest_only() const; void clear_bones(); + void set_motion_scale(float p_motion_scale); + float get_motion_scale() const; + // posing api void set_bone_pose_position(int p_bone, const Vector3 &p_position); @@ -285,4 +292,4 @@ public: ~Skeleton3D(); }; -#endif +#endif // SKELETON_3D_H diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index f29b060069..2182c5ba11 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec } } -void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { +void FabrikInverseKinematic::solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet) { if (p_solve_magnet && !r_chain.middle_chain_item) { return; } @@ -391,12 +391,12 @@ void SkeletonIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target", PROPERTY_HINT_NONE, "suffix:m"), "set_target_transform", "get_target_transform"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet", PROPERTY_HINT_NONE, "suffix:m"), "set_magnet_position", "get_magnet_position"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_min_distance", "get_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); } diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 3ced5c49d3..6ae86a2bf6 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETON_IK_H -#define SKELETON_IK_H +#ifndef SKELETON_IK_3D_H +#define SKELETON_IK_3D_H #ifndef _3D_DISABLED @@ -103,7 +103,7 @@ private: static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos); /// Special solvers that solve only chains with one end effector - static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet); static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos); public: @@ -192,4 +192,4 @@ private: #endif // _3D_DISABLED -#endif // SKELETON_IK_H +#endif // SKELETON_IK_3D_H diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp index 8ee777bcbf..d68e7fd527 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -162,12 +162,13 @@ bool SoftDynamicBody3D::_get(const StringName &p_name, Variant &r_ret) const { void SoftDynamicBody3D::_get_property_list(List<PropertyInfo> *p_list) const { const int pinned_points_indices_size = pinned_points.size(); - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "pinned_points")); + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, PNAME("pinned_points"))); for (int i = 0; i < pinned_points_indices_size; ++i) { - p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); + const String prefix = vformat("%s/%d/", PNAME("attachments"), i); + p_list->push_back(PropertyInfo(Variant::INT, prefix + PNAME("point_index"))); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, prefix + PNAME("spatial_attachment_path"))); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prefix + PNAME("offset"))); } } @@ -378,12 +379,12 @@ TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (mesh.is_null()) { - warnings.push_back(TTR("This body will be ignored until you set a mesh.")); + warnings.push_back(RTR("This body will be ignored until you set a mesh.")); } Transform3D t = get_transform(); - if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if ((ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05)) { + warnings.push_back(RTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; @@ -417,8 +418,8 @@ void SoftDynamicBody3D::_draw_soft_mesh() { PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid); } - if (!rendering_server_handler.is_ready(mesh_rid)) { - rendering_server_handler.prepare(mesh_rid, 0); + if (!rendering_server_handler->is_ready(mesh_rid)) { + rendering_server_handler->prepare(mesh_rid, 0); /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) simulation_started = true; @@ -428,11 +429,11 @@ void SoftDynamicBody3D::_draw_soft_mesh() { _update_physics_server(); - rendering_server_handler.open(); - PhysicsServer3D::get_singleton()->soft_body_update_rendering_server(physics_rid, &rendering_server_handler); - rendering_server_handler.close(); + rendering_server_handler->open(); + PhysicsServer3D::get_singleton()->soft_body_update_rendering_server(physics_rid, rendering_server_handler); + rendering_server_handler->close(); - rendering_server_handler.commit_changes(); + rendering_server_handler->commit_changes(); } void SoftDynamicBody3D::_prepare_physics_server() { @@ -688,10 +689,12 @@ bool SoftDynamicBody3D::is_ray_pickable() const { SoftDynamicBody3D::SoftDynamicBody3D() : physics_rid(PhysicsServer3D::get_singleton()->soft_body_create()) { + rendering_server_handler = memnew(SoftDynamicBodyRenderingServerHandler); PhysicsServer3D::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); } SoftDynamicBody3D::~SoftDynamicBody3D() { + memdelete(rendering_server_handler); PhysicsServer3D::get_singleton()->free(physics_rid); } diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h index c30ec701c7..04f3365f72 100644 --- a/scene/3d/soft_dynamic_body_3d.h +++ b/scene/3d/soft_dynamic_body_3d.h @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SOFT_DYNAMIC_BODY_H -#define SOFT_DYNAMIC_BODY_H +#ifndef SOFT_DYNAMIC_BODY_3D_H +#define SOFT_DYNAMIC_BODY_3D_H #include "scene/3d/mesh_instance_3d.h" #include "servers/physics_server_3d.h" class SoftDynamicBody3D; -class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler { +class SoftDynamicBodyRenderingServerHandler : public PhysicsServer3DRenderingServerHandler { friend class SoftDynamicBody3D; RID mesh; @@ -84,7 +84,7 @@ public: }; private: - SoftDynamicBodyRenderingServerHandler rendering_server_handler; + SoftDynamicBodyRenderingServerHandler *rendering_server_handler = nullptr; RID physics_rid; @@ -199,4 +199,4 @@ private: VARIANT_ENUM_CAST(SoftDynamicBody3D::DisableMode); -#endif // SOFT_DYNAMIC_BODY_H +#endif // SOFT_DYNAMIC_BODY_3D_H diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 230801bd52..f855fce318 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -73,8 +73,8 @@ void SpringArm3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spring_length"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spring_length", PROPERTY_HINT_NONE, "suffix:m"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_NONE, "suffix:m"), "set_margin", "get_margin"); } real_t SpringArm3D::get_length() const { diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h index b247ea1707..1a6f03abe4 100644 --- a/scene/3d/spring_arm_3d.h +++ b/scene/3d/spring_arm_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SPRING_ARM_H -#define SPRING_ARM_H +#ifndef SPRING_ARM_3D_H +#define SPRING_ARM_3D_H #include "scene/3d/node_3d.h" @@ -37,7 +37,7 @@ class SpringArm3D : public Node3D { GDCLASS(SpringArm3D, Node3D); Ref<Shape3D> shape; - Set<RID> excluded_objects; + HashSet<RID> excluded_objects; real_t spring_length = 1.0; real_t current_spring_length = 0.0; bool keep_child_basis = false; @@ -68,4 +68,4 @@ private: void process_spring(); }; -#endif +#endif // SPRING_ARM_3D_H diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index b9fb3e9287..cb6354f7a8 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -134,6 +134,16 @@ Color SpriteBase3D::get_modulate() const { return modulate; } +void SpriteBase3D::set_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + render_priority = p_priority; + _queue_update(); +} + +int SpriteBase3D::get_render_priority() const { + return render_priority; +} + void SpriteBase3D::set_pixel_size(real_t p_amount) { pixel_size = p_amount; _queue_update(); @@ -177,10 +187,6 @@ AABB SpriteBase3D::get_aabb() const { return aabb; } -Vector<Face3> SpriteBase3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) { return triangle_mesh; @@ -272,6 +278,17 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { return billboard_mode; } +void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { + if (texture_filter != p_filter) { + texture_filter = p_filter; + _queue_update(); + } +} + +StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const { + return texture_filter; +} + void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_centered", "centered"), &SpriteBase3D::set_centered); ClassDB::bind_method(D_METHOD("is_centered"), &SpriteBase3D::is_centered); @@ -288,6 +305,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &SpriteBase3D::set_modulate); ClassDB::bind_method(D_METHOD("get_modulate"), &SpriteBase3D::get_modulate); + ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &SpriteBase3D::set_render_priority); + ClassDB::bind_method(D_METHOD("get_render_priority"), &SpriteBase3D::get_render_priority); + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &SpriteBase3D::set_pixel_size); ClassDB::bind_method(D_METHOD("get_pixel_size"), &SpriteBase3D::get_pixel_size); @@ -303,6 +323,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode); ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode); + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &SpriteBase3D::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &SpriteBase3D::get_texture_filter); + ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); @@ -310,22 +333,28 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis", PROPERTY_HINT_ENUM, "X-Axis,Y-Axis,Z-Axis"), "set_axis", "get_axis"); ADD_GROUP("Flags", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); BIND_ENUM_CONSTANT(FLAG_SHADED); BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); + BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); + BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); @@ -548,7 +577,7 @@ void Sprite3D::_draw() { value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; if (t.d > 0) { - value |= 3 << 30; + value |= 3UL << 30; } v_tangent = value; } @@ -590,7 +619,7 @@ void Sprite3D::_draw() { set_aabb(aabb); RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); last_shader = shader_rid; @@ -599,6 +628,10 @@ void Sprite3D::_draw() { RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); last_texture = texture->get_rid(); } + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); + } } void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { @@ -759,10 +792,10 @@ void Sprite3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); ADD_GROUP("Region", "region_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region_enabled", "is_region_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect"); ADD_SIGNAL(MethodInfo("frame_changed")); ADD_SIGNAL(MethodInfo("texture_changed")); @@ -911,7 +944,7 @@ void AnimatedSprite3D::_draw() { value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; if (t.d > 0) { - value |= 3 << 30; + value |= 3UL << 30; } v_tangent = value; } @@ -952,7 +985,7 @@ void AnimatedSprite3D::_draw() { set_aabb(aabb); RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); last_shader = shader_rid; @@ -961,6 +994,10 @@ void AnimatedSprite3D::_draw() { RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); last_texture = texture->get_rid(); } + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); + } } void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { @@ -997,8 +1034,11 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { if (property.name == "frame") { property.hint = PROPERTY_HINT_RANGE; - if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) { + if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; + } else { + // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. + property.hint_string = "0,0,1"; } property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } @@ -1019,14 +1059,14 @@ void AnimatedSprite3D::_notification(int p_what) { return; } - float speed = frames->get_animation_speed(animation); - if (speed == 0) { - return; //do nothing - } - double remaining = get_process_delta_time(); while (remaining) { + double speed = frames->get_animation_speed(animation); + if (speed == 0) { + return; // Do nothing. + } + if (timeout <= 0) { timeout = 1.0 / speed; @@ -1204,12 +1244,23 @@ StringName AnimatedSprite3D::get_animation() const { TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { - warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); + warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } return warnings; } +void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0 && p_function == "play" && frames.is_valid()) { + List<StringName> al; + frames->get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 985103e190..6ac85a7bbc 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -44,6 +44,8 @@ public: FLAG_TRANSPARENT, FLAG_SHADED, FLAG_DOUBLE_SIDED, + FLAG_DISABLE_DEPTH_TEST, + FLAG_FIXED_SIZE, FLAG_MAX }; @@ -69,6 +71,7 @@ private: bool vflip = false; Color modulate = Color(1, 1, 1, 1); + int render_priority = 0; Vector3::Axis axis = Vector3::AXIS_Z; real_t pixel_size = 0.01; @@ -80,6 +83,7 @@ private: bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; + StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; bool pending_update = false; void _im_update(); @@ -97,10 +101,10 @@ protected: uint32_t mesh_surface_offsets[RS::ARRAY_MAX]; PackedByteArray vertex_buffer; PackedByteArray attribute_buffer; - uint32_t vertex_stride; - uint32_t attrib_stride; - uint32_t skin_stride; - uint32_t mesh_surface_format; + uint32_t vertex_stride = 0; + uint32_t attrib_stride = 0; + uint32_t skin_stride = 0; + uint32_t mesh_surface_format = 0; void _queue_update(); @@ -117,6 +121,9 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; + void set_render_priority(int p_priority); + int get_render_priority() const; + void set_modulate(const Color &p_color); Color get_modulate() const; @@ -131,13 +138,17 @@ public: void set_alpha_cut_mode(AlphaCutMode p_mode); AlphaCutMode get_alpha_cut_mode() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; + void set_texture_filter(StandardMaterial3D::TextureFilter p_filter); + StandardMaterial3D::TextureFilter get_texture_filter() const; + virtual Rect2 get_item_rect() const = 0; virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; + Ref<TriangleMesh> generate_triangle_mesh() const; SpriteBase3D(); @@ -237,6 +248,8 @@ public: virtual Rect2 get_item_rect() const override; virtual TypedArray<String> get_configuration_warnings() const override; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimatedSprite3D(); }; diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 8d02d26fc4..42ed52c9f2 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -40,7 +40,7 @@ public: Vector3 m_0MinvJt; Vector3 m_1MinvJt; //Optimization: can be stored in the w/last component of one of the vectors - real_t m_Adiag; + real_t m_Adiag = 0.0; real_t getDiagonal() const { return m_Adiag; } @@ -90,8 +90,8 @@ void VehicleWheel3D::_notification(int p_what) { cb->wheels.push_back(this); m_chassisConnectionPointCS = get_transform().origin; - m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + m_wheelDirectionCS = -get_transform().basis.get_column(Vector3::AXIS_Y).normalized(); + m_wheelAxleCS = get_transform().basis.get_column(Vector3::AXIS_X).normalized(); } break; case NOTIFICATION_EXIT_TREE: { @@ -109,7 +109,7 @@ TypedArray<String> VehicleWheel3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { - warnings.push_back(TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); + warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); } return warnings; @@ -274,21 +274,21 @@ void VehicleWheel3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering); ADD_GROUP("Per-Wheel Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, U"-1024,1024.0,0.01,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, U"0.0,1.0,0.01,suffix:kg\u22C5m/s\u00B2 (N)"), "set_brake", "get_brake"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01,radians"), "set_steering", "get_steering"); ADD_GROUP("VehicleBody3D Motion", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering"); ADD_GROUP("Wheel", "wheel_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_roll_influence"), "set_roll_influence", "get_roll_influence"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_radius"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_rest_length"), "set_suspension_rest_length", "get_suspension_rest_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_radius", PROPERTY_HINT_NONE, "suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_rest_length", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_rest_length", "get_suspension_rest_length"); 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"), "set_suspension_travel", "get_suspension_travel"); + 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_max_force"), "set_suspension_max_force", "get_suspension_max_force"); + 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"); @@ -684,7 +684,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; - m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); + m_axle.write[i] = wheelBasis0.get_column(Vector3::AXIS_X); //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; @@ -918,9 +918,9 @@ void VehicleBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering); ADD_GROUP("Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, U"-1024,1024.0,0.01,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, U"0.0,1.0,0.01,suffix:kg\u22C5m/s\u00B2 (N)"), "set_brake", "get_brake"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01,radians"), "set_steering", "get_steering"); } VehicleBody3D::VehicleBody3D() { diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index d2371d819b..2f3a37af2a 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VEHICLE_BODY_H -#define VEHICLE_BODY_H +#ifndef VEHICLE_BODY_3D_H +#define VEHICLE_BODY_3D_H #include "scene/3d/physics_body_3d.h" @@ -162,7 +162,7 @@ class VehicleBody3D : public RigidDynamicBody3D { real_t m_steeringValue = 0.0; real_t m_currentVehicleSpeedKmHour = 0.0; - Set<RID> exclude; + HashSet<RID> exclude; Vector<Vector3> m_forwardWS; Vector<Vector3> m_axle; @@ -210,4 +210,4 @@ public: VehicleBody3D(); }; -#endif // VEHICLE_BODY_H +#endif // VEHICLE_BODY_3D_H diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h index 7fdcacc9c1..6b27cdffc2 100644 --- a/scene/3d/velocity_tracker_3d.h +++ b/scene/3d/velocity_tracker_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SPATIAL_VELOCITY_TRACKER_H -#define SPATIAL_VELOCITY_TRACKER_H +#ifndef VELOCITY_TRACKER_3D_H +#define VELOCITY_TRACKER_3D_H #include "scene/3d/node_3d.h" @@ -58,4 +58,4 @@ public: VelocityTracker3D(); }; -#endif // SPATIAL_VELOCITY_TRACKER_H +#endif // VELOCITY_TRACKER_3D_H diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 11367d7ca9..bcf294e216 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -83,16 +83,12 @@ void VisibleOnScreenNotifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibleOnScreenNotifier3D::set_aabb); ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibleOnScreenNotifier3D::is_on_screen); - ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_aabb", "get_aabb"); ADD_SIGNAL(MethodInfo("screen_entered")); ADD_SIGNAL(MethodInfo("screen_exited")); } -Vector<Face3> VisibleOnScreenNotifier3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() { RID notifier = RS::get_singleton()->visibility_notifier_create(); RS::get_singleton()->visibility_notifier_set_aabb(notifier, aabb); diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h index 852c7e2ed3..60461569f4 100644 --- a/scene/3d/visible_on_screen_notifier_3d.h +++ b/scene/3d/visible_on_screen_notifier_3d.h @@ -57,8 +57,6 @@ public: virtual AABB get_aabb() const override; bool is_on_screen() const; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - VisibleOnScreenNotifier3D(); ~VisibleOnScreenNotifier3D(); }; @@ -98,4 +96,4 @@ public: VARIANT_ENUM_CAST(VisibleOnScreenEnabler3D::EnableMode); -#endif // VISIBILITY_NOTIFIER_H +#endif // VISIBLE_ON_SCREEN_NOTIFIER_3D_H diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index d8dffe6c7d..e76e85cfef 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -32,6 +32,14 @@ #include "scene/scene_string_names.h" +AABB VisualInstance3D::get_aabb() const { + AABB ret; + if (GDVIRTUAL_CALL(_get_aabb, ret)) { + return ret; + } + return AABB(); +} + AABB VisualInstance3D::get_transformed_aabb() const { return get_global_transform().xform(get_aabb()); } @@ -115,6 +123,7 @@ void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb); + GDVIRTUAL_BIND(_get_aabb); ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_RENDER), "set_layer_mask", "get_layer_mask"); } @@ -380,15 +389,15 @@ TypedArray<String> GeometryInstance3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) { - warnings.push_back(TTR("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.")); + 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.")); } if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_begin) && Math::is_zero_approx(visibility_range_begin_margin)) { - warnings.push_back(TTR("The GeometryInstance3D is configured to fade in smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range Begin Margin above 0.")); + warnings.push_back(RTR("The GeometryInstance3D is configured to fade in smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range Begin Margin above 0.")); } if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_end) && Math::is_zero_approx(visibility_range_end_margin)) { - warnings.push_back(TTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0.")); + warnings.push_back(RTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0.")); } return warnings; @@ -449,20 +458,20 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_overlay", "get_material_overlay"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "transparency", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_transparency", "get_transparency"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "set_extra_cull_margin", "get_extra_cull_margin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); + ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); ADD_GROUP("Visibility Range", "visibility_range_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), "set_visibility_range_fade_mode", "get_visibility_range_fade_mode"); - //ADD_SIGNAL( MethodInfo("visibility_changed")); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_ON); @@ -485,5 +494,13 @@ void GeometryInstance3D::_bind_methods() { } GeometryInstance3D::GeometryInstance3D() { - //RS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),0); +} + +GeometryInstance3D::~GeometryInstance3D() { + if (material_overlay.is_valid()) { + set_material_overlay(Ref<Material>()); + } + if (material_override.is_valid()) { + set_material_override(Ref<Material>()); + } } diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 02f62b41e5..159b14613c 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -28,14 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISUAL_INSTANCE_H -#define VISUAL_INSTANCE_H +#ifndef VISUAL_INSTANCE_3D_H +#define VISUAL_INSTANCE_3D_H #include "scene/3d/node_3d.h" class VisualInstance3D : public Node3D { GDCLASS(VisualInstance3D, Node3D); - OBJ_CATEGORY("3D Visual Nodes"); RID base; RID instance; @@ -49,6 +48,7 @@ protected: void _notification(int p_what); static void _bind_methods(); + GDVIRTUAL0RC(AABB, _get_aabb) public: enum GetFacesFlags { FACES_SOLID = 1, // solid geometry @@ -58,8 +58,7 @@ public: }; RID get_instance() const; - virtual AABB get_aabb() const = 0; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const = 0; + virtual AABB get_aabb() const; virtual AABB get_transformed_aabb() const; // helper @@ -127,7 +126,7 @@ private: float extra_cull_margin = 0.0; LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; - GIMode gi_mode = GI_MODE_DISABLED; + GIMode gi_mode = GI_MODE_STATIC; bool ignore_occlusion_culling = false; const StringName *_instance_uniform_get_remap(const StringName p_name) const; @@ -189,6 +188,7 @@ public: TypedArray<String> get_configuration_warnings() const override; GeometryInstance3D(); + virtual ~GeometryInstance3D(); }; VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting); @@ -196,4 +196,4 @@ VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance3D::GIMode); VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode); -#endif +#endif // VISUAL_INSTANCE_3D_H diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index bfe3c80a4f..ae231026a7 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -272,7 +272,8 @@ VoxelGI::Subdiv VoxelGI::get_subdiv() const { } void VoxelGI::set_extents(const Vector3 &p_extents) { - extents = p_extents; + // Prevent very small extents as these break baking if other extents are set very high. + extents = Vector3(MAX(1.0, p_extents.x), MAX(1.0, p_extents.y), MAX(1.0, p_extents.z)); update_gizmos(); } @@ -450,17 +451,13 @@ AABB VoxelGI::get_aabb() const { return AABB(-extents, extents * 2); } -Vector<Face3> VoxelGI::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - TypedArray<String> VoxelGI::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(TTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead.")); + warnings.push_back(RTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead.")); } else if (probe_data.is_null()) { - warnings.push_back(TTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI.")); + warnings.push_back(RTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI.")); } return warnings; } @@ -480,7 +477,7 @@ void VoxelGI::_bind_methods() { ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 3678bd4f3b..e1a38dd7a0 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -149,7 +149,6 @@ public: void bake(Node *p_from_node = nullptr, bool p_create_visual_debug = false); virtual AABB get_aabb() const override; - virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index f56e3caa4b..42a2a68e2d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -592,7 +592,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { bake_cells.write[p_idx].albedo[2] = 0; float alpha_average = 0; - int children_found = 0; for (int i = 0; i < 8; i++) { uint32_t child = bake_cells[p_idx].children[i]; @@ -603,8 +602,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { _fixup_plot(child, p_level + 1); alpha_average += bake_cells[child].alpha; - - children_found++; } bake_cells.write[p_idx].alpha = alpha_average / 8.0; @@ -777,8 +774,8 @@ Vector<int> Voxelizer::get_voxel_gi_level_cell_count() const { /* dt of 1d function using squared distance */ static void edt(float *f, int stride, int n) { float *d = (float *)alloca(sizeof(float) * n + sizeof(int) * n + sizeof(float) * (n + 1)); - int *v = (int *)&(d[n]); - float *z = (float *)&v[n]; + int *v = reinterpret_cast<int *>(&(d[n])); + float *z = reinterpret_cast<float *>(&v[n]); int k = 0; v[0] = 0; @@ -872,7 +869,7 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { if (d == 0) { w[i] = 0; } else { - w[i] = MIN(d, 254) + 1; + w[i] = MIN(d, 254u) + 1; } } } diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index dc7569d17c..68bce768b7 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VOXEL_LIGHT_BAKER_H -#define VOXEL_LIGHT_BAKER_H +#ifndef VOXELIZER_H +#define VOXELIZER_H #include "scene/resources/multimesh.h" @@ -86,7 +86,7 @@ private: Vector<Color> emission; }; - Map<Ref<Material>, MaterialCache> material_cache; + HashMap<Ref<Material>, MaterialCache> material_cache; AABB original_bounds; AABB po2_bounds; int axis_cell_size[3] = {}; @@ -129,4 +129,4 @@ public: Voxelizer(); }; -#endif // VOXEL_LIGHT_BAKER_H +#endif // VOXELIZER_H diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 300e761f39..fe9d9ae4dd 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -71,7 +71,7 @@ void WorldEnvironment::_update_current_environment() { } else { get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); } - get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::_update_current_camera_effects() { @@ -82,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() { get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); } - get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { @@ -139,7 +139,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!environment.is_valid() && !camera_effects.is_valid()) { - warnings.push_back(TTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both.")); + warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both.")); } if (!is_inside_tree()) { @@ -151,7 +151,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { } if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { - warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); + warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } return warnings; diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 8dbb57364c..9955aa72a8 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENARIO_FX_H -#define SCENARIO_FX_H +#ifndef WORLD_ENVIRONMENT_H +#define WORLD_ENVIRONMENT_H #include "scene/main/node.h" #include "scene/resources/camera_effects.h" @@ -60,4 +60,4 @@ public: WorldEnvironment(); }; -#endif +#endif // WORLD_ENVIRONMENT_H diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index b18819c920..40a43043c6 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -95,7 +95,7 @@ TypedArray<String> XRCamera3D::get_configuration_warnings() const { // must be child node of XROrigin3D! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); if (origin == nullptr) { - warnings.push_back(TTR("XRCamera3D must have an XROrigin3D node as its parent.")); + warnings.push_back(RTR("XRCamera3D must have an XROrigin3D node as its parent.")); }; } @@ -120,7 +120,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { Vector3 ray; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 screen_he = cm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); @@ -143,7 +143,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -173,7 +173,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co Size2 viewport_size = get_viewport()->get_visible_rect().size; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -202,7 +202,7 @@ Vector<Plane> XRCamera3D::get_frustum() const { Size2 viewport_size = get_viewport()->get_visible_rect().size; // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here. - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); }; @@ -423,15 +423,15 @@ TypedArray<String> XRNode3D::get_configuration_warnings() const { // must be child node of XROrigin! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); if (origin == nullptr) { - warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent.")); + warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent.")); } if (tracker_name == "") { - warnings.push_back(TTR("No tracker name is set.")); + warnings.push_back(RTR("No tracker name is set.")); } if (pose_name == "") { - warnings.push_back(TTR("No pose is set.")); + warnings.push_back(RTR("No pose is set.")); } } @@ -448,11 +448,6 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); - ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); - ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); - ADD_PROPERTY_DEFAULT("rumble", 0.0); - ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); @@ -558,20 +553,6 @@ Vector2 XRController3D::get_axis(const StringName &p_name) const { } } -real_t XRController3D::get_rumble() const { - if (!tracker.is_valid()) { - return 0.0; - } - - return tracker->get_rumble(); -} - -void XRController3D::set_rumble(real_t p_rumble) { - if (tracker.is_valid()) { - tracker->set_rumble(p_rumble); - } -} - XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { // get our XRServer if (!tracker.is_valid()) { @@ -596,7 +577,7 @@ Plane XRAnchor3D::get_plane() const { Vector3 location = get_position(); Basis orientation = get_transform().basis; - Plane plane(orientation.get_axis(1).normalized(), location); + Plane plane(orientation.get_column(1).normalized(), location); return plane; } @@ -608,13 +589,13 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const { if (is_visible() && is_inside_tree()) { if (tracked_camera == nullptr) { - warnings.push_back(TTR("XROrigin3D requires an XRCamera3D child node.")); + warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node.")); } } - bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); + bool xr_enabled = GLOBAL_GET("xr/shaders/enabled"); if (!xr_enabled) { - warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); + warnings.push_back(RTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); } return warnings; diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 5675cbd944..3079e20dc7 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -139,9 +139,6 @@ public: float get_value(const StringName &p_name) const; Vector2 get_axis(const StringName &p_name) const; - real_t get_rumble() const; - void set_rumble(real_t p_rumble); - XRPositionalTracker::TrackerHand get_tracker_hand() const; XRController3D() {} |