diff options
Diffstat (limited to 'scene')
58 files changed, 967 insertions, 729 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 5eb76bdbb5..f80da6e9ab 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -175,6 +175,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i return; //does not exist because it was likely removed from the tree } + lock_callback(); locked = true; if (body_in) { @@ -224,6 +225,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i } locked = false; + unlock_callback(); } void Area2D::_area_enter_tree(ObjectID p_id) { @@ -268,6 +270,8 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i if (!area_in && !E) { return; //likely removed from the tree } + + lock_callback(); locked = true; if (area_in) { @@ -317,6 +321,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i } locked = false; + unlock_callback(); } void Area2D::_clear_monitoring() { diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index b2fee6ad82..caea753d99 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -94,10 +94,14 @@ void CollisionObject2D::_notification(int p_what) { bool disabled = !is_enabled(); if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { - if (area) { - PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + if (callback_lock > 0) { + ERR_PRINT("Removing a CollisionObject node during a physics callback is not allowed and will cause undesired behavior. Remove with call_deferred() instead."); } else { - PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + } } } @@ -225,10 +229,14 @@ void CollisionObject2D::_apply_disabled() { switch (disable_mode) { case DISABLE_MODE_REMOVE: { if (is_inside_tree()) { - if (area) { - PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + if (callback_lock > 0) { + ERR_PRINT("Disabling a CollisionObject node during a physics callback is not allowed and will cause undesired behavior. Disable with call_deferred() instead."); } else { - PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + if (area) { + PhysicsServer2D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer2D::get_singleton()->body_set_space(rid, RID()); + } } } } break; diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index d44e402e96..88429b145d 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -53,6 +53,7 @@ private: bool area = false; RID rid; + uint32_t callback_lock = 0; bool pickable = false; DisableMode disable_mode = DISABLE_MODE_REMOVE; @@ -83,6 +84,12 @@ private: void _apply_enabled(); protected: + _FORCE_INLINE_ void lock_callback() { callback_lock++; } + _FORCE_INLINE_ void unlock_callback() { + ERR_FAIL_COND(callback_lock == 0); + callback_lock--; + } + CollisionObject2D(RID p_rid, bool p_area); void _notification(int p_what); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 9fee99c6a7..1721bcde3b 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -434,6 +434,8 @@ struct _RigidBody2DInOut { }; void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { + lock_callback(); + set_block_transform_notify(true); // don't want notify (would feedback loop) if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); @@ -527,6 +529,8 @@ void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { contact_monitor->locked = false; } + + unlock_callback(); } void RigidBody2D::_apply_body_mode() { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 4f282dc0ab..05d28e8a0e 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -803,10 +803,10 @@ void TileMap::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator void TileMap::_make_all_quadrants_dirty() { // Make all quandrants dirty, then trigger an update later. - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { if (!E.value.dirty_list_element.in_list()) { - layers[layer].dirty_quadrant_list.add(&E.value.dirty_list_element); + layer.dirty_quadrant_list.add(&E.value.dirty_list_element); } } } @@ -1014,8 +1014,8 @@ void TileMap::_rendering_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_CANVAS: { bool node_visible = is_visible_in_tree(); - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { TileMapQuadrant &q = E_quadrant.value; for (const KeyValue<Vector2i, RID> &kv : q.occluders) { Transform2D xform; @@ -1030,8 +1030,8 @@ void TileMap::_rendering_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { bool node_visible = is_visible_in_tree(); - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. @@ -1050,8 +1050,8 @@ void TileMap::_rendering_notification(int p_what) { if (!is_inside_tree()) { return; } - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. @@ -1071,8 +1071,8 @@ void TileMap::_rendering_notification(int p_what) { } break; case NOTIFICATION_EXIT_CANVAS: { - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { TileMapQuadrant &q = E_quadrant.value; for (const KeyValue<Vector2i, RID> &kv : q.occluders) { RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, RID()); @@ -1257,16 +1257,16 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (_rendering_quadrant_order_dirty) { int index = -(int64_t)0x80000000; //always must be drawn below children. - for (int layer = 0; layer < (int)layers.size(); layer++) { + for (TileMapLayer &layer : layers) { // Sort the quadrants coords per local coordinates. RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map; - for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { local_to_map[map_to_local(E.key)] = E.key; } // Sort the quadrants. for (const KeyValue<Vector2i, Vector2i> &E : local_to_map) { - TileMapQuadrant &q = layers[layer].quadrant_map[E.value]; + TileMapQuadrant &q = layer.quadrant_map[E.value]; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_draw_index(ci, index++); } @@ -1453,8 +1453,8 @@ void TileMap::_physics_notification(int p_what) { if (is_inside_tree() && (!collision_animatable || in_editor)) { // Update the new transform directly if we are not in animatable mode. Transform2D gl_transform = get_global_transform(); - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { TileMapQuadrant &q = E.value; for (RID body : q.bodies) { @@ -1476,8 +1476,8 @@ void TileMap::_physics_notification(int p_what) { if (is_inside_tree() && !in_editor && collision_animatable) { // Only active when animatable. Send the new transform to the physics... new_transform = get_global_transform(); - for (int layer = 0; layer < (int)layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (TileMapLayer &layer : layers) { + for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { TileMapQuadrant &q = E.value; for (RID body : q.bodies) { @@ -1667,13 +1667,12 @@ void TileMap::_navigation_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSFORM_CHANGED: { if (is_inside_tree()) { - for (int layer = 0; layer < (int)layers.size(); layer++) { + for (TileMapLayer &layer : layers) { Transform2D tilemap_xform = get_global_transform(); - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { TileMapQuadrant &q = E_quadrant.value; for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) { - for (int layer_index = 0; layer_index < E_region.value.size(); layer_index++) { - RID region = E_region.value[layer_index]; + for (const RID ®ion : E_region.value) { if (!region.is_valid()) { continue; } @@ -2073,7 +2072,7 @@ void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) { int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE); - // Get a cell source id from position + // Get a cell source id from position. const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); @@ -2129,7 +2128,9 @@ int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bo TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { int source_id = get_cell_source_id(p_layer, p_coords, p_use_proxies); - ERR_FAIL_COND_V_MSG(source_id == TileSet::INVALID_SOURCE, nullptr, vformat("Invalid TileSetSource at cell %s. Make sure a tile exists at this cell.", p_coords)); + if (source_id == TileSet::INVALID_SOURCE) { + return nullptr; + } Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); if (source.is_valid()) { @@ -2813,8 +2814,8 @@ void TileMap::clear_layer(int p_layer) { void TileMap::clear() { // Remove all tiles. _clear_internals(); - for (unsigned int i = 0; i < layers.size(); i++) { - layers[i].tile_map.clear(); + for (TileMapLayer &layer : layers) { + layer.tile_map.clear(); } _recreate_internals(); used_rect_cache_dirty = true; @@ -3954,15 +3955,15 @@ PackedStringArray TileMap::get_configuration_warnings() const { // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; - for (int layer = 0; layer < (int)layers.size(); layer++) { - if (layers[layer].y_sort_enabled) { - y_sorted_z_index.insert(layers[layer].z_index); + for (const TileMapLayer &layer : layers) { + if (layer.y_sort_enabled) { + y_sorted_z_index.insert(layer.z_index); } } // Check if we have a non-sorted layer in a Z-index with a Y-sorted layer. - for (int layer = 0; layer < (int)layers.size(); layer++) { - if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) { + for (const TileMapLayer &layer : layers) { + if (!layer.y_sort_enabled && y_sorted_z_index.has(layer.z_index)) { warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers.")); break; } @@ -3971,8 +3972,8 @@ PackedStringArray TileMap::get_configuration_warnings() const { if (tile_set.is_valid() && tile_set->get_tile_shape() == TileSet::TILE_SHAPE_ISOMETRIC) { bool warn = !is_y_sort_enabled(); if (!warn) { - for (int layer = 0; layer < (int)layers.size(); layer++) { - if (!layers[layer].y_sort_enabled) { + for (const TileMapLayer &layer : layers) { + if (!layer.y_sort_enabled) { warn = true; break; } diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index fa61ce5546..72f186c676 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -230,6 +230,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i return; //likely removed from the tree } + lock_callback(); locked = true; if (body_in) { @@ -279,6 +280,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i } locked = false; + unlock_callback(); } void Area3D::_clear_monitoring() { @@ -417,6 +419,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i return; //likely removed from the tree } + lock_callback(); locked = true; if (area_in) { @@ -466,6 +469,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i } locked = false; + unlock_callback(); } bool Area3D::is_monitoring() const { diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index c408b0714a..26ada1da5a 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -100,10 +100,14 @@ void CollisionObject3D::_notification(int p_what) { bool disabled = !is_enabled(); if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) { - if (area) { - PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + if (callback_lock > 0) { + ERR_PRINT("Removing a CollisionObject node during a physics callback is not allowed and will cause undesired behavior. Remove with call_deferred() instead."); } else { - PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + } } } @@ -223,10 +227,14 @@ void CollisionObject3D::_apply_disabled() { switch (disable_mode) { case DISABLE_MODE_REMOVE: { if (is_inside_tree()) { - if (area) { - PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + if (callback_lock > 0) { + ERR_PRINT("Disabling a CollisionObject node during a physics callback is not allowed and will cause undesired behavior. Disable with call_deferred() instead."); } else { - PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + if (area) { + PhysicsServer3D::get_singleton()->area_set_space(rid, RID()); + } else { + PhysicsServer3D::get_singleton()->body_set_space(rid, RID()); + } } } } break; diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 656b8c9bf1..ebcbb39e0d 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -52,6 +52,7 @@ private: bool area = false; RID rid; + uint32_t callback_lock = 0; DisableMode disable_mode = DISABLE_MODE_REMOVE; @@ -97,6 +98,12 @@ private: protected: CollisionObject3D(RID p_rid, bool p_area); + _FORCE_INLINE_ void lock_callback() { callback_lock++; } + _FORCE_INLINE_ void unlock_callback() { + ERR_FAIL_COND(callback_lock == 0); + callback_lock--; + } + void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index e9cc4e9479..fbcb1c8f2c 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -156,6 +156,10 @@ void Decal::_validate_property(PropertyInfo &p_property) const { if (!distance_fade_enabled && (p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_length")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } + + if (p_property.name == "sorting_offset") { + p_property.usage = PROPERTY_USAGE_DEFAULT; + } } PackedStringArray Decal::get_configuration_warnings() const { diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index f8c54809da..d0f71768d2 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -442,7 +442,7 @@ void Label3D::_shape() { TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } - TypedArray<Vector2i> stt; + TypedArray<Vector3i> stt; if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt); } else { diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 8fc772e4b0..96cc941209 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -143,7 +143,7 @@ private: 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) + GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String) void _notification(int p_what); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 6940bad4a6..f308ad0999 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -455,8 +455,8 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc Plane best_plane; float best_plane_score = -1.0; - for (uint32_t i = 0; i < p_simplex_indices.size(); i++) { - const BSPSimplex &s = p_simplices[p_simplex_indices[i]]; + for (const int idx : p_simplex_indices) { + const BSPSimplex &s = p_simplices[idx]; for (int j = 0; j < 4; j++) { uint32_t plane_index = s.planes[j]; if (planes_tested[plane_index] == node_index) { @@ -484,8 +484,8 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc int over_count = 0; int under_count = 0; - for (uint32_t k = 0; k < p_simplex_indices.size(); k++) { - int side = _bsp_get_simplex_side(p_points, p_simplices, plane, p_simplex_indices[k]); + for (const int &index : p_simplex_indices) { + int side = _bsp_get_simplex_side(p_points, p_simplices, plane, index); if (side == -2) { continue; //this simplex is invalid, skip for now } else if (side < 0) { @@ -523,8 +523,7 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc LocalVector<int32_t> indices_under; //split again, but add to list - for (uint32_t i = 0; i < p_simplex_indices.size(); i++) { - uint32_t index = p_simplex_indices[i]; + for (const uint32_t index : p_simplex_indices) { int side = _bsp_get_simplex_side(p_points, p_simplices, best_plane, index); if (side == -2) { @@ -977,8 +976,8 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } } - for (uint32_t i = 0; i < new_probe_positions.size(); i++) { - probes_found.push_back(new_probe_positions[i]); + for (const Vector3 &position : new_probe_positions) { + probes_found.push_back(position); } } @@ -1219,8 +1218,8 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa LocalVector<BSPNode> bsp_nodes; LocalVector<int32_t> planes_tested; planes_tested.resize(bsp_planes.size()); - for (uint32_t i = 0; i < planes_tested.size(); i++) { - planes_tested[i] = 0x7FFFFFFF; + for (int &index : planes_tested) { + index = 0x7FFFFFFF; } if (p_bake_step) { diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 46956b0a2e..106efbc596 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -484,6 +484,8 @@ struct _RigidBodyInOut { }; void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + lock_callback(); + set_ignore_transform_notification(true); set_global_transform(p_state->get_transform()); @@ -578,6 +580,8 @@ void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { contact_monitor->locked = false; } + + unlock_callback(); } void RigidBody3D::_notification(int p_what) { diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 64fb0a7657..8026b12c2b 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -120,6 +120,12 @@ bool VisualInstance3D::is_sorting_use_aabb_center() const { return sorting_use_aabb_center; } +void VisualInstance3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance3D::set_base); ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance3D::get_base); @@ -437,6 +443,12 @@ PackedStringArray GeometryInstance3D::get_configuration_warnings() const { return warnings; } +void GeometryInstance3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "sorting_offset" || p_property.name == "sorting_use_aabb_center") { + p_property.usage = PROPERTY_USAGE_DEFAULT; + } +} + void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override); ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 190ed17753..ef0f7966e2 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -47,6 +47,7 @@ protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; GDVIRTUAL0RC(AABB, _get_aabb) public: @@ -140,6 +141,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 1ef0774828..a00b1d8ee1 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -229,15 +229,17 @@ AnimationNodeSync::AnimationNodeSync() { //////////////////////////////////////////////////////// void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::BOOL, active)); - r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); + r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort")); r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { - if (p_parameter == active || p_parameter == prev_active) { + if (p_parameter == request) { + return ONE_SHOT_REQUEST_NONE; + } else if (p_parameter == active) { return false; } else if (p_parameter == time_to_restart) { return -1; @@ -246,6 +248,13 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa } } +bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const { + if (p_parameter == active) { + return true; + } + return false; +} + void AnimationNodeOneShot::set_fadein_time(double p_time) { fade_in = p_time; } @@ -303,41 +312,42 @@ bool AnimationNodeOneShot::has_filter() const { } double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_external_seeking) { + OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request)); bool cur_active = get_parameter(active); - bool cur_prev_active = get_parameter(prev_active); double cur_time = get_parameter(time); double cur_remaining = get_parameter(remaining); double cur_time_to_restart = get_parameter(time_to_restart); - if (!cur_active) { - //make it as if this node doesn't exist, pass input 0 by. - if (cur_prev_active) { - set_parameter(prev_active, false); - } + set_parameter(request, ONE_SHOT_REQUEST_NONE); + + bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE; + if (cur_request == ONE_SHOT_REQUEST_ABORT) { + set_parameter(active, false); + set_parameter(time_to_restart, -1); + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + } else if (!do_start && !cur_active) { if (cur_time_to_restart >= 0.0 && !p_seek) { cur_time_to_restart -= p_time; if (cur_time_to_restart < 0) { - //restart - set_parameter(active, true); - cur_active = true; + do_start = true; // Restart. } set_parameter(time_to_restart, cur_time_to_restart); } - - return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + if (!do_start) { + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + } } bool os_seek = p_seek; - if (p_seek) { cur_time = p_time; } - bool do_start = !cur_prev_active; if (do_start) { cur_time = 0; os_seek = true; - set_parameter(prev_active, true); + set_parameter(request, ONE_SHOT_REQUEST_NONE); + set_parameter(active, true); } real_t blend; @@ -375,7 +385,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter cur_remaining = os_rem; if (cur_remaining <= 0) { set_parameter(active, false); - set_parameter(prev_active, false); if (autorestart) { double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay; set_parameter(time_to_restart, restart_sec); @@ -419,6 +428,10 @@ void AnimationNodeOneShot::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay"); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT); + BIND_ENUM_CONSTANT(MIX_MODE_BLEND); BIND_ENUM_CONSTANT(MIX_MODE_ADD); } @@ -640,9 +653,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con anims += inputs[i].name; } - r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); - r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); - r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::STRING, current_state, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); // For interface. + r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately. + r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally. + r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } @@ -650,13 +664,22 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { if (p_parameter == time || p_parameter == prev_xfading) { return 0.0; - } else if (p_parameter == prev || p_parameter == prev_current) { + } else if (p_parameter == prev_index) { return -1; + } else if (p_parameter == transition_request || p_parameter == current_state) { + return String(); } else { return 0; } } +bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const { + if (p_parameter == current_state || p_parameter == current_index) { + return true; + } + return false; +} + String AnimationNodeTransition::get_caption() const { return "Transition"; } @@ -702,6 +725,17 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } +int AnimationNodeTransition::find_input_caption(const String &p_name) const { + int idx = -1; + for (int i = 0; i < MAX_INPUTS; i++) { + if (inputs[i].name == p_name) { + idx = i; + break; + } + } + return idx; +} + void AnimationNodeTransition::set_xfade_time(double p_fade) { xfade_time = p_fade; } @@ -718,35 +752,62 @@ Ref<Curve> AnimationNodeTransition::get_xfade_curve() const { return xfade_curve; } -void AnimationNodeTransition::set_from_start(bool p_from_start) { - from_start = p_from_start; +void AnimationNodeTransition::set_reset(bool p_reset) { + reset = p_reset; } -bool AnimationNodeTransition::is_from_start() const { - return from_start; +bool AnimationNodeTransition::is_reset() const { + return reset; } double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) { - int cur_current = get_parameter(current); - int cur_prev = get_parameter(prev); - int cur_prev_current = get_parameter(prev_current); + String cur_transition_request = get_parameter(transition_request); + int cur_current_index = get_parameter(current_index); + int cur_prev_index = get_parameter(prev_index); double cur_time = get_parameter(time); double cur_prev_xfading = get_parameter(prev_xfading); - bool switched = cur_current != cur_prev_current; + bool switched = false; + bool restart = false; + + if (!cur_transition_request.is_empty()) { + int new_idx = find_input_caption(cur_transition_request); + if (new_idx >= 0) { + if (cur_current_index == new_idx) { + // Transition to same state. + restart = reset; + cur_prev_xfading = 0; + set_parameter(prev_xfading, 0); + cur_prev_index = -1; + set_parameter(prev_index, -1); + } else { + switched = true; + cur_prev_index = cur_current_index; + set_parameter(prev_index, cur_current_index); + } + cur_current_index = new_idx; + set_parameter(current_index, cur_current_index); + set_parameter(current_state, cur_transition_request); + } else { + ERR_PRINT("No such input: '" + cur_transition_request + "'"); + } + cur_transition_request = String(); + set_parameter(transition_request, cur_transition_request); + } - if (switched) { - set_parameter(prev_current, cur_current); - set_parameter(prev, cur_prev_current); + // Special case for restart. + if (restart) { + set_parameter(time, 0); + return blend_input(cur_current_index, 0, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + } - cur_prev = cur_prev_current; + if (switched) { cur_prev_xfading = xfade_time; cur_time = 0; - switched = true; } - if (cur_current < 0 || cur_current >= enabled_inputs || cur_prev >= enabled_inputs) { + if (cur_current_index < 0 || cur_current_index >= enabled_inputs || cur_prev_index >= enabled_inputs) { return 0; } @@ -754,15 +815,15 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex if (sync) { for (int i = 0; i < enabled_inputs; i++) { - if (i != cur_current && i != cur_prev) { + if (i != cur_current_index && i != cur_prev_index) { blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); } } } - if (cur_prev < 0) { // process current animation, check for transition + if (cur_prev_index < 0) { // process current animation, check for transition - rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); if (p_seek) { cur_time = p_time; @@ -770,8 +831,8 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex cur_time += p_time; } - if (inputs[cur_current].auto_advance && rem <= xfade_time) { - set_parameter(current, (cur_current + 1) % enabled_inputs); + if (inputs[cur_current_index].auto_advance && rem <= xfade_time) { + set_parameter(transition_request, get_input_caption((cur_current_index + 1) % enabled_inputs)); } } else { // cross-fading from prev to current @@ -783,21 +844,21 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex // Blend values must be more than CMP_EPSILON to process discrete keys in edge. real_t blend_inv = 1.0 - blend; - if (from_start && !p_seek && switched) { //just switched, seek to start of current - rem = blend_input(cur_current, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); + if (reset && !p_seek && switched) { //just switched, seek to start of current + rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } else { - rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); + rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } if (p_seek) { - blend_input(cur_prev, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); + blend_input(cur_prev_index, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time = p_time; } else { - blend_input(cur_prev, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); + blend_input(cur_prev_index, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time += p_time; cur_prev_xfading -= p_time; if (cur_prev_xfading < 0) { - set_parameter(prev, -1); + set_parameter(prev_index, -1); } } } @@ -829,6 +890,7 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); + ClassDB::bind_method(D_METHOD("find_input_caption", "caption"), &AnimationNodeTransition::find_input_caption); ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time); @@ -836,13 +898,13 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve); ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve); - ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start); - ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start); + ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeTransition::set_reset); + ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeTransition::is_reset); - ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,31,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset"); for (int i = 0; i < MAX_INPUTS; i++) { ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i); diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index e72471202e..a1969bb621 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -96,6 +96,12 @@ class AnimationNodeOneShot : public AnimationNodeSync { GDCLASS(AnimationNodeOneShot, AnimationNodeSync); public: + enum OneShotRequest { + ONE_SHOT_REQUEST_NONE, + ONE_SHOT_REQUEST_FIRE, + ONE_SHOT_REQUEST_ABORT, + }; + enum MixMode { MIX_MODE_BLEND, MIX_MODE_ADD @@ -110,13 +116,8 @@ private: double autorestart_random_delay = 0.0; MixMode mix = MIX_MODE_BLEND; - /* bool active; - bool do_start; - double time; - double remaining;*/ - + StringName request = PNAME("request"); StringName active = PNAME("active"); - StringName prev_active = "prev_active"; StringName time = "time"; StringName remaining = "remaining"; StringName time_to_restart = "time_to_restart"; @@ -127,6 +128,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; + virtual bool is_parameter_read_only(const StringName &p_parameter) const override; virtual String get_caption() const override; @@ -153,6 +155,7 @@ public: AnimationNodeOneShot(); }; +VARIANT_ENUM_CAST(AnimationNodeOneShot::OneShotRequest) VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNodeSync { @@ -284,22 +287,19 @@ class AnimationNodeTransition : public AnimationNodeSync { InputData inputs[MAX_INPUTS]; int enabled_inputs = 0; - /* - double prev_xfading; - int prev; - double time; - int current; - int prev_current; */ - - StringName prev_xfading = "prev_xfading"; - StringName prev = "prev"; StringName time = "time"; - StringName current = PNAME("current"); - StringName prev_current = "prev_current"; + StringName prev_xfading = "prev_xfading"; + StringName prev_index = "prev_index"; + StringName current_index = PNAME("current_index"); + StringName current_state = PNAME("current_state"); + StringName transition_request = PNAME("transition_request"); + + StringName prev_frame_current = "pf_current"; + StringName prev_frame_current_idx = "pf_current_idx"; double xfade_time = 0.0; Ref<Curve> xfade_curve; - bool from_start = true; + bool reset = true; void _update_inputs(); @@ -310,6 +310,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; + virtual bool is_parameter_read_only(const StringName &p_parameter) const override; virtual String get_caption() const override; @@ -321,6 +322,7 @@ public: void set_input_caption(int p_input, const String &p_name); String get_input_caption(int p_input) const; + int find_input_caption(const String &p_name) const; void set_xfade_time(double p_fade); double get_xfade_time() const; @@ -328,8 +330,8 @@ public: void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() const; - void set_from_start(bool p_from_start); - bool is_from_start() const; + void set_reset(bool p_reset); + bool is_reset() const; double process(double p_time, bool p_seek, bool p_is_external_seeking) override; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f5df64dbdd..02f1e9f9a6 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -107,6 +107,15 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const { return xfade_curve; } +void AnimationNodeStateMachineTransition::set_reset(bool p_reset) { + reset = p_reset; + emit_changed(); +} + +bool AnimationNodeStateMachineTransition::is_reset() const { + return reset; +} + void AnimationNodeStateMachineTransition::set_priority(int p_priority) { priority = p_priority; emit_changed(); @@ -132,6 +141,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve); ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve); + ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset); + ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset); + ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority); @@ -140,6 +152,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_GROUP("Switch", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode"); @@ -164,18 +179,27 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { //////////////////////////////////////////////////////// -void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - start_request_travel = true; - start_request = p_state; +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) { + travel_request = p_state; + reset_request_on_teleport = p_reset_on_teleport; stop_request = false; } -void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { - start_request_travel = false; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) { + travel_request = StringName(); + reset_request = p_reset; + _start(p_state); +} + +void AnimationNodeStateMachinePlayback::_start(const StringName &p_state) { start_request = p_state; stop_request = false; } +void AnimationNodeStateMachinePlayback::next() { + next_request = true; +} + void AnimationNodeStateMachinePlayback::stop() { stop_request = true; } @@ -188,7 +212,7 @@ StringName AnimationNodeStateMachinePlayback::get_current_node() const { return current; } -StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { +StringName AnimationNodeStateMachinePlayback::get_fading_from_node() const { return fading_from; } @@ -212,7 +236,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta path.clear(); //a new one will be needed if (current == p_travel) { - return true; //nothing to do + return false; // Will teleport oneself (restart). } Vector2 current_pos = p_state_machine->states[current].position; @@ -323,6 +347,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta } double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) { + double rem = _process(p_state_machine, p_time, p_seek, p_is_external_seeking); + start_request = StringName(); + next_request = false; + stop_request = false; + reset_request_on_teleport = false; + return rem; +} + +double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) { if (p_time == -1) { Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; if (anodesm.is_valid()) { @@ -335,14 +368,13 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s //if not playing and it can restart, then restart if (!playing && start_request == StringName()) { if (!stop_request && p_state_machine->start_node) { - start(p_state_machine->start_node); + _start(p_state_machine->start_node); } else { return 0; } } if (playing && stop_request) { - stop_request = false; playing = false; return 0; } @@ -350,42 +382,47 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s bool play_start = false; if (start_request != StringName()) { - if (start_request_travel) { - if (!playing) { - if (!stop_request && p_state_machine->start_node) { - // can restart, just postpone traveling - path.clear(); - current = p_state_machine->start_node; - playing = true; - play_start = true; - } else { - // stopped, invalid state - String node_name = start_request; - start_request = StringName(); //clear start request - ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?"); - } - } else { - if (!_travel(p_state_machine, start_request)) { - // can't travel, then teleport - path.clear(); - current = start_request; - play_start = true; - } - start_request = StringName(); //clear start request - } + // teleport to start + if (p_state_machine->states.has(start_request)) { + path.clear(); + current = start_request; + playing = true; + play_start = true; } else { - // teleport to start - if (p_state_machine->states.has(start_request)) { + StringName node = start_request; + ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + } + } else if (travel_request != StringName()) { + if (!playing) { + if (!stop_request && p_state_machine->start_node) { + // can restart, just postpone traveling path.clear(); - current = start_request; + current = p_state_machine->start_node; playing = true; play_start = true; - start_request = StringName(); //clear start request } else { - StringName node = start_request; - start_request = StringName(); //clear start request - ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + // stopped, invalid state + String node_name = travel_request; + travel_request = StringName(); + ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?"); } + } else { + if (!_travel(p_state_machine, travel_request)) { + // can't travel, then teleport + if (p_state_machine->states.has(travel_request)) { + path.clear(); + if (current != travel_request || reset_request_on_teleport) { + current = travel_request; + play_start = true; + reset_request = reset_request_on_teleport; + } + } else { + StringName node = travel_request; + travel_request = StringName(); + ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + } + } + travel_request = StringName(); } } @@ -396,8 +433,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = p_state_machine->start_node; } - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true); - pos_current = 0; + if (reset_request) { + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true); + pos_current = 0; + reset_request = false; + } } if (!p_state_machine->states.has(current)) { @@ -421,7 +461,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (current_curve.is_valid()) { fade_blend = current_curve->sample(fade_blend); } - double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + + double rem = do_start ? len_current : p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (fading_from != StringName()) { double fade_blend_inv = 1.0 - fade_blend; @@ -457,6 +498,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s next_xfade = p_state_machine->transitions[i].transition->get_xfade_time(); current_curve = p_state_machine->transitions[i].transition->get_xfade_curve(); switch_mode = p_state_machine->transitions[i].transition->get_switch_mode(); + reset_request = p_state_machine->transitions[i].transition->is_reset(); next = path[0]; } } @@ -513,6 +555,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve(); next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time(); switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode(); + reset_request = p_state_machine->transitions[auto_advance_to].transition->is_reset(); } } @@ -567,7 +610,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s goto_next = fading_from == StringName(); } - if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped + if (next_request || goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped if (next_xfade) { //time to fade, baby fading_from = current; @@ -591,7 +634,9 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = next; - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. + if (reset_request) { + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. + } if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { pos_current = MIN(pos_current, len_current); p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); @@ -652,13 +697,15 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima } void AnimationNodeStateMachinePlayback::_bind_methods() { - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("travel", "to_node", "reset_on_teleport"), &AnimationNodeStateMachinePlayback::travel, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("start", "node", "reset"), &AnimationNodeStateMachinePlayback::start, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("next"), &AnimationNodeStateMachinePlayback::next); ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); ClassDB::bind_method(D_METHOD("get_current_play_position"), &AnimationNodeStateMachinePlayback::get_current_play_pos); ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length); + ClassDB::bind_method(D_METHOD("get_fading_from_node"), &AnimationNodeStateMachinePlayback::get_fading_from_node); ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 116589eb2f..1b4e010a06 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -57,6 +57,7 @@ private: StringName advance_condition_name; float xfade_time = 0.0; Ref<Curve> xfade_curve; + bool reset = true; int priority = 1; String advance_expression; @@ -84,6 +85,9 @@ public: void set_xfade_time(float p_xfade); float get_xfade_time() const; + void set_reset(bool p_reset); + bool is_reset() const; + void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() const; @@ -131,10 +135,15 @@ class AnimationNodeStateMachinePlayback : public Resource { bool playing = false; StringName start_request; - bool start_request_travel = false; + StringName travel_request; + bool reset_request = false; + bool reset_request_on_teleport = false; + bool next_request = false; bool stop_request = false; bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); + void _start(const StringName &p_state); + double _process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking); double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking); @@ -144,12 +153,13 @@ protected: static void _bind_methods(); public: - void travel(const StringName &p_state); - void start(const StringName &p_state); + void travel(const StringName &p_state, bool p_reset_on_teleport = true); + void start(const StringName &p_state, bool p_reset = true); + void next(); void stop(); bool is_playing() const; StringName get_current_node() const; - StringName get_blend_from_node() const; + StringName get_fading_from_node() const; Vector<StringName> get_travel_path() const; float get_current_play_pos() const; float get_current_length() const; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 0febe580db..047997ca09 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -143,8 +143,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } else if (name.begins_with("libraries")) { Dictionary d; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - d[animation_libraries[i].name] = animation_libraries[i].library; + for (const AnimationLibraryData &lib : animation_libraries) { + d[lib.name] = lib.library; } r_ret = d; @@ -1269,13 +1269,13 @@ void AnimationPlayer::_animation_set_cache_update() { bool clear_cache_needed = false; // Update changed and add otherwise - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { - StringName key = animation_libraries[i].name == StringName() ? K.key : StringName(String(animation_libraries[i].name) + "/" + String(K.key)); + for (const AnimationLibraryData &lib : animation_libraries) { + for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) { + StringName key = lib.name == StringName() ? K.key : StringName(String(lib.name) + "/" + String(K.key)); if (!animation_set.has(key)) { AnimationData ad; ad.animation = K.value; - ad.animation_library = animation_libraries[i].name; + ad.animation_library = lib.name; ad.name = key; ad.last_update = animation_set_update_pass; animation_set.insert(ad.name, ad); @@ -1283,11 +1283,11 @@ void AnimationPlayer::_animation_set_cache_update() { AnimationData &ad = animation_set[key]; if (ad.last_update != animation_set_update_pass) { // Was not updated, update. If the animation is duplicated, the second one will be ignored. - if (ad.animation != K.value || ad.animation_library != animation_libraries[i].name) { + if (ad.animation != K.value || ad.animation_library != lib.name) { // Animation changed, update and clear caches. clear_cache_needed = true; ad.animation = K.value; - ad.animation_library = animation_libraries[i].name; + ad.animation_library = lib.name; } ad.last_update = animation_set_update_pass; @@ -1405,11 +1405,11 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref int insert_pos = 0; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ERR_FAIL_COND_V_MSG(animation_libraries[i].name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name)); - ERR_FAIL_COND_V_MSG(animation_libraries[i].library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + animation_libraries[i].name.operator String() + "'."); + for (const AnimationLibraryData &lib : animation_libraries) { + ERR_FAIL_COND_V_MSG(lib.name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name)); + ERR_FAIL_COND_V_MSG(lib.library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + lib.name.operator String() + "'."); - if (animation_libraries[i].name.operator String() >= p_name.operator String()) { + if (lib.name.operator String() >= p_name.operator String()) { break; } @@ -1468,21 +1468,21 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S #endif bool found = false; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ERR_FAIL_COND_MSG(animation_libraries[i].name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name)); - if (animation_libraries[i].name == p_name) { + for (AnimationLibraryData &lib : animation_libraries) { + ERR_FAIL_COND_MSG(lib.name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name)); + if (lib.name == p_name) { found = true; - animation_libraries[i].name = p_new_name; + lib.name = p_new_name; // rename connections - animation_libraries[i].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); - animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); - animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + lib.library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); + lib.library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); + lib.library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); - animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); + lib.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); + lib.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name)); + lib.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); - for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { + for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) { StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key)); StringName new_name = p_new_name == StringName() ? K.key : StringName(String(p_new_name) + "/" + String(K.key)); _rename_animation(old_name, new_name); @@ -1502,8 +1502,8 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S } bool AnimationPlayer::has_animation_library(const StringName &p_name) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - if (animation_libraries[i].name == p_name) { + for (const AnimationLibraryData &lib : animation_libraries) { + if (lib.name == p_name) { return true; } } @@ -1512,9 +1512,9 @@ bool AnimationPlayer::has_animation_library(const StringName &p_name) const { } Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p_name) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - if (animation_libraries[i].name == p_name) { - return animation_libraries[i].library; + for (const AnimationLibraryData &lib : animation_libraries) { + if (lib.name == p_name) { + return lib.library; } } ERR_FAIL_V(Ref<AnimationLibrary>()); @@ -1522,15 +1522,15 @@ Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p TypedArray<StringName> AnimationPlayer::_get_animation_library_list() const { TypedArray<StringName> ret; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ret.push_back(animation_libraries[i].name); + for (const AnimationLibraryData &lib : animation_libraries) { + ret.push_back(lib.name); } return ret; } void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - p_libraries->push_back(animation_libraries[i].name); + for (const AnimationLibraryData &lib : animation_libraries) { + p_libraries->push_back(lib.name); } } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index ab341797c7..077a5696bb 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -54,13 +54,19 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter return ret; } +bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const { + bool ret = false; + GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret); + return ret; +} + void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND(!state); ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path)); ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name)); StringName path = state->tree->property_parent_map[base_path][p_name]; - state->tree->property_map[path] = p_value; + state->tree->property_map[path].first = p_value; } Variant AnimationNode::get_parameter(const StringName &p_name) const { @@ -69,7 +75,7 @@ Variant AnimationNode::get_parameter(const StringName &p_name) const { ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name), Variant()); StringName path = state->tree->property_parent_map[base_path][p_name]; - return state->tree->property_map[path]; + return state->tree->property_map[path].first; } void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { @@ -427,6 +433,7 @@ void AnimationNode::_bind_methods() { GDVIRTUAL_BIND(_get_parameter_list); GDVIRTUAL_BIND(_get_child_by_name, "name"); GDVIRTUAL_BIND(_get_parameter_default_value, "parameter"); + GDVIRTUAL_BIND(_is_parameter_read_only, "parameter"); GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking"); GDVIRTUAL_BIND(_get_caption); GDVIRTUAL_BIND(_has_filter); @@ -747,7 +754,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { if (has_reset_anim) { int rt = reset_anim->find_track(path, track_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_bezier->init_value = reset_anim->track_get_key_value(rt, 0); + track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; } } } break; @@ -1889,7 +1896,10 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A StringName key = pinfo.name; if (!property_map.has(p_base_path + key)) { - property_map[p_base_path + key] = node->get_parameter_default_value(key); + Pair<Variant, bool> param; + param.first = node->get_parameter_default_value(key); + param.second = node->is_parameter_read_only(key); + property_map[p_base_path + key] = param; } property_parent_map[p_base_path][key] = p_base_path + key; @@ -1931,7 +1941,10 @@ bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) { } if (property_map.has(p_name)) { - property_map[p_name] = p_value; + if (is_inside_tree() && property_map[p_name].second) { + return false; // Prevent to set property by user. + } + property_map[p_name].first = p_value; return true; } @@ -1944,7 +1957,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { } if (property_map.has(p_name)) { - r_ret = property_map[p_name]; + r_ret = property_map[p_name].first; return true; } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 2c1be6199c..52d3e1bd41 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -117,6 +117,7 @@ protected: GDVIRTUAL0RC(Array, _get_parameter_list) GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName) GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName) + GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName) GDVIRTUAL3RC(double, _process, double, bool, bool) GDVIRTUAL0RC(String, _get_caption) GDVIRTUAL0RC(bool, _has_filter) @@ -124,6 +125,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const; virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual bool is_parameter_read_only(const StringName &p_parameter) const; void set_parameter(const StringName &p_name, const Variant &p_value); Variant get_parameter(const StringName &p_name) const; @@ -304,7 +306,7 @@ private: void _update_properties(); List<PropertyInfo> properties; HashMap<StringName, HashMap<StringName, StringName>> property_parent_map; - HashMap<StringName, Variant> property_map; + HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag. struct Activity { uint64_t last_pass = 0; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index d0326290ac..472299b135 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -385,6 +385,7 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) { if (shortcut_feedback) { if (shortcut_feedback_timer == nullptr) { shortcut_feedback_timer = memnew(Timer); + shortcut_feedback_timer->set_one_shot(true); add_child(shortcut_feedback_timer); shortcut_feedback_timer->set_wait_time(GLOBAL_GET("gui/timers/button_shortcut_feedback_highlight_time")); shortcut_feedback_timer->connect("timeout", callable_mp(this, &BaseButton::_shortcut_feedback_timeout)); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index b0261dcf23..2c5248f088 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -35,11 +35,6 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "scene/gui/color_mode.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - #include "thirdparty/misc/ok_color.h" #include "thirdparty/misc/ok_color_shader.h" @@ -50,31 +45,6 @@ void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_color(); -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - if (preset_cache.is_empty()) { - PackedColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PackedColorArray()); - for (int i = 0; i < saved_presets.size(); i++) { - preset_cache.push_back(saved_presets[i]); - } - } - - for (int i = 0; i < preset_cache.size(); i++) { - presets.push_back(preset_cache[i]); - } - - if (recent_preset_cache.is_empty()) { - PackedColorArray saved_recent_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "recent_presets", PackedColorArray()); - for (int i = 0; i < saved_recent_presets.size(); i++) { - recent_preset_cache.push_back(saved_recent_presets[i]); - } - } - - for (int i = 0; i < recent_preset_cache.size(); i++) { - recent_presets.push_back(recent_preset_cache[i]); - } - } -#endif [[fallthrough]]; } case NOTIFICATION_THEME_CHANGED: { @@ -404,6 +374,40 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) { } } +#ifdef TOOLS_ENABLED +void ColorPicker::set_editor_settings(Object *p_editor_settings) { + if (editor_settings) { + return; + } + editor_settings = p_editor_settings; + + if (preset_cache.is_empty()) { + PackedColorArray saved_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "presets", PackedColorArray()); + for (int i = 0; i < saved_presets.size(); i++) { + preset_cache.push_back(saved_presets[i]); + } + } + + for (int i = 0; i < preset_cache.size(); i++) { + presets.push_back(preset_cache[i]); + } + + if (recent_preset_cache.is_empty()) { + PackedColorArray saved_recent_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "recent_presets", PackedColorArray()); + for (int i = 0; i < saved_recent_presets.size(); i++) { + recent_preset_cache.push_back(saved_recent_presets[i]); + } + } + + for (int i = 0; i < recent_preset_cache.size(); i++) { + recent_presets.push_back(recent_preset_cache[i]); + } + + _update_presets(); + _update_recent_presets(); +} +#endif + HSlider *ColorPicker::get_slider(int p_idx) { if (p_idx < SLIDER_COUNT) { return sliders[p_idx]; @@ -553,7 +557,7 @@ void ColorPicker::_update_presets() { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { // Only load preset buttons when the only child is the add-preset button. if (preset_container->get_child_count() == 1) { for (int i = 0; i < preset_cache.size(); i++) { @@ -567,7 +571,7 @@ void ColorPicker::_update_presets() { void ColorPicker::_update_recent_presets() { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { int recent_preset_count = recent_preset_hbc->get_child_count(); for (int i = 0; i < recent_preset_count; i++) { memdelete(recent_preset_hbc->get_child(0)); @@ -743,9 +747,9 @@ void ColorPicker::add_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save); } #endif } @@ -764,9 +768,9 @@ void ColorPicker::add_recent_preset(const Color &p_color) { _select_from_preset_container(p_color); #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_recent_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "recent_presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save); } #endif } @@ -787,9 +791,9 @@ void ColorPicker::erase_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save); } #endif } @@ -811,9 +815,9 @@ void ColorPicker::erase_recent_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_recent_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "recent_presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save); } #endif } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 5eaeecca2a..f7578612cd 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -100,6 +100,10 @@ private: static List<Color> preset_cache; static List<Color> recent_preset_cache; +#ifdef TOOLS_ENABLED + Object *editor_settings = nullptr; +#endif + int current_slider_count = SLIDER_COUNT; static const int MODE_BUTTON_COUNT = 3; @@ -231,6 +235,10 @@ protected: static void _bind_methods(); public: +#ifdef TOOLS_ENABLED + void set_editor_settings(Object *p_editor_settings); +#endif + HSlider *get_slider(int idx); Vector<float> get_active_slider_values(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 5930818763..fead0878fb 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1582,10 +1582,6 @@ void Control::set_block_minimum_size_adjust(bool p_block) { data.block_minimum_size_adjust = p_block; } -bool Control::is_minimum_size_adjust_blocked() const { - return data.block_minimum_size_adjust; -} - Size2 Control::get_minimum_size() const { Vector2 ms; GDVIRTUAL_CALL(_get_minimum_size, ms); @@ -2769,9 +2765,9 @@ void Control::end_bulk_theme_override() { // Internationalization. -TypedArray<Vector2i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { +TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { - TypedArray<Vector2i> ret; + TypedArray<Vector3i> ret; GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret); return ret; } else { diff --git a/scene/gui/control.h b/scene/gui/control.h index aaab9f530c..a93a88e5b4 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -145,7 +145,7 @@ public: TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO, TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR, TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL, - TEXT_DIRECTION_INHERITED, + TEXT_DIRECTION_INHERITED = TextServer::DIRECTION_INHERITED, }; private: @@ -330,7 +330,7 @@ protected: // Internationalization. - virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; + virtual TypedArray<Vector3i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; // Base object overrides. @@ -340,7 +340,7 @@ protected: // Exposed virtual methods. GDVIRTUAL1RC(bool, _has_point, Vector2) - GDVIRTUAL2RC(TypedArray<Vector2i>, _structured_text_parser, Array, String) + GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String) GDVIRTUAL0RC(Vector2, _get_minimum_size) GDVIRTUAL1RC(Variant, _get_drag_data, Vector2) @@ -464,7 +464,6 @@ public: void update_minimum_size(); void set_block_minimum_size_adjust(bool p_block); - bool is_minimum_size_adjust_blocked() const; virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 372baeadae..25a27d5e1a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -309,12 +309,6 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) { shape_changed = true; } -Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Texture2D>()); - - return items[p_idx].tag_icon; -} - void ItemList::set_item_selectable(int p_idx, bool p_selectable) { if (p_idx < 0) { p_idx += get_item_count(); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 848f9a2ba9..934318dbb4 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -191,7 +191,6 @@ public: Variant get_item_metadata(int p_idx) const; void set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon); - Ref<Texture2D> get_item_tag_icon(int p_idx) const; void set_item_tooltip_enabled(int p_idx, const bool p_enabled); bool is_item_tooltip_enabled(int p_idx) const; diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index c939e7a48a..2ea1b93810 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -59,9 +59,9 @@ void Popup::_initialize_visible_parents() { void Popup::_deinitialize_visible_parents() { if (is_embedded()) { - for (uint32_t i = 0; i < visible_parents.size(); ++i) { - visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); - visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); + for (Window *parent_window : visible_parents) { + parent_window->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); + parent_window->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); } visible_parents.clear(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 5ab64b35fd..a7e50a765e 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -4074,8 +4074,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL; } else if (subtag_a[1] == "l" || subtag_a[1] == "list") { st_parser_type = TextServer::STRUCTURED_TEXT_LIST; - } else if (subtag_a[1] == "n" || subtag_a[1] == "none") { - st_parser_type = TextServer::STRUCTURED_TEXT_NONE; + } else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") { + st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT; } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") { st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 108a533a74..8ffaa9e81f 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1208,7 +1208,15 @@ void TextEdit::_notification(int p_what) { char_ofs = 0; } for (int j = 0; j < gl_size; j++) { - const Variant *color_data = color_map.getptr(glyphs[j].start); + int64_t color_start = -1; + for (const Variant *key = color_map.next(nullptr); key; key = color_map.next(key)) { + if (int64_t(*key) <= glyphs[j].start) { + color_start = *key; + } else { + break; + } + } + const Variant *color_data = (color_start >= 0) ? color_map.getptr(color_start) : nullptr; if (color_data != nullptr) { current_color = (color_data->operator Dictionary()).get("color", font_color); if (!editable && current_color.a > font_readonly_color.a) { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 2138f10ad0..2d985c2324 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -332,7 +332,7 @@ void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::Struc } TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const { - ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE); + ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_DEFAULT); return cells[p_column].st_parser; } diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index a4af7988c6..7b0554442c 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -388,6 +388,16 @@ Color CanvasItem::get_modulate() const { return modulate; } +Color CanvasItem::get_modulate_in_tree() const { + Color final_modulate = modulate; + CanvasItem *parent_item = get_parent_item(); + while (parent_item) { + final_modulate *= parent_item->get_modulate(); + parent_item = parent_item->get_parent_item(); + } + return final_modulate; +} + void CanvasItem::set_as_top_level(bool p_top_level) { if (top_level == p_top_level) { return; @@ -560,44 +570,47 @@ void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vec void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + Rect2 rect = p_rect.abs(); + if (p_filled) { if (p_width != -1.0) { WARN_PRINT("The draw_rect() \"width\" argument has no effect when \"filled\" is \"true\"."); } - RenderingServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(canvas_item, rect, p_color); + } else if (p_width >= rect.size.width || p_width >= rect.size.height) { + RenderingServer::get_singleton()->canvas_item_add_rect(canvas_item, rect.grow(0.5f * p_width), p_color); } else { // Thick lines are offset depending on their width to avoid partial overlapping. - // Thin lines don't require an offset, so don't apply one in this case - real_t offset; - if (p_width >= 0) { - offset = p_width / 2.0; - } else { - offset = 0.0; - } + // Thin lines are drawn without offset. The result may not be perfect. + real_t offset = (p_width >= 0) ? 0.5f * p_width : 0.0f; + // Top line. RenderingServer::get_singleton()->canvas_item_add_line( canvas_item, - p_rect.position + Size2(-offset, 0), - p_rect.position + Size2(p_rect.size.width + offset, 0), + rect.position + Size2(-offset, 0), + rect.position + Size2(-offset + rect.size.width, 0), p_color, p_width); + // Right line. RenderingServer::get_singleton()->canvas_item_add_line( canvas_item, - p_rect.position + Size2(p_rect.size.width, offset), - p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset), + rect.position + Size2(rect.size.width, -offset), + rect.position + Size2(rect.size.width, -offset + rect.size.height), p_color, p_width); + // Bottom line. RenderingServer::get_singleton()->canvas_item_add_line( canvas_item, - p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height), - p_rect.position + Size2(-offset, p_rect.size.height), + rect.position + Size2(offset + rect.size.width, rect.size.height), + rect.position + Size2(offset, rect.size.height), p_color, p_width); + // Left line. RenderingServer::get_singleton()->canvas_item_add_line( canvas_item, - p_rect.position + Size2(0, p_rect.size.height - offset), - p_rect.position + Size2(0, offset), + rect.position + Size2(0, offset + rect.size.height), + rect.position + Size2(0, offset), p_color, p_width); } @@ -973,9 +986,9 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(-1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash", "aligned"), &CanvasItem::draw_dashed_line, DEFVAL(-1.0), DEFVAL(2.0), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(-1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(-1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(-1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(-1.0)); ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(-1.0)); ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(-1.0)); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index b2c12b2ea2..644fe856ec 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -224,6 +224,7 @@ public: void set_modulate(const Color &p_modulate); Color get_modulate() const; + Color get_modulate_in_tree() const; void set_self_modulate(const Color &p_self_modulate); Color get_self_modulate() const; @@ -249,9 +250,9 @@ public: void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, real_t p_dash = 2.0, bool p_aligned = true); void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false); - void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); - void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0, bool p_antialiased = false); - void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); + void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false); + void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false); + void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false); void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0); void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0); void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = -1.0); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index eb57ccfef1..def91c424c 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -279,7 +279,10 @@ void Node::_propagate_exit_tree() { //block while removing children #ifdef DEBUG_ENABLED - SceneDebugger::remove_from_cache(data.scene_file_path, this); + if (!data.scene_file_path.is_empty()) { + // Only remove if file path is set (optimization). + SceneDebugger::remove_from_cache(data.scene_file_path, this); + } #endif data.blocked++; @@ -305,7 +308,6 @@ void Node::_propagate_exit_tree() { } // exit groups - for (KeyValue<StringName, GroupData> &E : data.grouped) { data.tree->remove_from_group(E.key, this); E.value.group = nullptr; @@ -1166,7 +1168,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { void Node::remove_child(Node *p_child) { ERR_FAIL_NULL(p_child); - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `remove_child()` failed. Consider using `remove_child.call_deferred(child)` instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy adding/removing children, `remove_child()` can't be called at this time. Consider using `remove_child.call_deferred(child)` instead."); int child_count = data.children.size(); Node **children = data.children.ptrw(); @@ -1197,11 +1199,13 @@ void Node::remove_child(Node *p_child) { data.internal_children_back--; } + data.blocked++; p_child->_set_tree(nullptr); //} remove_child_notify(p_child); p_child->notification(NOTIFICATION_UNPARENTED); + data.blocked--; data.children.remove_at(idx); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9efe649e6f..07bcf45899 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3916,7 +3916,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); #ifndef _3D_DISABLED ADD_GROUP("Scaling 3D", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index b371266c83..50f3015814 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -4754,17 +4754,17 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol // The frame has advanced, time to validate the previous frame uint32_t current_page_size = base_page_size; - for (uint32_t i = 0; i < data_tracks.size(); i++) { - uint32_t track_size = data_tracks[i].data.size(); // track size - track_size += data_tracks[i].get_temp_packet_size(); // Add the temporary data + for (const AnimationCompressionDataState &state : data_tracks) { + uint32_t track_size = state.data.size(); // track size + track_size += state.get_temp_packet_size(); // Add the temporary data if (track_size > Compression::MAX_DATA_TRACK_SIZE) { rollback = true; //track to large, time track can't point to keys any longer, because key offset is 12 bits break; } current_page_size += track_size; } - for (uint32_t i = 0; i < time_tracks.size(); i++) { - current_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits + for (const AnimationCompressionTimeState &state : time_tracks) { + current_page_size += state.packets.size() * 4; // time packet is 32 bits } if (!rollback && current_page_size > p_page_size) { @@ -4776,22 +4776,22 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol if (rollback) { // Not valid any longer, so rollback and commit page - for (uint32_t i = 0; i < data_tracks.size(); i++) { - data_tracks[i].temp_packets.resize(data_tracks[i].validated_packet_count); + for (AnimationCompressionDataState &state : data_tracks) { + state.temp_packets.resize(state.validated_packet_count); } - for (uint32_t i = 0; i < time_tracks.size(); i++) { - time_tracks[i].key_index = time_tracks[i].validated_key_index; //rollback key - time_tracks[i].packets.resize(time_tracks[i].validated_packet_count); + for (AnimationCompressionTimeState &state : time_tracks) { + state.key_index = state.validated_key_index; //rollback key + state.packets.resize(state.validated_packet_count); } } else { // All valid, so save rollback information - for (uint32_t i = 0; i < data_tracks.size(); i++) { - data_tracks[i].validated_packet_count = data_tracks[i].temp_packets.size(); + for (AnimationCompressionDataState &state : data_tracks) { + state.validated_packet_count = state.temp_packets.size(); } - for (uint32_t i = 0; i < time_tracks.size(); i++) { - time_tracks[i].validated_key_index = time_tracks[i].key_index; - time_tracks[i].validated_packet_count = time_tracks[i].packets.size(); + for (AnimationCompressionTimeState &state : time_tracks) { + state.validated_key_index = state.key_index; + state.validated_packet_count = state.packets.size(); } // Accept this frame as the frame being processed (as long as it exists) @@ -4976,8 +4976,8 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol } uint32_t new_size = 0; - for (uint32_t i = 0; i < compression.pages.size(); i++) { - new_size += compression.pages[i].data.size(); + for (const Compression::Page &page : compression.pages) { + new_size += page.data.size(); } print_line("Original size: " + itos(orig_size) + " - Compressed size: " + itos(new_size) + " " + String::num(float(new_size) / float(orig_size) * 100, 2) + "% pages: " + itos(compression.pages.size())); @@ -5289,8 +5289,8 @@ int Animation::_get_compressed_key_count(uint32_t p_compressed_track) const { int key_count = 0; - for (uint32_t i = 0; i < compression.pages.size(); i++) { - const uint8_t *page_data = compression.pages[i].data.ptr(); + for (const Compression::Page &page : compression.pages) { + const uint8_t *page_data = page.data.ptr(); // Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported. const uint32_t *indices = (const uint32_t *)page_data; const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; @@ -5323,8 +5323,8 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in ERR_FAIL_COND_V(!compression.enabled, false); ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false); - for (uint32_t i = 0; i < compression.pages.size(); i++) { - const uint8_t *page_data = compression.pages[i].data.ptr(); + for (const Compression::Page &page : compression.pages) { + const uint8_t *page_data = page.data.ptr(); // Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported. const uint32_t *indices = (const uint32_t *)page_data; const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]]; @@ -5374,7 +5374,7 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in } } - r_time = compression.pages[i].time_offset + double(frame) / double(compression.fps); + r_time = page.time_offset + double(frame) / double(compression.fps); for (uint32_t l = 0; l < COMPONENTS; l++) { r_value[l] = decode[l]; } diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 746f2f8f9b..8b4656414d 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1043,6 +1043,18 @@ void Environment::_validate_property(PropertyInfo &p_property) const { } } + if (p_property.name == "ambient_light_color" || p_property.name == "ambient_light_energy") { + if (ambient_source == AMBIENT_SOURCE_DISABLED) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } + + if (p_property.name == "ambient_light_sky_contribution") { + if (ambient_source == AMBIENT_SOURCE_DISABLED || ambient_source == AMBIENT_SOURCE_COLOR) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } + if (p_property.name == "fog_aerial_perspective") { if (bg_mode != BG_SKY) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index f4a0db3930..48d609da97 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -41,8 +41,8 @@ void ImmediateMesh::surface_set_color(const Color &p_color) { if (!uses_colors) { colors.resize(vertices.size()); - for (uint32_t i = 0; i < colors.size(); i++) { - colors[i] = p_color; + for (Color &color : colors) { + color = p_color; } uses_colors = true; } @@ -54,8 +54,8 @@ void ImmediateMesh::surface_set_normal(const Vector3 &p_normal) { if (!uses_normals) { normals.resize(vertices.size()); - for (uint32_t i = 0; i < normals.size(); i++) { - normals[i] = p_normal; + for (Vector3 &normal : normals) { + normal = p_normal; } uses_normals = true; } @@ -66,8 +66,8 @@ void ImmediateMesh::surface_set_tangent(const Plane &p_tangent) { ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); if (!uses_tangents) { tangents.resize(vertices.size()); - for (uint32_t i = 0; i < tangents.size(); i++) { - tangents[i] = p_tangent; + for (Plane &tangent : tangents) { + tangent = p_tangent; } uses_tangents = true; } @@ -78,8 +78,8 @@ void ImmediateMesh::surface_set_uv(const Vector2 &p_uv) { ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); if (!uses_uvs) { uvs.resize(vertices.size()); - for (uint32_t i = 0; i < uvs.size(); i++) { - uvs[i] = p_uv; + for (Vector2 &uv : uvs) { + uv = p_uv; } uses_uvs = true; } @@ -90,8 +90,8 @@ void ImmediateMesh::surface_set_uv2(const Vector2 &p_uv2) { ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); if (!uses_uv2s) { uv2s.resize(vertices.size()); - for (uint32_t i = 0; i < uv2s.size(); i++) { - uv2s[i] = p_uv2; + for (Vector2 &uv : uv2s) { + uv = p_uv2; } uses_uv2s = true; } diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index eefa5aa14a..55b633a40c 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -364,9 +364,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli const LocalVector<Pair<int, int>> &close_verts = E->value; bool found = false; - for (unsigned int k = 0; k < close_verts.size(); k++) { - const Pair<int, int> &idx = close_verts[k]; - + for (const Pair<int, int> &idx : close_verts) { bool is_uvs_close = (!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2); bool is_uv2s_close = (!uv2s_ptr || uv2s_ptr[j].distance_squared_to(uv2s_ptr[idx.second]) < CMP_EPSILON2); ERR_FAIL_INDEX(idx.second, normals.size()); @@ -599,8 +597,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli const LocalVector<int> &corners = vertex_corners[j]; const Vector3 &vertex_normal = normals_ptr[j]; - for (unsigned int k = 0; k < corners.size(); k++) { - const int &corner_idx = corners[k]; + for (const int &corner_idx : corners) { const Vector3 &ray_normal = ray_normals[corner_idx]; if (ray_normal.length_squared() < CMP_EPSILON2) { @@ -635,8 +632,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli split_vertex_indices.push_back(j); split_vertex_normals.push_back(n); int new_idx = split_vertex_count++; - for (unsigned int l = 0; l < group_indices.size(); l++) { - new_indices_ptr[group_indices[l]] = new_idx; + for (const int &index : group_indices) { + new_indices_ptr[index] = new_idx; } } } @@ -1241,10 +1238,10 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, } //generate surfaces - for (unsigned int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools[i]->index(); - Array arrays = surfaces_tools[i]->commit_to_arrays(); - add_surface(surfaces_tools[i]->get_primitive_type(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); + for (Ref<SurfaceTool> &tool : surfaces_tools) { + tool->index(); + Array arrays = tool->commit_to_arrays(); + add_surface(tool->get_primitive_type(), arrays, Array(), Dictionary(), tool->get_material(), tool->get_meta("name")); } set_lightmap_size_hint(Size2(size_x, size_y)); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 3e2a952ea7..db7385428b 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -149,11 +149,36 @@ Material::~Material() { bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { if (shader.is_valid()) { - StringName pr = shader->remap_parameter(p_name); - if (pr) { - set_shader_parameter(pr, p_value); + const StringName *sn = remap_cache.getptr(p_name); + if (sn) { + set_shader_parameter(*sn, p_value); + return true; + } + String s = p_name; + if (s.begins_with("shader_parameter/")) { + String param = s.replace_first("shader_parameter/", ""); + remap_cache[s] = param; + set_shader_parameter(param, p_value); return true; } +#ifndef DISABLE_DEPRECATED + // Compatibility remaps are only needed here. + if (s.begins_with("param/")) { + s = s.replace_first("param/", "shader_parameter/"); + } else if (s.begins_with("shader_param/")) { + s = s.replace_first("shader_param/", "shader_parameter/"); + } else if (s.begins_with("shader_uniform/")) { + s = s.replace_first("shader_uniform/", "shader_parameter/"); + } else { + return false; // Not a shader parameter. + } + + WARN_PRINT("This material (containing shader with path: '" + shader->get_path() + "') uses an old deprecated parameter names. Consider re-saving this resource (or scene which contains it) in order for it to continue working in future versions."); + String param = s.replace_first("shader_parameter/", ""); + remap_cache[s] = param; + set_shader_parameter(param, p_value); + return true; +#endif } return false; @@ -161,9 +186,10 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { if (shader.is_valid()) { - StringName pr = shader->remap_parameter(p_name); - if (pr) { - r_ret = get_shader_parameter(pr); + const StringName *sn = remap_cache.getptr(p_name); + if (sn) { + // Only return a parameter if it was previosly set. + r_ret = get_shader_parameter(*sn); return true; } } @@ -247,6 +273,12 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { PropertyInfo info = E->get(); info.name = "shader_parameter/" + info.name; + if (!param_cache.has(E->get().name)) { + // Property has never been edited, retrieve with default value. + Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), E->get().name); + param_cache.insert(E->get().name, default_value); + remap_cache.insert(info.name, E->get().name); + } groups[last_group][last_subgroup].push_back(info); } @@ -275,11 +307,10 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { if (shader.is_valid()) { - StringName pr = shader->remap_parameter(p_name); + const StringName *pr = remap_cache.getptr(p_name); if (pr) { - Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); - Variant current_value; - _get(p_name, current_value); + Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr); + Variant current_value = get_shader_parameter(*pr); return default_value.get_type() != Variant::NIL && default_value != current_value; } } @@ -288,9 +319,9 @@ bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { bool ShaderMaterial::_property_get_revert(const StringName &p_name, Variant &r_property) const { if (shader.is_valid()) { - StringName pr = shader->remap_parameter(p_name); - if (pr) { - r_property = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); + const StringName *pr = remap_cache.getptr(p_name); + if (*pr) { + r_property = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr); return true; } } diff --git a/scene/resources/material.h b/scene/resources/material.h index f1777d31f4..83c7a09cc4 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -82,7 +82,8 @@ class ShaderMaterial : public Material { GDCLASS(ShaderMaterial, Material); Ref<Shader> shader; - HashMap<StringName, Variant> param_cache; + mutable HashMap<StringName, StringName> remap_cache; + mutable HashMap<StringName, Variant> param_cache; protected: bool _set(const StringName &p_name, const Variant &p_value); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 5e18b5df37..cedf4319f8 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -687,6 +687,7 @@ void Mesh::_bind_methods() { BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS); + BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); @@ -1555,6 +1556,7 @@ void ArrayMesh::_recompute_aabb() { // TODO: Need to add binding to add_surface using future MeshSurfaceData object. void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) { + ERR_FAIL_COND(surfaces.size() == RS::MAX_MESH_SURFACES); _create_if_empty(); Surface s; @@ -1590,6 +1592,7 @@ void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_prim } void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, BitField<ArrayFormat> p_flags) { + ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size()); ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX); RS::SurfaceData surface; @@ -2058,7 +2061,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode); - ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region); ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region); diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index d45b8a9295..015eb0fa45 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -139,7 +139,6 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].name = p_name; emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { @@ -155,7 +154,6 @@ void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_trans item_map[p_item].mesh_transform = p_transform; notify_change_to_owners(); emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) { @@ -190,7 +188,6 @@ void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_l notify_property_list_changed(); notify_change_to_owners(); emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_preview(int p_item, const Ref<Texture2D> &p_preview) { diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 514e7eb260..c24186a109 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -402,8 +402,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - for (uint32_t i = 0; i < deferred_node_paths.size(); i++) { - const DeferredNodePathProperties &dnp = deferred_node_paths[i]; + for (const DeferredNodePathProperties &dnp : deferred_node_paths) { Node *other = dnp.base->get_node_or_null(dnp.path); dnp.base->set(dnp.property, other); } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 5ef66a22b6..86ed0001dd 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2901,7 +2901,7 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } - Array stt; + TypedArray<Vector3i> stt; if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt); } else { diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 22cd12b004..e62f26b17c 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -622,7 +622,7 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: - GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String) TextMesh(); ~TextMesh(); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 80b9ff3f38..0ba177f882 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -49,10 +49,6 @@ /// -void ResourceLoaderText::set_local_path(const String &p_local_path) { - res_path = p_local_path; -} - Ref<Resource> ResourceLoaderText::get_resource() { return resource; } @@ -927,7 +923,11 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String if (is_scene) { fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); } else { - fw->store_line("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); + String script_res_text; + if (!script_class.is_empty()) { + script_res_text = "script_class=\"" + script_class + "\" "; + } + fw->store_line("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); } } @@ -1051,6 +1051,10 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { return; } + if (tag.fields.has("script_class")) { + script_class = tag.fields["script_class"]; + } + res_type = tag.fields["type"]; } else { @@ -1497,6 +1501,44 @@ Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) { return OK; } +String ResourceLoaderText::recognize_script_class(Ref<FileAccess> p_f) { + error = OK; + + lines = 1; + f = p_f; + + stream.f = f; + + ignore_resource_parsing = true; + + VariantParser::Tag tag; + Error err = VariantParser::parse_tag(&stream, lines, error_text, tag); + + if (err) { + _printerr(); + return ""; + } + + if (tag.fields.has("format")) { + int fmt = tag.fields["format"]; + if (fmt > FORMAT_VERSION) { + error_text = "Saved with newer format version"; + _printerr(); + return ""; + } + } + + if (tag.name != "gd_resource") { + return ""; + } + + if (tag.fields.has("script_class")) { + return tag.fields["script_class"]; + } + + return ""; +} + String ResourceLoaderText::recognize(Ref<FileAccess> p_f) { error = OK; @@ -1666,6 +1708,25 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { return ClassDB::get_compatibility_remapped_class(r); } +String ResourceFormatLoaderText::get_resource_script_class(const String &p_path) const { + String ext = p_path.get_extension().to_lower(); + if (ext != "tres") { + return String(); + } + + // ...for anything else must test... + + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return ""; //could not read + } + + ResourceLoaderText loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + return loader.recognize_script_class(f); +} + ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) const { String ext = p_path.get_extension().to_lower(); @@ -1846,6 +1907,9 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, List<Variant> keys; d.get_key_list(&keys); for (const Variant &E : keys) { + // Of course keys should also be cached, after all we can't prevent users from using resources as keys, right? + // See also ResourceFormatSaverBinaryInstance::_find_resources (when p_variant is of type Variant::DICTIONARY) + _find_resources(E); Variant v = d[E]; _find_resources(v); } @@ -1906,7 +1970,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; if (packed_scene.is_null()) { title += "type=\"" + _resource_get_class(p_resource) + "\" "; + Ref<Script> script = p_resource->get_script(); + if (script.is_valid() && script->get_global_name()) { + title += "script_class=\"" + String(script->get_global_name()) + "\" "; + } } + int load_steps = saved_resources.size() + external_resources.size(); if (load_steps > 1) { @@ -2245,7 +2314,12 @@ Error ResourceLoaderText::set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid) { if (is_scene) { fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } else { - fw->store_string("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + String script_res_text; + if (!script_class.is_empty()) { + script_res_text = "script_class=\"" + script_class + "\" "; + } + + fw->store_string("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } uint8_t c = f->get_8(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 0f95e2fbfd..25001d8023 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -64,6 +64,7 @@ class ResourceLoaderText { int resources_total = 0; int resource_current = 0; String resource_type; + String script_class; VariantParser::Tag next_tag; @@ -115,7 +116,6 @@ class ResourceLoaderText { Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser); public: - void set_local_path(const String &p_local_path); Ref<Resource> get_resource(); Error load(); Error set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid); @@ -125,6 +125,7 @@ public: void open(Ref<FileAccess> p_f, bool p_skip_first_tag = false); String recognize(Ref<FileAccess> p_f); + String recognize_script_class(Ref<FileAccess> p_f); ResourceUID::ID get_uid(Ref<FileAccess> p_f); void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types); Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map); @@ -144,6 +145,7 @@ public: virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes); virtual String get_resource_type(const String &p_path) const; + virtual String get_resource_script_class(const String &p_path) const; virtual ResourceUID::ID get_resource_uid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 9bb2db17ab..b3952e745f 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -43,7 +43,6 @@ Shader::Mode Shader::get_mode() const { void Shader::_dependency_changed() { RenderingServer::get_singleton()->shader_set_code(shader, RenderingServer::get_singleton()->shader_get_code(shader)); - params_cache_dirty = true; emit_changed(); } @@ -93,7 +92,6 @@ void Shader::set_code(const String &p_code) { } RenderingServer::get_singleton()->shader_set_code(shader, pp_code); - params_cache_dirty = true; emit_changed(); } @@ -108,8 +106,6 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr List<PropertyInfo> local; RenderingServer::get_singleton()->get_shader_parameter_list(shader, &local); - params_cache.clear(); - params_cache_dirty = false; for (PropertyInfo &pi : local) { bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP; @@ -120,7 +116,6 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr if (default_textures.has(pi.name)) { //do not show default textures continue; } - params_cache[pi.name] = pi.name; } if (p_params) { //small little hack @@ -176,11 +171,17 @@ bool Shader::is_text_shader() const { return true; } -bool Shader::has_parameter(const StringName &p_name) const { - return params_cache.has(p_name); +void Shader::_update_shader() const { } -void Shader::_update_shader() const { +Array Shader::_get_shader_uniform_list(bool p_get_groups) { + List<PropertyInfo> uniform_list; + get_shader_uniform_list(&uniform_list, p_get_groups); + Array ret; + for (const PropertyInfo &pi : uniform_list) { + ret.push_back(pi.operator Dictionary()); + } + return ret; } void Shader::_bind_methods() { @@ -192,7 +193,7 @@ void Shader::_bind_methods() { ClassDB::bind_method(D_METHOD("set_default_texture_parameter", "name", "texture", "index"), &Shader::set_default_texture_parameter, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_default_texture_parameter", "name", "index"), &Shader::get_default_texture_parameter, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("has_parameter", "name"), &Shader::has_parameter); + ClassDB::bind_method(D_METHOD("get_shader_uniform_list", "get_groups"), &Shader::_get_shader_uniform_list, DEFVAL(false)); ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code"); diff --git a/scene/resources/shader.h b/scene/resources/shader.h index e579c2fca1..75c490e912 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -57,15 +57,12 @@ private: HashSet<Ref<ShaderInclude>> include_dependencies; String code; - // hack the name of performance - // shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make - // conversion fast and save memory. - mutable bool params_cache_dirty = true; - mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param.. HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures; void _dependency_changed(); virtual void _update_shader() const; //used for visual shader + Array _get_shader_uniform_list(bool p_get_groups = false); + protected: static void _bind_methods(); @@ -79,7 +76,6 @@ public: String get_code() const; void get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const; - bool has_parameter(const StringName &p_name) const; void set_default_texture_parameter(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index = 0); Ref<Texture2D> get_default_texture_parameter(const StringName &p_name, int p_index = 0) const; @@ -87,47 +83,6 @@ public: virtual bool is_text_shader() const; - // Finds the shader parameter name for the given property name, which should start with "shader_parameter/". - _FORCE_INLINE_ StringName remap_parameter(const StringName &p_property) const { - if (params_cache_dirty) { - get_shader_uniform_list(nullptr); - } - - String n = p_property; - - // Backwards compatibility with old shader parameter names. - // Note: The if statements are important to make sure we are only replacing text exactly at index 0. - if (n.find("param/") == 0) { - n = n.replace_first("param/", "shader_parameter/"); - } - if (n.find("shader_param/") == 0) { - n = n.replace_first("shader_param/", "shader_parameter/"); - } - if (n.find("shader_uniform/") == 0) { - n = n.replace_first("shader_uniform/", "shader_parameter/"); - } - - { - // Additional backwards compatibility for projects between #62972 and #64092 (about a month of v4.0 development). - // These projects did not have any prefix for shader uniforms due to a bug. - // This code should be removed during beta or rc of 4.0. - const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); - if (E) { - return E->value; - } - } - - if (n.begins_with("shader_parameter/")) { - n = n.replace_first("shader_parameter/", ""); - const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); - if (E) { - return E->value; - } - } - - return StringName(); - } - virtual RID get_rid() const override; Shader(); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index e802e1c2d9..17e92ddfca 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -732,13 +732,13 @@ void SurfaceTool::index() { LocalVector<Vertex> old_vertex_array = vertex_array; vertex_array.clear(); - for (uint32_t i = 0; i < old_vertex_array.size(); i++) { - int *idxptr = indices.getptr(old_vertex_array[i]); + for (const Vertex &vertex : old_vertex_array) { + int *idxptr = indices.getptr(vertex); int idx; if (!idxptr) { idx = indices.size(); - vertex_array.push_back(old_vertex_array[i]); - indices[old_vertex_array[i]] = idx; + vertex_array.push_back(vertex); + indices[vertex] = idx; } else { idx = *idxptr; } @@ -756,9 +756,8 @@ void SurfaceTool::deindex() { LocalVector<Vertex> old_vertex_array = vertex_array; vertex_array.clear(); - for (uint32_t i = 0; i < index_array.size(); i++) { - uint32_t index = index_array[i]; - ERR_FAIL_COND(index >= old_vertex_array.size()); + for (const int &index : index_array) { + ERR_FAIL_COND(uint32_t(index) >= old_vertex_array.size()); vertex_array.push_back(old_vertex_array[index]); } format &= ~Mesh::ARRAY_FORMAT_INDEX; @@ -1000,8 +999,7 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const } int vfrom = vertex_array.size(); - for (uint32_t vi = 0; vi < nvertices.size(); vi++) { - Vertex v = nvertices[vi]; + for (Vertex &v : nvertices) { v.vertex = p_xform.xform(v.vertex); if (nformat & RS::ARRAY_FORMAT_NORMAL) { v.normal = p_xform.basis.xform(v.normal); @@ -1014,8 +1012,8 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const vertex_array.push_back(v); } - for (uint32_t i = 0; i < nindices.size(); i++) { - int dst_index = nindices[i] + vfrom; + for (const int &index : nindices) { + int dst_index = index + vfrom; index_array.push_back(dst_index); } if (index_array.size() % 3) { @@ -1132,9 +1130,9 @@ void SurfaceTool::generate_tangents() { TangentGenerationContextUserData triangle_data; triangle_data.vertices = &vertex_array; - for (uint32_t i = 0; i < vertex_array.size(); i++) { - vertex_array[i].binormal = Vector3(); - vertex_array[i].tangent = Vector3(); + for (Vertex &vertex : vertex_array) { + vertex.binormal = Vector3(); + vertex.tangent = Vector3(); } triangle_data.indices = &index_array; msc.m_pUserData = &triangle_data; @@ -1176,12 +1174,12 @@ void SurfaceTool::generate_normals(bool p_flip) { } } - for (uint32_t vi = 0; vi < vertex_array.size(); vi++) { - Vector3 *lv = vertex_hash.getptr(vertex_array[vi]); + for (Vertex &vertex : vertex_array) { + Vector3 *lv = vertex_hash.getptr(vertex); if (!lv) { - vertex_array[vi].normal = Vector3(); + vertex.normal = Vector3(); } else { - vertex_array[vi].normal = lv->normalized(); + vertex.normal = lv->normalized(); } } diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 6d66c48b78..dfafc7d2bc 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -135,8 +135,8 @@ void TextParagraph::_bind_methods() { void TextParagraph::_shape_lines() { if (lines_dirty) { - for (int i = 0; i < (int)lines_rid.size(); i++) { - TS->free_rid(lines_rid[i]); + for (const RID &line_rid : lines_rid) { + TS->free_rid(line_rid); } lines_rid.clear(); @@ -234,14 +234,14 @@ void TextParagraph::_shape_lines() { } else { // Autowrap disabled. - for (int i = 0; i < (int)lines_rid.size(); i++) { + for (const RID &line_rid : lines_rid) { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); + TS->shaped_text_fit_to_width(line_rid, width, jst_flags); overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); - TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); + TS->shaped_text_overrun_trim_to_width(line_rid, width, overrun_flags); + TS->shaped_text_fit_to_width(line_rid, width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { - TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); + TS->shaped_text_overrun_trim_to_width(line_rid, width, overrun_flags); } } } @@ -268,8 +268,8 @@ RID TextParagraph::get_dropcap_rid() const { void TextParagraph::clear() { _THREAD_SAFE_METHOD_ - for (int i = 0; i < (int)lines_rid.size(); i++) { - TS->free_rid(lines_rid[i]); + for (const RID &line_rid : lines_rid) { + TS->free_rid(line_rid); } lines_rid.clear(); TS->shaped_text_clear(rid); @@ -915,17 +915,17 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { return 0; } } - for (int i = 0; i < (int)lines_rid.size(); i++) { - if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) { - return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x); + for (const RID &line_rid : lines_rid) { + if (TS->shaped_text_get_orientation(line_rid) == TextServer::ORIENTATION_HORIZONTAL) { + if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(line_rid).y)) { + return TS->shaped_text_hit_test_position(line_rid, p_coords.x); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).y; + ofs.y += TS->shaped_text_get_size(line_rid).y; } else { - if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) { - return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y); + if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(line_rid).x)) { + return TS->shaped_text_hit_test_position(line_rid, p_coords.y); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).x; + ofs.y += TS->shaped_text_get_size(line_rid).x; } } return TS->shaped_text_get_range(rid).y; @@ -1027,8 +1027,8 @@ TextParagraph::TextParagraph() { } TextParagraph::~TextParagraph() { - for (int i = 0; i < (int)lines_rid.size(); i++) { - TS->free_rid(lines_rid[i]); + for (const RID &line_rid : lines_rid) { + TS->free_rid(line_rid); } lines_rid.clear(); TS->free_rid(rid); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 94e78fc3aa..b5a68ef14b 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -404,8 +404,8 @@ void TileSet::_update_terrains_cache() { if (terrains_cache_dirty) { // Organizes tiles into structures. per_terrain_pattern_tiles.resize(terrain_sets.size()); - for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) { - per_terrain_pattern_tiles[i].clear(); + for (RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>> &tiles : per_terrain_pattern_tiles) { + tiles.clear(); } for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) { @@ -1342,8 +1342,8 @@ void TileSet::clear_tile_proxies() { int TileSet::add_pattern(Ref<TileMapPattern> p_pattern, int p_index) { ERR_FAIL_COND_V(!p_pattern.is_valid(), -1); ERR_FAIL_COND_V_MSG(p_pattern->is_empty(), -1, "Cannot add an empty pattern to the TileSet."); - for (unsigned int i = 0; i < patterns.size(); i++) { - ERR_FAIL_COND_V_MSG(patterns[i] == p_pattern, -1, "TileSet has already this pattern."); + for (const Ref<TileMapPattern> &pattern : patterns) { + ERR_FAIL_COND_V_MSG(pattern == p_pattern, -1, "TileSet has already this pattern."); } ERR_FAIL_COND_V(p_index > (int)patterns.size(), -1); if (p_index < 0) { @@ -4190,8 +4190,8 @@ real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_at ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); real_t sum = 0.0; - for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) { - sum += tiles[p_atlas_coords].animation_frames_durations[frame]; + for (const real_t &duration : tiles[p_atlas_coords].animation_frames_durations) { + sum += duration; } return sum; } @@ -4573,8 +4573,8 @@ void TileSetAtlasSource::_clear_tiles_outside_texture() { } } - for (unsigned int i = 0; i < to_remove.size(); i++) { - remove_tile(to_remove[i]); + for (const Vector2i &v : to_remove) { + remove_tile(v); } } diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 76fb2d1367..1cbeaae428 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -399,7 +399,6 @@ void VisualShaderNode::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded"); - ADD_SIGNAL(MethodInfo("editor_refresh_request")); BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR); BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index ec89c87bd2..e78d9b924d 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -756,194 +756,157 @@ Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_t } String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - if (source == SOURCE_TEXTURE) { - String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "tex"); - switch (texture_type) { - case TYPE_DATA: - break; - case TYPE_COLOR: - u += " : source_color"; - break; - case TYPE_NORMAL_MAP: - u += " : hint_normal"; - break; - default: - break; - } - return u + ";\n"; - } else if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "screen_tex"); - return u + " : hint_screen_texture;\n"; - } else if (source == SOURCE_DEPTH || source == SOURCE_3D_NORMAL || source == SOURCE_ROUGHNESS) { - String sampler_name = ""; - String hint = " : "; - if (source == SOURCE_DEPTH) { - sampler_name = "depth_tex"; - hint += "hint_depth_texture;\n"; - } else if (source == SOURCE_3D_NORMAL) { - sampler_name = "screen_normal_tex"; - hint += "hint_normal_roughness_texture;\n"; - } else if (source == SOURCE_ROUGHNESS) { - sampler_name = "screen_roughness_tex"; - hint += "hint_normal_roughness_texture;\n"; - } - return "uniform sampler2D " + make_unique_id(p_type, p_id, sampler_name) + hint; + String code; + + switch (source) { + case SOURCE_TEXTURE: { + code += "uniform sampler2D " + make_unique_id(p_type, p_id, "tex"); + switch (texture_type) { + case TYPE_DATA: { + } break; + case TYPE_COLOR: { + code += " : source_color"; + } break; + case TYPE_NORMAL_MAP: { + code += " : hint_normal"; + } break; + default: { + } break; + } + code += ";\n"; + } break; + case SOURCE_SCREEN: { + if ((p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + code += "uniform sampler2D " + make_unique_id(p_type, p_id, "screen_tex") + " : hint_screen_texture;\n"; + } + } break; + case SOURCE_DEPTH: + case SOURCE_3D_NORMAL: + case SOURCE_ROUGHNESS: { + if (p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { + String sampler_name = ""; + String hint = " : "; + if (source == SOURCE_DEPTH) { + sampler_name = "depth_tex"; + hint += "hint_depth_texture;\n"; + } else { + sampler_name = source == SOURCE_ROUGHNESS ? "roughness_tex" : "normal_roughness_tex"; + hint += "hint_normal_roughness_texture;\n"; + } + code += "uniform sampler2D " + make_unique_id(p_type, p_id, sampler_name) + hint; + } + } break; + default: { + } break; } - return String(); + return code; } String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String default_uv; if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - default_uv = "UV"; + if (source == SOURCE_SCREEN) { + default_uv = "SCREEN_UV"; + } else { + default_uv = "UV"; + } } else { default_uv = "vec2(0.0)"; } String code; - if (source == SOURCE_TEXTURE) { - String id = make_unique_id(p_type, p_id, "tex"); - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - return code; - } + String uv = p_input_vars[0].is_empty() ? default_uv : p_input_vars[0]; - if (source == SOURCE_PORT) { - String id = p_input_vars[2]; - if (id.is_empty()) { - code += " " + p_output_vars[0] + " = vec4(0.0);\n"; - } else { - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + switch (source) { + case SOURCE_PORT: + case SOURCE_TEXTURE: { + String id; + if (source == SOURCE_PORT) { + id = p_input_vars[2]; + if (id.is_empty()) { + break; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - } - return code; - } - - if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - String id = make_unique_id(p_type, p_id, "screen_tex"); - if (p_input_vars[0].is_empty() || p_for_preview) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", 0.0);\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + } else { // SOURCE_TEXTURE + id = make_unique_id(p_type, p_id, "tex"); } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", 0.0);\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - return code; - } - - if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(TEXTURE, " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n"; } else { - code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(TEXTURE, " + p_input_vars[0] + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - return code; - } - - if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; - } - return code; - } - - if (source == SOURCE_DEPTH || source == SOURCE_ROUGHNESS) { - if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { - String var_name = ""; - String sampler_name = ""; - - if (source == SOURCE_DEPTH) { - var_name = "_depth"; - sampler_name = "depth_tex"; - } else if (source == SOURCE_ROUGHNESS) { - var_name = "_screen_roughness"; - sampler_name = "screen_roughness_tex"; + return code; + } break; + case SOURCE_SCREEN: { + if ((p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + String id = make_unique_id(p_type, p_id, "screen_tex"); + if (p_input_vars[1].is_empty()) { + code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n"; + } else { + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n"; + } + return code; } + } break; + case SOURCE_2D_NORMAL: + case SOURCE_2D_TEXTURE: { + if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + String id = source == SOURCE_2D_TEXTURE ? "TEXTURE" : "NORMAL_TEXTURE"; - String id = make_unique_id(p_type, p_id, sampler_name); - code += " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " float " + var_name + " = texture(" + id + ", " + default_uv + ").r;\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n"; } else { - code += " float " + var_name + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " float " + var_name + " = texture(" + id + ", " + p_input_vars[0] + ".xy).r;\n"; - } else { - code += " float " + var_name + " = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; + return code; } + } break; + case SOURCE_3D_NORMAL: + case SOURCE_ROUGHNESS: + case SOURCE_DEPTH: { + if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { + String var_name = ""; + String sampler_name = ""; + + switch (source) { + case SOURCE_DEPTH: { + var_name = "_depth"; + sampler_name = "depth_tex"; + } break; + case SOURCE_ROUGHNESS: { + var_name = "_roughness"; + sampler_name = "roughness_tex"; + } break; + case SOURCE_3D_NORMAL: { + var_name = "_normal"; + sampler_name = "normal_roughness_tex"; + } break; + default: { + } break; + } - code += " " + p_output_vars[0] + " = vec4(" + var_name + ", " + var_name + ", " + var_name + ", 1.0);\n"; - code += " }\n"; - return code; - } - } + String id = make_unique_id(p_type, p_id, sampler_name); + String type = source == SOURCE_3D_NORMAL ? "vec3" : "float"; + String components = source == SOURCE_3D_NORMAL ? "rgb" : "r"; - if (source == SOURCE_3D_NORMAL) { - if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { - String id = make_unique_id(p_type, p_id, "screen_normal_tex"); - code += " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. + code += " {\n"; if (p_input_vars[1].is_empty()) { - code += " vec3 _screen_normal = texture(" + id + ", " + default_uv + ").xyz;\n"; + code += " " + type + " " + var_name + " = texture(" + id + ", " + uv + ")." + components + ";\n"; } else { - code += " vec3 _screen_normal = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ").xyz;\n"; + code += " " + type + " " + var_name + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ")." + components + ";\n"; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " vec3 _screen_normal = texture(" + id + ", " + p_input_vars[0] + ".xy).xyz;\n"; - } else { - code += " vec3 _screen_normal = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").xyz;\n"; - } + if (source == SOURCE_3D_NORMAL) { + code += " " + p_output_vars[0] + " = vec4(" + var_name + ", 1.0);\n"; + } else { + code += " " + p_output_vars[0] + " = vec4(" + var_name + ", " + var_name + ", " + var_name + ", 1.0);\n"; + } + code += " }\n"; - code += " " + p_output_vars[0] + " = vec4(_screen_normal, 1.0);\n"; - code += " }\n"; - return code; - } + return code; + } + } break; + default: { + } break; } code += " " + p_output_vars[0] + " = vec4(0.0);\n"; @@ -985,7 +948,6 @@ void VisualShaderNodeTexture::set_source(Source p_source) { } source = p_source; emit_changed(); - emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const { @@ -1029,31 +991,34 @@ String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::T return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); } - if (source == SOURCE_TEXTURE) { - return String(); // all good - } - - if (source == SOURCE_PORT) { - return String(); // all good - } - - if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - return String(); // all good - } - - if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - return String(); // all good - } - - if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM) { - return String(); // all good - } - - if ((source == SOURCE_DEPTH || source == SOURCE_3D_NORMAL || source == SOURCE_ROUGHNESS) && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { - if (get_output_port_for_preview() == 0) { // DEPTH_TEXTURE and NORMAL_ROUGHNESS_TEXTURE are not supported in preview(canvas_item) shader - return RTR("Invalid source for preview."); - } - return String(); // all good + switch (source) { + case SOURCE_TEXTURE: + case SOURCE_PORT: { + return String(); // All good. + } break; + case SOURCE_SCREEN: { + if ((p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + return String(); // All good. + } + } break; + case SOURCE_2D_NORMAL: + case SOURCE_2D_TEXTURE: { + if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + return String(); // All good. + } + } break; + case SOURCE_3D_NORMAL: + case SOURCE_ROUGHNESS: + case SOURCE_DEPTH: { + if (p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { + if (get_output_port_for_preview() == 0) { // Not supported in preview(canvas_item) shader. + return RTR("Invalid source for preview."); + } + return String(); // All good. + } + } break; + default: { + } break; } return RTR("Invalid source for shader."); @@ -1069,7 +1034,7 @@ void VisualShaderNodeTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type); ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort,ScreenNormal,Roughness"), "set_source", "get_source"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort,Normal3D,Roughness"), "set_source", "get_source"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type"); @@ -1321,6 +1286,17 @@ bool VisualShaderNodeSample3D::is_input_port_default(int p_port, Shader::Mode p_ } String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + String code; + String id; + if (source == SOURCE_TEXTURE) { + id = make_unique_id(p_type, p_id, "tex3d"); + } else { // SOURCE_PORT + id = p_input_vars[2]; + if (id.is_empty()) { + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; + return code; + } + } String default_uv; if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { default_uv = "vec3(UV, 0.0)"; @@ -1328,33 +1304,12 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader default_uv = "vec3(0.0)"; } - String code; - if (source == SOURCE_TEXTURE || source == SOURCE_PORT) { - String id; - if (source == SOURCE_TEXTURE) { - id = make_unique_id(p_type, p_id, "tex3d"); - } else { - id = p_input_vars[2]; - } - if (!id.is_empty()) { - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - } else { - code += " " + p_output_vars[0] + " = vec4(0.0);\n"; - } - return code; + String uv = p_input_vars[0].is_empty() ? default_uv : p_input_vars[0]; + if (p_input_vars[1].is_empty()) { + code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n"; + } else { + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n"; } - code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } @@ -1365,7 +1320,6 @@ void VisualShaderNodeSample3D::set_source(Source p_source) { } source = p_source; emit_changed(); - emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeSample3D::Source VisualShaderNodeSample3D::get_source() const { @@ -1387,14 +1341,7 @@ String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader:: if (is_input_port_connected(2) && source != SOURCE_PORT) { return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); } - - if (source == SOURCE_TEXTURE) { - return String(); // all good - } - if (source == SOURCE_PORT) { - return String(); // all good - } - return RTR("Invalid source for shader."); + return String(); } VisualShaderNodeSample3D::VisualShaderNodeSample3D() { @@ -1600,42 +1547,33 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade } String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - String default_uv; - if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - default_uv = "vec3(UV, 0.0)"; - } else { - default_uv = "vec3(0.0)"; - } - String code; String id; + if (source == SOURCE_TEXTURE) { id = make_unique_id(p_type, p_id, "cube"); - } else if (source == SOURCE_PORT) { + } else { // SOURCE_PORT id = p_input_vars[2]; - } else { - return code; + if (id.is_empty()) { + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; + return code; + } } - if (id.is_empty()) { - code += " " + p_output_vars[0] + " = vec4(0.0);\n"; - return code; + String default_uv; + if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { + default_uv = "vec3(UV, 0.0)"; + } else { + default_uv = "vec3(0.0)"; } - if (p_input_vars[0].is_empty()) { // Use UV by default. - - if (p_input_vars[1].is_empty()) { - code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; + String uv = p_input_vars[0].is_empty() ? default_uv : p_input_vars[0]; + if (p_input_vars[1].is_empty()) { + code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n"; } else { - code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n"; } + return code; } @@ -1655,7 +1593,6 @@ void VisualShaderNodeCubemap::set_source(Source p_source) { } source = p_source; emit_changed(); - emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const { @@ -7757,12 +7694,15 @@ bool VisualShaderNodeProximityFade::has_output_port_preview(int p_port) const { return false; } +String VisualShaderNodeProximityFade::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform sampler2D " + make_unique_id(p_type, p_id, "depth_tex") + " : hint_depth_texture;\n"; +} + String VisualShaderNodeProximityFade::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; code += " {\n"; - String proximity_fade_distance = vformat("%s", p_input_vars[0]); - code += " float __depth_tex = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;\n"; + code += " float __depth_tex = texture(" + make_unique_id(p_type, p_id, "depth_tex") + ", SCREEN_UV).r;\n"; if (!RenderingServer::get_singleton()->is_low_end()) { code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; } else { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 8d0f88d83a..e3b101cf84 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -2849,6 +2849,7 @@ public: virtual String get_output_port_name(int p_port) const override; virtual bool has_output_port_preview(int p_port) const override; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; VisualShaderNodeProximityFade(); |