diff options
author | George L. Albany <Megacake1234@gmail.com> | 2024-11-26 19:30:58 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-26 19:30:58 +0000 |
commit | b06d20bf39d15ec736d08d4e4fcb32e0c3c1ce1e (patch) | |
tree | 79c2a4c34f2d888ff962d76edf474c518d1abdea /scene | |
parent | c5b1645e60a59c0292c04bece3fdb0715a61afea (diff) | |
parent | e58e18261ea7ed3978146ef8d77a900be2601be3 (diff) | |
download | redot-engine-b06d20bf39d15ec736d08d4e4fcb32e0c3c1ce1e.tar.gz |
Merge pull request #885 from Spartan322/merge/d09d82d
Merge commit godotengine/godot@d09d82d
Diffstat (limited to 'scene')
29 files changed, 582 insertions, 105 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 165b4a89d6..847704e058 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -412,7 +412,7 @@ void AudioStreamPlayer2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,suffix:dB"), "set_volume_db", "get_volume_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_ONESHOT, "", 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, "1,4096,1,or_greater,exp,suffix:px"), "set_max_distance", "get_max_distance"); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index b7cd72fe7d..b5fd71a88f 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -508,6 +508,10 @@ bool CPUParticles2D::get_split_scale() { } void CPUParticles2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emitting") { + p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE; + } + if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -1287,7 +1291,7 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &CPUParticles2D::restart); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "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,suffix:s"), "set_lifetime", "get_lifetime"); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index dfce5d43bd..0eb3758d0b 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -386,6 +386,9 @@ Ref<Texture2D> GPUParticles2D::get_texture() const { } void GPUParticles2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emitting") { + p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE; + } } void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { @@ -820,7 +823,7 @@ void GPUParticles2D::_bind_methods() { ADD_SIGNAL(MethodInfo("finished")); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting"); ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false. ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_amount_ratio", "get_amount_ratio"); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index fcd0f488bd..9434aabc15 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -836,7 +836,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db", PROPERTY_HINT_RANGE, "-24,6,suffix:dB"), "set_max_db", "get_max_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_ONESHOT, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,or_greater,suffix:m"), "set_max_distance", "get_max_distance"); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 3ad018f8e6..0052bd223f 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -545,6 +545,10 @@ AABB CPUParticles3D::capture_aabb() const { } void CPUParticles3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emitting") { + p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE; + } + if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -1483,7 +1487,7 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart); ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "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,suffix:s"), "set_lifetime", "get_lifetime"); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 83609dfa91..95e183f302 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -416,6 +416,10 @@ AABB GPUParticles3D::capture_aabb() const { } void GPUParticles3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emitting") { + p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE; + } + if (p_property.name.begins_with("draw_pass_")) { int index = p_property.name.get_slicec('_', 2).to_int() - 1; if (index >= draw_passes.size()) { @@ -746,7 +750,7 @@ void GPUParticles3D::_bind_methods() { ADD_SIGNAL(MethodInfo("finished")); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting"); ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false. ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_amount_ratio", "get_amount_ratio"); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 8cd6f0f547..13ac899a3a 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1191,6 +1191,8 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa return BAKE_ERROR_MESHES_INVALID; } else if (bake_err == Lightmapper::BAKE_ERROR_ATLAS_TOO_SMALL) { return BAKE_ERROR_ATLAS_TOO_SMALL; + } else if (bake_err == Lightmapper::BAKE_ERROR_USER_ABORTED) { + return BAKE_ERROR_USER_ABORTED; } // POSTBAKE: Save Textures. diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index fc4615f067..8673ce32b8 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -149,6 +149,7 @@ public: BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, BAKE_ERROR_ATLAS_TOO_SMALL, + BAKE_ERROR_USER_ABORTED, }; enum BakeQuality { diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 6ef7e46f96..fd5b729ba3 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -391,6 +391,17 @@ VoxelGI::BakeBeginFunc VoxelGI::bake_begin_function = nullptr; VoxelGI::BakeStepFunc VoxelGI::bake_step_function = nullptr; VoxelGI::BakeEndFunc VoxelGI::bake_end_function = nullptr; +static int voxelizer_plot_bake_base = 0; +static int voxelizer_plot_bake_total = 0; + +static bool voxelizer_plot_bake_step_function(int current, int) { + return VoxelGI::bake_step_function((voxelizer_plot_bake_base + current) * 500 / voxelizer_plot_bake_total, RTR("Plotting Meshes")); +} + +static bool voxelizer_sdf_bake_step_function(int current, int total) { + return VoxelGI::bake_step_function(500 + current * 500 / total, RTR("Generating Distance Field")); +} + Vector3i VoxelGI::get_estimated_cell_size() const { static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 }; int cell_subdiv = subdiv_value[subdiv]; @@ -434,22 +445,27 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { _find_meshes(p_from_node, mesh_list); if (bake_begin_function) { - bake_begin_function(mesh_list.size() + 1); + bake_begin_function(); } - int pmc = 0; + Voxelizer::BakeStepFunc voxelizer_step_func = bake_step_function != nullptr ? voxelizer_plot_bake_step_function : nullptr; + voxelizer_plot_bake_total = voxelizer_plot_bake_base = 0; for (PlotMesh &E : mesh_list) { - if (bake_step_function) { - bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(mesh_list.size())); + voxelizer_plot_bake_total += baker.get_bake_steps(E.mesh); + } + for (PlotMesh &E : mesh_list) { + if (baker.plot_mesh(E.local_xform, E.mesh, E.instance_materials, E.override_material, voxelizer_step_func) != Voxelizer::BAKE_RESULT_OK) { + baker.end_bake(); + if (bake_end_function) { + bake_end_function(); + } + return; } - - pmc++; - - baker.plot_mesh(E.local_xform, E.mesh, E.instance_materials, E.override_material); + voxelizer_plot_bake_base += baker.get_bake_steps(E.mesh); } if (bake_step_function) { - bake_step_function(pmc++, RTR("Finishing Plot")); + bake_step_function(500, RTR("Finishing Plot")); } baker.end_bake(); @@ -478,19 +494,22 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { } if (bake_step_function) { - bake_step_function(pmc++, RTR("Generating Distance Field")); + bake_step_function(500, RTR("Generating Distance Field")); } - Vector<uint8_t> df = baker.get_sdf_3d_image(); + voxelizer_step_func = bake_step_function != nullptr ? voxelizer_sdf_bake_step_function : nullptr; - RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data_new->get_rid(), exposure_normalization); + Vector<uint8_t> df; + if (baker.get_sdf_3d_image(df, voxelizer_step_func) == Voxelizer::BAKE_RESULT_OK) { + RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data_new->get_rid(), exposure_normalization); - probe_data_new->allocate(baker.get_to_cell_space_xform(), AABB(-size / 2, size), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); + probe_data_new->allocate(baker.get_to_cell_space_xform(), AABB(-size / 2, size), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); - set_probe_data(probe_data_new); + set_probe_data(probe_data_new); #ifdef TOOLS_ENABLED - probe_data_new->set_edited(true); //so it gets saved + probe_data_new->set_edited(true); //so it gets saved #endif + } } if (bake_end_function) { diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 55bc23c62b..0f91a5d32b 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -110,8 +110,8 @@ public: }; - typedef void (*BakeBeginFunc)(int); - typedef void (*BakeStepFunc)(int, const String &); + typedef void (*BakeBeginFunc)(); + typedef bool (*BakeStepFunc)(int, const String &); typedef void (*BakeEndFunc)(); private: diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index e5ada49b0d..6256d8da61 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -384,8 +384,24 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material return mc; } -void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material) { - ERR_FAIL_COND_MSG(!p_xform.is_finite(), "Invalid mesh bake transform."); +int Voxelizer::get_bake_steps(Ref<Mesh> &p_mesh) const { + int bake_total = 0; + for (int i = 0; i < p_mesh->get_surface_count(); i++) { + if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; // Only triangles. + } + Array a = p_mesh->surface_get_arrays(i); + Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + Vector<int> index = a[Mesh::ARRAY_INDEX]; + bake_total += (index.size() > 0 ? index.size() : vertices.size()) / 3; + } + return bake_total; +} + +Voxelizer::BakeResult Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material, BakeStepFunc p_bake_step_func) { + ERR_FAIL_COND_V_MSG(!p_xform.is_finite(), BAKE_RESULT_INVALID_PARAMETER, "Invalid mesh bake transform."); + + int bake_total = get_bake_steps(p_mesh), bake_current = 0; for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { @@ -430,6 +446,13 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const V Vector2 uvs[3]; Vector3 normal[3]; + bake_current++; + if (p_bake_step_func != nullptr && (bake_current & 2047) == 1) { + if (p_bake_step_func(bake_current, bake_total)) { + return BAKE_RESULT_CANCELLED; + } + } + for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]); } @@ -462,6 +485,13 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const V Vector2 uvs[3]; Vector3 normal[3]; + bake_current++; + if (p_bake_step_func != nullptr && (bake_current & 2047) == 1) { + if (p_bake_step_func(bake_current, bake_total)) { + return BAKE_RESULT_CANCELLED; + } + } + for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[j * 3 + k]); } @@ -489,6 +519,8 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const V } max_original_cells = bake_cells.size(); + + return BAKE_RESULT_OK; } void Voxelizer::_sort() { @@ -823,7 +855,7 @@ static void edt(float *f, int stride, int n) { #undef square -Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { +Voxelizer::BakeResult Voxelizer::get_sdf_3d_image(Vector<uint8_t> &r_image, BakeStepFunc p_bake_step_function) const { Vector3i octree_size = get_voxel_gi_octree_size(); uint32_t float_count = octree_size.x * octree_size.y * octree_size.z; @@ -851,9 +883,17 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { //process in each direction + int bake_total = octree_size.x * 2 + octree_size.y, bake_current = 0; + //xy->z - for (int i = 0; i < octree_size.x; i++) { + for (int i = 0; i < octree_size.x; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.y; j++) { edt(&work_memory[i + j * y_mult], z_mult, octree_size.z); } @@ -861,23 +901,34 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { //xz->y - for (int i = 0; i < octree_size.x; i++) { + for (int i = 0; i < octree_size.x; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.z; j++) { edt(&work_memory[i + j * z_mult], y_mult, octree_size.y); } } //yz->x - for (int i = 0; i < octree_size.y; i++) { + for (int i = 0; i < octree_size.y; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.z; j++) { edt(&work_memory[i * y_mult + j * z_mult], 1, octree_size.x); } } - Vector<uint8_t> image3d; - image3d.resize(float_count); + r_image.resize(float_count); { - uint8_t *w = image3d.ptrw(); + uint8_t *w = r_image.ptrw(); for (uint32_t i = 0; i < float_count; i++) { uint32_t d = uint32_t(Math::sqrt(work_memory[i])); if (d == 0) { @@ -890,7 +941,7 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { memdelete_arr(work_memory); - return image3d; + return BAKE_RESULT_OK; } #undef INF diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index f65da6a6d9..3daa28d42d 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -36,6 +36,15 @@ #include "scene/resources/multimesh.h" class Voxelizer { +public: + enum BakeResult { + BAKE_RESULT_OK, + BAKE_RESULT_INVALID_PARAMETER, + BAKE_RESULT_CANCELLED, + }; + + typedef bool (*BakeStepFunc)(int, int); + private: enum : uint32_t { CHILD_EMPTY = 0xFFFFFFFF @@ -114,7 +123,8 @@ private: public: void begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization); - void plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); + int get_bake_steps(Ref<Mesh> &p_mesh) const; + BakeResult plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material, BakeStepFunc p_bake_step_function); void end_bake(); int get_voxel_gi_octree_depth() const; @@ -123,7 +133,7 @@ public: Vector<uint8_t> get_voxel_gi_octree_cells() const; Vector<uint8_t> get_voxel_gi_data_cells() const; Vector<int> get_voxel_gi_level_cell_count() const; - Vector<uint8_t> get_sdf_3d_image() const; + BakeResult get_sdf_3d_image(Vector<uint8_t> &r_image, BakeStepFunc p_bake_step_function) const; Ref<MultiMesh> create_debug_multimesh(); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 35e974c92e..d4a16ccb10 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1005,6 +1005,14 @@ String AnimationNodeTimeSeek::get_caption() const { return "TimeSeek"; } +void AnimationNodeTimeSeek::set_explicit_elapse(bool p_enable) { + explicit_elapse = p_enable; +} + +bool AnimationNodeTimeSeek::is_explicit_elapse() const { + return explicit_elapse; +} + AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { double cur_seek_pos = get_parameter(seek_pos_request); @@ -1013,7 +1021,7 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer if (Animation::is_greater_or_equal_approx(cur_seek_pos, 0)) { pi.time = cur_seek_pos; pi.seeked = true; - pi.is_external_seeking = true; + pi.is_external_seeking = explicit_elapse; set_parameter(seek_pos_request, -1.0); // Reset. } @@ -1024,6 +1032,12 @@ AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); } +void AnimationNodeTimeSeek::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_explicit_elapse", "enable"), &AnimationNodeTimeSeek::set_explicit_elapse); + ClassDB::bind_method(D_METHOD("is_explicit_elapse"), &AnimationNodeTimeSeek::is_explicit_elapse); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "explicit_elapse"), "set_explicit_elapse", "is_explicit_elapse"); +} + ///////////////////////////////////////////////// bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_value) { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 6cb208d710..34f4ce39b3 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -304,6 +304,10 @@ class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); StringName seek_pos_request = PNAME("seek_request"); + bool explicit_elapse = true; + +protected: + static void _bind_methods(); public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; @@ -313,6 +317,9 @@ public: virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + void set_explicit_elapse(bool p_enable); + bool is_explicit_elapse() const; + AnimationNodeTimeSeek(); }; diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 4566f34674..f4b497bf59 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -35,6 +35,8 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" +#include "core/string/print_string.h" +#include "core/string/string_name.h" #include "scene/2d/audio_stream_player_2d.h" #include "scene/animation/animation_player.h" #include "scene/audio/audio_stream_player.h" @@ -129,6 +131,9 @@ void AnimationMixer::_validate_property(PropertyInfo &p_property) const { p_property.usage |= PROPERTY_USAGE_READ_ONLY; } #endif // TOOLS_ENABLED + if (root_motion_track.is_empty() && p_property.name == "root_motion_local") { + p_property.usage = PROPERTY_USAGE_NONE; + } } /* -------------------------------------------- */ @@ -269,6 +274,16 @@ bool AnimationMixer::has_animation_library(const StringName &p_name) const { return false; } +StringName AnimationMixer::get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const { + ERR_FAIL_COND_V(p_animation_library.is_null(), StringName()); + for (const AnimationLibraryData &lib : animation_libraries) { + if (lib.library == p_animation_library) { + return lib.name; + } + } + return StringName(); +} + StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animation) const { for (const KeyValue<StringName, AnimationData> &E : animation_set) { if (E.value.animation == p_animation) { @@ -735,6 +750,15 @@ bool AnimationMixer::_update_caches() { } } + if (is_value && callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { + if (child) { + PropertyInfo prop_info; + ClassDB::get_property_info(child->get_class_name(), path.get_concatenated_subnames(), &prop_info); + if (prop_info.hint == PROPERTY_HINT_ONESHOT) { + WARN_PRINT_ED(vformat("%s: '%s', Value Track: '%s' is oneshot property, but will be continuously updated. Consider setting a value other than ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS to AnimationMixer.callback_mode_dominant.", mixer_name, String(E), String(path))); + } + } + } } break; case Animation::TYPE_POSITION_3D: case Animation::TYPE_ROTATION_3D: @@ -1202,6 +1226,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { + int rot_track = -1; + if (root_motion_local) { + rot_track = a->find_track(a->track_get_path(i), Animation::TYPE_ROTATION_3D); + } double prev_time = time - delta; if (!backward) { if (Animation::is_less_approx(prev_time, start)) { @@ -1236,41 +1264,92 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } } } - Vector3 loc[2]; - if (!backward) { - if (Animation::is_greater_approx(prev_time, time)) { - Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); - if (err != OK) { - continue; + if (rot_track >= 0) { + Vector3 loc[2]; + Quaternion rot; + if (!backward) { + if (Animation::is_greater_approx(prev_time, time)) { + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, end, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + + a->try_rotation_track_interpolate(rot_track, end, &rot); + rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx); + + root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend; + prev_time = start; } - loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); - a->try_position_track_interpolate(i, end, &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); - root_motion_cache.loc += (loc[1] - loc[0]) * blend; - prev_time = start; + } else { + if (Animation::is_less_approx(prev_time, time)) { + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, start, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + + a->try_rotation_track_interpolate(rot_track, start, &rot); + rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx); + + root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend; + prev_time = end; + } + } + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, time, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + + a->try_rotation_track_interpolate(rot_track, time, &rot); + rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx); + + root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend; + prev_time = !backward ? start : end; } else { - if (Animation::is_less_approx(prev_time, time)) { - Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); - if (err != OK) { - continue; + Vector3 loc[2]; + if (!backward) { + if (Animation::is_greater_approx(prev_time, time)) { + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, end, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + root_motion_cache.loc += (loc[1] - loc[0]) * blend; + prev_time = start; + } + } else { + if (Animation::is_less_approx(prev_time, time)) { + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, start, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + root_motion_cache.loc += (loc[1] - loc[0]) * blend; + prev_time = end; } - loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); - a->try_position_track_interpolate(i, start, &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); - root_motion_cache.loc += (loc[1] - loc[0]) * blend; - prev_time = end; } + Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); + a->try_position_track_interpolate(i, time, &loc[1]); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); + root_motion_cache.loc += (loc[1] - loc[0]) * blend; + prev_time = !backward ? start : end; } - Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); - if (err != OK) { - continue; - } - loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); - a->try_position_track_interpolate(i, time, &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); - root_motion_cache.loc += (loc[1] - loc[0]) * blend; - prev_time = !backward ? start : end; } { Vector3 loc; @@ -1345,6 +1424,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); a->try_rotation_track_interpolate(i, start, &rot[1]); + rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = end; } @@ -1420,8 +1500,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx); a->try_scale_track_interpolate(i, end, &scale[1]); - root_motion_cache.scale += (scale[1] - scale[0]) * blend; scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); + root_motion_cache.scale += (scale[1] - scale[0]) * blend; prev_time = start; } } else { @@ -1992,12 +2072,21 @@ void AnimationMixer::clear_caches() { void AnimationMixer::set_root_motion_track(const NodePath &p_track) { root_motion_track = p_track; + notify_property_list_changed(); } NodePath AnimationMixer::get_root_motion_track() const { return root_motion_track; } +void AnimationMixer::set_root_motion_local(bool p_enabled) { + root_motion_local = p_enabled; +} + +bool AnimationMixer::is_root_motion_local() const { + return root_motion_local; +} + Vector3 AnimationMixer::get_root_motion_position() const { return root_motion_position; } @@ -2343,6 +2432,8 @@ void AnimationMixer::_bind_methods() { /* ---- Root motion accumulator for Skeleton3D ---- */ ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationMixer::set_root_motion_track); ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationMixer::get_root_motion_track); + ClassDB::bind_method(D_METHOD("set_root_motion_local", "enabled"), &AnimationMixer::set_root_motion_local); + ClassDB::bind_method(D_METHOD("is_root_motion_local"), &AnimationMixer::is_root_motion_local); ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationMixer::get_root_motion_position); ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationMixer::get_root_motion_rotation); @@ -2370,6 +2461,7 @@ void AnimationMixer::_bind_methods() { ADD_GROUP("Root Motion", "root_motion_"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "root_motion_local"), "set_root_motion_local", "is_root_motion_local"); ADD_GROUP("Audio", "audio_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony"); diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index 757ccb8ff6..06fb17ce42 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -336,6 +336,7 @@ protected: /* ---- Root motion accumulator for Skeleton3D ---- */ NodePath root_motion_track; + bool root_motion_local = false; Vector3 root_motion_position = Vector3(0, 0, 0); Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1); Vector3 root_motion_scale = Vector3(0, 0, 0); @@ -411,6 +412,7 @@ public: void get_animation_library_list(List<StringName> *p_animations) const; Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const; bool has_animation_library(const StringName &p_name) const; + StringName get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const; StringName find_animation_library(const Ref<Animation> &p_animation) const; Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library); void remove_animation_library(const StringName &p_name); @@ -448,6 +450,9 @@ public: void set_root_motion_track(const NodePath &p_track); NodePath get_root_motion_track() const; + void set_root_motion_local(bool p_enabled); + bool is_root_motion_local() const; + Vector3 get_root_motion_position() const; Quaternion get_root_motion_rotation() const; Vector3 get_root_motion_scale() const; diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index 66bb637236..6897f7e8c4 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -96,7 +96,6 @@ void RootMotionView::_notification(int p_what) { if (has_node(path)) { Node *node = get_node(path); - AnimationMixer *mixer = Object::cast_to<AnimationMixer>(node); if (mixer && mixer->is_active() && mixer->get_root_motion_track() != NodePath()) { if (is_processing_internal() && mixer->get_callback_mode_process() == AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS) { @@ -108,12 +107,12 @@ void RootMotionView::_notification(int p_what) { set_process_internal(true); set_physics_process_internal(false); } + transform.origin = mixer->get_root_motion_position(); transform.basis = mixer->get_root_motion_rotation(); // Scale is meaningless. - diff = mixer->get_root_motion_rotation_accumulator(); + diff = mixer->is_root_motion_local() ? Quaternion() : mixer->get_root_motion_rotation_accumulator(); } } - if (!first && transform == Transform3D()) { return; } diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index c90c80d871..f386996558 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -264,7 +264,7 @@ void AudioStreamPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,suffix:dB"), "set_volume_db", "get_volume_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_ONESHOT, "", 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::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target"); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index e3ce8033e3..e6edb5e0bf 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -219,14 +219,14 @@ void fragment() { circle_ok_color_shader->set_code(OK_COLOR_SHADER + R"( // ColorPicker ok color hsv circle shader. -uniform float v = 1.0; +uniform float ok_hsl_l = 1.0; void fragment() { float x = UV.x - 0.5; float y = UV.y - 0.5; float h = atan(y, x) / (2.0 * M_PI); float s = sqrt(x * x + y * y) * 2.0; - vec3 col = okhsl_to_srgb(vec3(h, s, v)); + vec3 col = okhsl_to_srgb(vec3(h, s, ok_hsl_l)); x += 0.001; y += 0.001; float b = float(sqrt(x * x + y * y) < 0.5); @@ -389,10 +389,21 @@ void ColorPicker::_slider_value_changed() { color = modes[current_mode]->get_color(); modes[current_mode]->_value_changed(); - if (current_mode == MODE_HSV || current_mode == MODE_OKHSL) { + if (current_mode == MODE_HSV) { h = sliders[0]->get_value() / 360.0; s = sliders[1]->get_value() / 100.0; v = sliders[2]->get_value() / 100.0; + ok_hsl_h = color.get_ok_hsl_h(); + ok_hsl_s = color.get_ok_hsl_s(); + ok_hsl_l = color.get_ok_hsl_l(); + last_color = color; + } else if (current_mode == MODE_OKHSL) { + ok_hsl_h = sliders[0]->get_value() / 360.0; + ok_hsl_s = sliders[1]->get_value() / 100.0; + ok_hsl_l = sliders[2]->get_value() / 100.0; + h = color.get_h(); + s = color.get_s(); + v = color.get_v(); last_color = color; } @@ -506,20 +517,17 @@ Vector<float> ColorPicker::get_active_slider_values() { } void ColorPicker::_copy_color_to_hsv() { - if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { - h = color.get_ok_hsl_h(); - s = color.get_ok_hsl_s(); - v = color.get_ok_hsl_l(); - } else { - h = color.get_h(); - s = color.get_s(); - v = color.get_v(); - } + ok_hsl_h = color.get_ok_hsl_h(); + ok_hsl_s = color.get_ok_hsl_s(); + ok_hsl_l = color.get_ok_hsl_l(); + h = color.get_h(); + s = color.get_s(); + v = color.get_v(); } void ColorPicker::_copy_hsv_to_color() { if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { - color.set_ok_hsl(h, s, v, color.a); + color.set_ok_hsl(ok_hsl_h, ok_hsl_s, ok_hsl_l, color.a); } else { color.set_hsv(h, s, v, color.a); } @@ -1203,8 +1211,8 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { int x; int y; if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { - x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (theme_cache.picker_cursor->get_width() / 2); - y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (theme_cache.picker_cursor->get_height() / 2); + x = center.x + (center.x * Math::cos((actual_shape == SHAPE_OKHSL_CIRCLE ? ok_hsl_h : h) * Math_TAU) * s) - (theme_cache.picker_cursor->get_width() / 2); + y = center.y + (center.y * Math::sin((actual_shape == SHAPE_OKHSL_CIRCLE ? ok_hsl_h : h) * Math_TAU) * s) - (theme_cache.picker_cursor->get_height() / 2); } else { real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0; @@ -1241,11 +1249,11 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { Vector<Point2> points; Vector<Color> colors; Color col; - col.set_ok_hsl(h, s, 1); + col.set_ok_hsl(ok_hsl_h, ok_hsl_s, 1); Color col2; - col2.set_ok_hsl(h, s, 0.5); + col2.set_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5); Color col3; - col3.set_ok_hsl(h, s, 0); + col3.set_ok_hsl(ok_hsl_h, ok_hsl_s, 0); points.resize(6); colors.resize(6); points.set(0, Vector2(c->get_size().x, 0)); @@ -1261,8 +1269,8 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { colors.set(4, col2); colors.set(5, col); c->draw_polygon(points, colors); - int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1); - col.set_ok_hsl(h, 1, v); + int y = c->get_size().y - c->get_size().y * CLAMP(ok_hsl_l, 0, 1); + col.set_ok_hsl(ok_hsl_h, 1, ok_hsl_l); c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); } else if (actual_shape == SHAPE_VHS_CIRCLE) { Vector<Point2> points; @@ -1286,8 +1294,10 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } } else if (p_which == 2) { c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1)); - if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { + if (actual_shape == SHAPE_VHS_CIRCLE) { circle_mat->set_shader_parameter("v", v); + } else if (actual_shape == SHAPE_OKHSL_CIRCLE) { + circle_mat->set_shader_parameter("ok_hsl_l", ok_hsl_l); } } } @@ -1311,6 +1321,8 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t rad = center.angle_to_point(bev->get_position()); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); + ok_hsl_h = h; + ok_hsl_s = s; } else { return; } @@ -1378,6 +1390,8 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t rad = center.angle_to_point(mev->get_position()); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); + ok_hsl_h = h; + ok_hsl_s = s; } else { if (spinning) { real_t rad = center.angle_to_point(mev->get_position()); @@ -1415,6 +1429,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height); if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { v = 1.0 - (y / w_edit->get_size().height); + ok_hsl_l = v; } else { h = y / w_edit->get_size().height; } @@ -1443,6 +1458,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height); if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { v = 1.0 - (y / w_edit->get_size().height); + ok_hsl_l = v; } else { h = y / w_edit->get_size().height; } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index bc0212d234..95aa94603b 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -213,6 +213,11 @@ private: float h = 0.0; float s = 0.0; float v = 0.0; + + float ok_hsl_h = 0.0; + float ok_hsl_s = 0.0; + float ok_hsl_l = 0.0; + Color last_color; struct ThemeCache { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 635f5d2d2a..2952109a78 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1835,8 +1835,7 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be. - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } @@ -3111,6 +3110,10 @@ void RichTextLabel::add_text(const String &p_text) { } void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) { + if (!internal_stack_editing) { + stack_externally_modified = true; + } + p_item->parent = current; p_item->E = current->subitems.push_back(p_item); p_item->index = current_idx++; @@ -3384,6 +3387,8 @@ bool RichTextLabel::remove_paragraph(int p_paragraph, bool p_no_invalidate) { return false; } + stack_externally_modified = true; + if (main->lines.size() == 1) { // Clear all. main->_clear_children(); @@ -4018,6 +4023,8 @@ void RichTextLabel::clear() { set_process_internal(false); MutexLock data_lock(data_mutex); + stack_externally_modified = false; + main->_clear_children(); current = main; current_frame = main; @@ -5820,11 +5827,19 @@ void RichTextLabel::set_text(const String &p_bbcode) { return; } + stack_externally_modified = false; + text = p_bbcode; _apply_translation(); } void RichTextLabel::_apply_translation() { + if (text.is_empty()) { + return; + } + + internal_stack_editing = true; + String xl_text = atr(text); if (use_bbcode) { parse_bbcode(xl_text); @@ -5832,6 +5847,8 @@ void RichTextLabel::_apply_translation() { clear(); add_text(xl_text); } + + internal_stack_editing = false; } String RichTextLabel::get_text() const { @@ -5845,8 +5862,7 @@ void RichTextLabel::set_use_bbcode(bool p_enable) { use_bbcode = p_enable; notify_property_list_changed(); - // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be. - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } } @@ -5856,7 +5872,7 @@ bool RichTextLabel::is_using_bbcode() const { } String RichTextLabel::get_parsed_text() const { - String txt = ""; + String txt; Item *it = main; while (it) { if (it->type == ITEM_DROPCAP) { @@ -5883,7 +5899,7 @@ void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) if (text_direction != p_text_direction) { text_direction = p_text_direction; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -5903,7 +5919,7 @@ void RichTextLabel::set_horizontal_alignment(HorizontalAlignment p_alignment) { if (default_alignment != p_alignment) { default_alignment = p_alignment; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -5922,7 +5938,7 @@ void RichTextLabel::set_justification_flags(BitField<TextServer::JustificationFl if (default_jst_flags != p_flags) { default_jst_flags = p_flags; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -5941,7 +5957,7 @@ void RichTextLabel::set_tab_stops(const PackedFloat32Array &p_tab_stops) { if (default_tab_stops != p_tab_stops) { default_tab_stops = p_tab_stops; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -5960,7 +5976,7 @@ void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredText _stop_thread(); st_parser = p_parser; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -5994,7 +6010,7 @@ void RichTextLabel::set_language(const String &p_language) { _stop_thread(); language = p_language; - if (!text.is_empty()) { + if (!stack_externally_modified) { _apply_translation(); } else { main->first_invalid_line.store(0); // Invalidate all lines. @@ -6052,7 +6068,7 @@ float RichTextLabel::get_visible_ratio() const { void RichTextLabel::set_effects(Array p_effects) { custom_effects = p_effects; - if ((!text.is_empty()) && use_bbcode) { + if (!stack_externally_modified && use_bbcode) { parse_bbcode(atr(text)); } } @@ -6067,7 +6083,7 @@ void RichTextLabel::install_effect(const Variant effect) { ERR_FAIL_COND_MSG(rteffect.is_null(), "Invalid RichTextEffect resource."); custom_effects.push_back(effect); - if ((!text.is_empty()) && use_bbcode) { + if (!stack_externally_modified && use_bbcode) { parse_bbcode(atr(text)); } } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 9730954482..0ea5c693ad 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -628,6 +628,9 @@ private: String text; void _apply_translation(); + bool internal_stack_editing = false; + bool stack_externally_modified = false; + bool fit_content = false; struct ThemeCache { diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 5cfa6b4bfe..9acf783c32 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -248,6 +248,14 @@ bool SubViewportContainer::_is_propagated_in_gui_input(const Ref<InputEvent> &p_ return false; } +void SubViewportContainer::set_consume_drag_and_drop(bool p_enable) { + consume_drag_and_drop = p_enable; +} + +bool SubViewportContainer::is_consume_drag_and_drop_enabled() { + return consume_drag_and_drop; +} + void SubViewportContainer::add_child_notify(Node *p_child) { if (Object::cast_to<SubViewport>(p_child)) { queue_redraw(); @@ -288,8 +296,12 @@ void SubViewportContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stretch_shrink", "amount"), &SubViewportContainer::set_stretch_shrink); ClassDB::bind_method(D_METHOD("get_stretch_shrink"), &SubViewportContainer::get_stretch_shrink); + ClassDB::bind_method(D_METHOD("set_consume_drag_and_drop", "amount"), &SubViewportContainer::set_consume_drag_and_drop); + ClassDB::bind_method(D_METHOD("is_consume_drag_and_drop_enabled"), &SubViewportContainer::is_consume_drag_and_drop_enabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), "set_stretch_shrink", "get_stretch_shrink"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "consume_drag_and_drop"), "set_consume_drag_and_drop", "is_consume_drag_and_drop_enabled"); GDVIRTUAL_BIND(_propagate_input_event, "event"); } diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index dccbec11ab..fba129b1b7 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -40,6 +40,8 @@ class SubViewportContainer : public Container { bool stretch = false; int shrink = 1; + bool consume_drag_and_drop = false; + void _notify_viewports(int p_notification); bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event); void _send_event_to_viewports(const Ref<InputEvent> &p_event); @@ -65,6 +67,9 @@ public: int get_stretch_shrink() const; void recalc_force_viewport_sizes(); + void set_consume_drag_and_drop(bool p_enable); + bool is_consume_drag_and_drop_enabled(); + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index dabd1dbe07..f66d39dbad 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3067,6 +3067,14 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { } v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos)); } + + Viewport *section_root = get_section_root_viewport(); + if (section_root && c->is_consume_drag_and_drop_enabled()) { + // Evaluating `consume_drag_and_drop` and adjusting target_control needs to happen + // after `_update_mouse_over` in the SubViewports, because otherwise physics picking + // would not work inside SubViewports. + section_root->gui.target_control = over; + } } } diff --git a/scene/resources/3d/primitive_meshes.cpp b/scene/resources/3d/primitive_meshes.cpp index 3ddacc16f6..56951170dd 100644 --- a/scene/resources/3d/primitive_meshes.cpp +++ b/scene/resources/3d/primitive_meshes.cpp @@ -33,6 +33,7 @@ #include "primitive_meshes.h" #include "core/config/project_settings.h" +#include "core/math/math_funcs.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" #include "servers/rendering_server.h" @@ -263,6 +264,9 @@ void PrimitiveMesh::_bind_methods() { } void PrimitiveMesh::set_material(const Ref<Material> &p_material) { + if (p_material == material) { + return; + } material = p_material; if (!pending_request) { // just apply it, else it'll happen when _update is called. @@ -281,6 +285,9 @@ Array PrimitiveMesh::get_mesh_arrays() const { } void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) { + if (p_custom.is_equal_approx(custom_aabb)) { + return; + } custom_aabb = p_custom; RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb); emit_changed(); @@ -291,6 +298,9 @@ AABB PrimitiveMesh::get_custom_aabb() const { } void PrimitiveMesh::set_flip_faces(bool p_enable) { + if (p_enable == flip_faces) { + return; + } flip_faces = p_enable; request_update(); } @@ -300,12 +310,18 @@ bool PrimitiveMesh::get_flip_faces() const { } void PrimitiveMesh::set_add_uv2(bool p_enable) { + if (p_enable == add_uv2) { + return; + } add_uv2 = p_enable; _update_lightmap_size(); request_update(); } void PrimitiveMesh::set_uv2_padding(float p_padding) { + if (Math::is_equal_approx(p_padding, uv2_padding)) { + return; + } uv2_padding = p_padding; _update_lightmap_size(); request_update(); @@ -580,6 +596,10 @@ void CapsuleMesh::_bind_methods() { } void CapsuleMesh::set_radius(const float p_radius) { + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -593,6 +613,10 @@ float CapsuleMesh::get_radius() const { } void CapsuleMesh::set_height(const float p_height) { + if (Math::is_equal_approx(height, p_height)) { + return; + } + height = p_height; if (radius > height * 0.5) { radius = height * 0.5; @@ -606,6 +630,10 @@ float CapsuleMesh::get_height() const { } void CapsuleMesh::set_radial_segments(const int p_segments) { + if (radial_segments == p_segments) { + return; + } + radial_segments = p_segments > 4 ? p_segments : 4; request_update(); } @@ -615,6 +643,10 @@ int CapsuleMesh::get_radial_segments() const { } void CapsuleMesh::set_rings(const int p_rings) { + if (rings == p_rings) { + return; + } + ERR_FAIL_COND(p_rings < 0); rings = p_rings; request_update(); @@ -910,6 +942,10 @@ void BoxMesh::_bind_methods() { } void BoxMesh::set_size(const Vector3 &p_size) { + if (p_size.is_equal_approx(size)) { + return; + } + size = p_size; _update_lightmap_size(); request_update(); @@ -920,6 +956,10 @@ Vector3 BoxMesh::get_size() const { } void BoxMesh::set_subdivide_width(const int p_divisions) { + if (p_divisions == subdivide_w) { + return; + } + subdivide_w = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -929,6 +969,10 @@ int BoxMesh::get_subdivide_width() const { } void BoxMesh::set_subdivide_height(const int p_divisions) { + if (p_divisions == subdivide_h) { + return; + } + subdivide_h = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -938,6 +982,10 @@ int BoxMesh::get_subdivide_height() const { } void BoxMesh::set_subdivide_depth(const int p_divisions) { + if (p_divisions == subdivide_d) { + return; + } + subdivide_d = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1185,6 +1233,10 @@ void CylinderMesh::_bind_methods() { } void CylinderMesh::set_top_radius(const float p_radius) { + if (Math::is_equal_approx(p_radius, top_radius)) { + return; + } + top_radius = p_radius; _update_lightmap_size(); request_update(); @@ -1195,6 +1247,10 @@ float CylinderMesh::get_top_radius() const { } void CylinderMesh::set_bottom_radius(const float p_radius) { + if (Math::is_equal_approx(p_radius, bottom_radius)) { + return; + } + bottom_radius = p_radius; _update_lightmap_size(); request_update(); @@ -1205,6 +1261,10 @@ float CylinderMesh::get_bottom_radius() const { } void CylinderMesh::set_height(const float p_height) { + if (Math::is_equal_approx(p_height, height)) { + return; + } + height = p_height; _update_lightmap_size(); request_update(); @@ -1215,6 +1275,10 @@ float CylinderMesh::get_height() const { } void CylinderMesh::set_radial_segments(const int p_segments) { + if (p_segments == radial_segments) { + return; + } + radial_segments = p_segments > 4 ? p_segments : 4; request_update(); } @@ -1224,6 +1288,10 @@ int CylinderMesh::get_radial_segments() const { } void CylinderMesh::set_rings(const int p_rings) { + if (p_rings == rings) { + return; + } + ERR_FAIL_COND(p_rings < 0); rings = p_rings; request_update(); @@ -1234,6 +1302,10 @@ int CylinderMesh::get_rings() const { } void CylinderMesh::set_cap_top(bool p_cap_top) { + if (p_cap_top == cap_top) { + return; + } + cap_top = p_cap_top; request_update(); } @@ -1243,6 +1315,10 @@ bool CylinderMesh::is_cap_top() const { } void CylinderMesh::set_cap_bottom(bool p_cap_bottom) { + if (p_cap_bottom == cap_bottom) { + return; + } + cap_bottom = p_cap_bottom; request_update(); } @@ -1377,6 +1453,9 @@ void PlaneMesh::_bind_methods() { } void PlaneMesh::set_size(const Size2 &p_size) { + if (p_size == size) { + return; + } size = p_size; _update_lightmap_size(); request_update(); @@ -1387,6 +1466,9 @@ Size2 PlaneMesh::get_size() const { } void PlaneMesh::set_subdivide_width(const int p_divisions) { + if (p_divisions == subdivide_w || (subdivide_w == 0 && p_divisions < 0)) { + return; + } subdivide_w = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1396,6 +1478,9 @@ int PlaneMesh::get_subdivide_width() const { } void PlaneMesh::set_subdivide_depth(const int p_divisions) { + if (p_divisions == subdivide_d || (subdivide_d == 0 && p_divisions < 0)) { + return; + } subdivide_d = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1405,6 +1490,9 @@ int PlaneMesh::get_subdivide_depth() const { } void PlaneMesh::set_center_offset(const Vector3 p_offset) { + if (p_offset.is_equal_approx(center_offset)) { + return; + } center_offset = p_offset; request_update(); } @@ -1414,6 +1502,9 @@ Vector3 PlaneMesh::get_center_offset() const { } void PlaneMesh::set_orientation(const Orientation p_orientation) { + if (p_orientation == orientation) { + return; + } orientation = p_orientation; request_update(); } @@ -1721,6 +1812,9 @@ void PrismMesh::_bind_methods() { } void PrismMesh::set_left_to_right(const float p_left_to_right) { + if (Math::is_equal_approx(p_left_to_right, left_to_right)) { + return; + } left_to_right = p_left_to_right; request_update(); } @@ -1730,6 +1824,9 @@ float PrismMesh::get_left_to_right() const { } void PrismMesh::set_size(const Vector3 &p_size) { + if (p_size.is_equal_approx(size)) { + return; + } size = p_size; _update_lightmap_size(); request_update(); @@ -1740,6 +1837,9 @@ Vector3 PrismMesh::get_size() const { } void PrismMesh::set_subdivide_width(const int p_divisions) { + if (p_divisions == subdivide_w || (p_divisions < 0 && subdivide_w == 0)) { + return; + } subdivide_w = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1749,6 +1849,9 @@ int PrismMesh::get_subdivide_width() const { } void PrismMesh::set_subdivide_height(const int p_divisions) { + if (p_divisions == subdivide_h || (p_divisions < 0 && subdivide_h == 0)) { + return; + } subdivide_h = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1758,6 +1861,9 @@ int PrismMesh::get_subdivide_height() const { } void PrismMesh::set_subdivide_depth(const int p_divisions) { + if (p_divisions == subdivide_d || (p_divisions < 0 && subdivide_d == 0)) { + return; + } subdivide_d = p_divisions > 0 ? p_divisions : 0; request_update(); } @@ -1904,6 +2010,9 @@ void SphereMesh::_bind_methods() { } void SphereMesh::set_radius(const float p_radius) { + if (Math::is_equal_approx(p_radius, radius)) { + return; + } radius = p_radius; _update_lightmap_size(); request_update(); @@ -1914,6 +2023,9 @@ float SphereMesh::get_radius() const { } void SphereMesh::set_height(const float p_height) { + if (Math::is_equal_approx(height, p_height)) { + return; + } height = p_height; _update_lightmap_size(); request_update(); @@ -1924,6 +2036,9 @@ float SphereMesh::get_height() const { } void SphereMesh::set_radial_segments(const int p_radial_segments) { + if (p_radial_segments == radial_segments || (radial_segments == 4 && p_radial_segments < 4)) { + return; + } radial_segments = p_radial_segments > 4 ? p_radial_segments : 4; request_update(); } @@ -1933,6 +2048,9 @@ int SphereMesh::get_radial_segments() const { } void SphereMesh::set_rings(const int p_rings) { + if (p_rings == rings) { + return; + } ERR_FAIL_COND(p_rings < 1); rings = p_rings; request_update(); @@ -1943,6 +2061,9 @@ int SphereMesh::get_rings() const { } void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) { + if (p_is_hemisphere == is_hemisphere) { + return; + } is_hemisphere = p_is_hemisphere; _update_lightmap_size(); request_update(); @@ -2088,6 +2209,9 @@ void TorusMesh::_bind_methods() { } void TorusMesh::set_inner_radius(const float p_inner_radius) { + if (Math::is_equal_approx(p_inner_radius, inner_radius)) { + return; + } inner_radius = p_inner_radius; request_update(); } @@ -2097,6 +2221,9 @@ float TorusMesh::get_inner_radius() const { } void TorusMesh::set_outer_radius(const float p_outer_radius) { + if (Math::is_equal_approx(p_outer_radius, outer_radius)) { + return; + } outer_radius = p_outer_radius; request_update(); } @@ -2106,6 +2233,9 @@ float TorusMesh::get_outer_radius() const { } void TorusMesh::set_rings(const int p_rings) { + if (p_rings == rings) { + return; + } ERR_FAIL_COND(p_rings < 3); rings = p_rings; request_update(); @@ -2116,6 +2246,9 @@ int TorusMesh::get_rings() const { } void TorusMesh::set_ring_segments(const int p_ring_segments) { + if (p_ring_segments == ring_segments) { + return; + } ERR_FAIL_COND(p_ring_segments < 3); ring_segments = p_ring_segments; request_update(); @@ -2145,6 +2278,9 @@ PointMesh::PointMesh() { // TUBE TRAIL void TubeTrailMesh::set_radius(const float p_radius) { + if (Math::is_equal_approx(p_radius, radius)) { + return; + } radius = p_radius; request_update(); } @@ -2153,6 +2289,9 @@ float TubeTrailMesh::get_radius() const { } void TubeTrailMesh::set_radial_steps(const int p_radial_steps) { + if (p_radial_steps == radial_steps) { + return; + } ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128); radial_steps = p_radial_steps; request_update(); @@ -2162,6 +2301,9 @@ int TubeTrailMesh::get_radial_steps() const { } void TubeTrailMesh::set_sections(const int p_sections) { + if (p_sections == sections) { + return; + } ERR_FAIL_COND(p_sections < 2 || p_sections > 128); sections = p_sections; request_update(); @@ -2171,6 +2313,9 @@ int TubeTrailMesh::get_sections() const { } void TubeTrailMesh::set_section_length(float p_section_length) { + if (p_section_length == section_length) { + return; + } section_length = p_section_length; request_update(); } @@ -2179,6 +2324,9 @@ float TubeTrailMesh::get_section_length() const { } void TubeTrailMesh::set_section_rings(const int p_section_rings) { + if (p_section_rings == section_rings) { + return; + } ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024); section_rings = p_section_rings; request_update(); @@ -2188,6 +2336,9 @@ int TubeTrailMesh::get_section_rings() const { } void TubeTrailMesh::set_cap_top(bool p_cap_top) { + if (p_cap_top == cap_top) { + return; + } cap_top = p_cap_top; request_update(); } @@ -2197,6 +2348,9 @@ bool TubeTrailMesh::is_cap_top() const { } void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) { + if (p_cap_bottom == cap_bottom) { + return; + } cap_bottom = p_cap_bottom; request_update(); } @@ -2503,6 +2657,9 @@ TubeTrailMesh::TubeTrailMesh() { // RIBBON TRAIL void RibbonTrailMesh::set_shape(Shape p_shape) { + if (p_shape == shape) { + return; + } shape = p_shape; request_update(); } @@ -2511,6 +2668,9 @@ RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const { } void RibbonTrailMesh::set_size(const float p_size) { + if (Math::is_equal_approx(p_size, size)) { + return; + } size = p_size; request_update(); } @@ -2519,6 +2679,9 @@ float RibbonTrailMesh::get_size() const { } void RibbonTrailMesh::set_sections(const int p_sections) { + if (p_sections == sections) { + return; + } ERR_FAIL_COND(p_sections < 2 || p_sections > 128); sections = p_sections; request_update(); @@ -2528,6 +2691,9 @@ int RibbonTrailMesh::get_sections() const { } void RibbonTrailMesh::set_section_length(float p_section_length) { + if (p_section_length == section_length) { + return; + } section_length = p_section_length; request_update(); } @@ -2536,6 +2702,9 @@ float RibbonTrailMesh::get_section_length() const { } void RibbonTrailMesh::set_section_segments(const int p_section_segments) { + if (p_section_segments == section_segments) { + return; + } ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024); section_segments = p_section_segments; request_update(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 1bd0c0058c..13858181a6 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -3169,6 +3169,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index e3eb96cde0..66445555f3 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3245,6 +3245,29 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade code += " " + p_output_vars[0] + " = vec3(r, g, b);\n"; code += " }\n"; break; + case FUNC_LINEAR_TO_SRGB: + code += " {\n"; + if (RenderingServer::get_singleton()->is_low_end()) { + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " " + p_output_vars[0] + " = max(vec3(1.055) * pow(c, vec3(0.416666667)) - vec3(0.055), vec3(0.0));\n"; + } else { + code += " vec3 c = clamp(" + p_input_vars[0] + ", vec3(0.0), vec3(1.0));\n"; + code += " const vec3 a = vec3(0.055f);\n"; + code += " " + p_output_vars[0] + " = mix((vec3(1.0f) + a) * pow(c.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * c.rgb, lessThan(c.rgb, vec3(0.0031308f)));\n"; + } + code += " }\n"; + break; + case FUNC_SRGB_TO_LINEAR: + code += " {\n"; + if (RenderingServer::get_singleton()->is_low_end()) { + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " " + p_output_vars[0] + " = c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878);\n"; + } else { + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " " + p_output_vars[0] + " = mix(pow((c.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), c.rgb * (1.0 / 12.92), lessThan(c.rgb, vec3(0.04045)));\n"; + } + code += " }\n"; + break; default: break; } @@ -3275,12 +3298,14 @@ void VisualShaderNodeColorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia,LinearToSRGB,SRGBToLinear"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_GRAYSCALE); BIND_ENUM_CONSTANT(FUNC_HSV2RGB); BIND_ENUM_CONSTANT(FUNC_RGB2HSV); BIND_ENUM_CONSTANT(FUNC_SEPIA); + BIND_ENUM_CONSTANT(FUNC_LINEAR_TO_SRGB); + BIND_ENUM_CONSTANT(FUNC_SRGB_TO_LINEAR); BIND_ENUM_CONSTANT(FUNC_MAX); } diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 8f5a7b8e4b..2ebb153ec8 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1355,6 +1355,8 @@ public: FUNC_HSV2RGB, FUNC_RGB2HSV, FUNC_SEPIA, + FUNC_LINEAR_TO_SRGB, + FUNC_SRGB_TO_LINEAR, FUNC_MAX, }; |