diff options
Diffstat (limited to 'scene')
41 files changed, 1467 insertions, 740 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 33b1c7bcce..fff9c47d4d 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -533,13 +533,13 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout); ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"))); ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"))); - ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"))); ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"))); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index f493d97ceb..41288d646f 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1000,8 +1000,8 @@ void RigidDynamicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "applied_torque"), "set_applied_torque", "get_applied_torque"); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c2f150ce00..b546eaefa6 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,98 +34,6 @@ #include "servers/navigation_server_2d.h" -void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords)); - - size = size.max(p_coords + Vector2i(1, 1)); - pattern[p_coords] = TileMapCell(p_source_id, p_atlas_coords, p_alternative_tile); -} - -bool TileMapPattern::has_cell(const Vector2i &p_coords) const { - return pattern.has(p_coords); -} - -void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { - ERR_FAIL_COND(!pattern.has(p_coords)); - - pattern.erase(p_coords); - if (p_update_size) { - size = Vector2i(); - for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { - size = size.max(E.key + Vector2i(1, 1)); - } - } -} - -int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const { - ERR_FAIL_COND_V(!pattern.has(p_coords), TileSet::INVALID_SOURCE); - - return pattern[p_coords].source_id; -} - -Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const { - ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_ATLAS_COORDS); - - return pattern[p_coords].get_atlas_coords(); -} - -int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const { - ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_TILE_ALTERNATIVE); - - return pattern[p_coords].alternative_tile; -} - -TypedArray<Vector2i> TileMapPattern::get_used_cells() const { - // Returns the cells used in the tilemap. - TypedArray<Vector2i> a; - a.resize(pattern.size()); - int i = 0; - for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { - Vector2i p(E.key.x, E.key.y); - a[i++] = p; - } - - return a; -} - -Vector2i TileMapPattern::get_size() const { - return size; -} - -void TileMapPattern::set_size(const Vector2i &p_size) { - for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { - Vector2i coords = E.key; - if (p_size.x <= coords.x || p_size.y <= coords.y) { - ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords)); - }; - } - - size = p_size; -} - -bool TileMapPattern::is_empty() const { - return pattern.is_empty(); -}; - -void TileMapPattern::clear() { - size = Vector2i(); - pattern.clear(); -}; - -void TileMapPattern::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); - ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell); - ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell); - ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id); - ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords); - ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile); - - ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapPattern::get_used_cells); - ClassDB::bind_method(D_METHOD("get_size"), &TileMapPattern::get_size); - ClassDB::bind_method(D_METHOD("set_size", "size"), &TileMapPattern::set_size); - ClassDB::bind_method(D_METHOD("is_empty"), &TileMapPattern::is_empty); -} - Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) { // Transform to stacked layout. Vector2i output = p_coords; @@ -1788,11 +1696,12 @@ int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bo return E->get().alternative_tile; } -TileMapPattern *TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { +Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); - TileMapPattern *output = memnew(TileMapPattern); + Ref<TileMapPattern> output; + output.instantiate(); if (p_coords_array.is_empty()) { return output; } @@ -1841,7 +1750,7 @@ TileMapPattern *TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_ return output; } -Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern) { +Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern) { ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i()); Vector2i output = p_position_in_tilemap + p_coords_in_pattern; @@ -1864,7 +1773,7 @@ Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_ return output; } -void TileMap::set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern) { +void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(!tile_set.is_valid()); @@ -3076,6 +2985,10 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); + ClassDB::bind_method(D_METHOD("get_pattern", "layer", "coords_array"), &TileMap::get_pattern); + ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern); + ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); @@ -3092,7 +3005,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants); - ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); + ClassDB::bind_method(D_METHOD("_set_tile_data", "layer", "data"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index e5809deabb..e1f38a314c 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -37,51 +37,6 @@ class TileSetAtlasSource; -union TileMapCell { - struct { - int32_t source_id : 16; - int16_t coord_x : 16; - int16_t coord_y : 16; - int32_t alternative_tile : 16; - }; - - uint64_t _u64t; - TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) { - source_id = p_source_id; - set_atlas_coords(p_atlas_coords); - alternative_tile = p_alternative_tile; - } - - Vector2i get_atlas_coords() const { - return Vector2i(coord_x, coord_y); - } - - void set_atlas_coords(const Vector2i &r_coords) { - coord_x = r_coords.x; - coord_y = r_coords.y; - } - - bool operator<(const TileMapCell &p_other) const { - if (source_id == p_other.source_id) { - if (coord_x == p_other.coord_x) { - if (coord_y == p_other.coord_y) { - return alternative_tile < p_other.alternative_tile; - } else { - return coord_y < p_other.coord_y; - } - } else { - return coord_x < p_other.coord_x; - } - } else { - return source_id < p_other.source_id; - } - } - - bool operator!=(const TileMapCell &p_other) const { - return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile); - } -}; - struct TileMapQuadrant { struct CoordsWorldComparator { _ALWAYS_INLINE_ bool operator()(const Vector2i &p_a, const Vector2i &p_b) const { @@ -150,32 +105,6 @@ struct TileMapQuadrant { } }; -class TileMapPattern : public Object { - GDCLASS(TileMapPattern, Object); - - Vector2i size; - Map<Vector2i, TileMapCell> pattern; - -protected: - static void _bind_methods(); - -public: - void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0); - bool has_cell(const Vector2i &p_coords) const; - void remove_cell(const Vector2i &p_coords, bool p_update_size = true); - int get_cell_source_id(const Vector2i &p_coords) const; - Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const; - int get_cell_alternative_tile(const Vector2i &p_coords) const; - - TypedArray<Vector2i> get_used_cells() const; - - Vector2i get_size() const; - void set_size(const Vector2i &p_size); - bool is_empty() const; - - void clear(); -}; - class TileMap : public Node2D { GDCLASS(TileMap, Node2D); @@ -349,9 +278,9 @@ public: Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; - TileMapPattern *get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); - Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern); - void set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern); + Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); + Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern); + void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern); // Not exposed to users TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 7e4c40ca0e..d411525707 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -649,13 +649,13 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_reverb_uniformity", "amount"), &Area3D::set_reverb_uniformity); ClassDB::bind_method(D_METHOD("get_reverb_uniformity"), &Area3D::get_reverb_uniformity); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index c422070480..44a685f506 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -835,7 +835,7 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Log,Disabled"), "set_attenuation_model", "get_attenuation_model"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Logarithmic,Disabled"), "set_attenuation_model", "get_attenuation_model"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); @@ -843,7 +843,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,or_greater"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index afd11482e3..8e89f4fc54 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -215,8 +215,6 @@ void BoneAttachment3D::_transform_changed() { sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans)); } } } @@ -273,8 +271,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } _transform_changed(); @@ -294,8 +290,6 @@ void BoneAttachment3D::set_override_mode(int p_mode) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } override_mode = p_mode; diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index cf681cace8..57b9854e0e 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -47,7 +47,6 @@ class BoneAttachment3D : public Node3D { enum OVERRIDE_MODES { MODE_GLOBAL_POSE, MODE_LOCAL_POSE, - MODE_CUSTOM_POSE }; bool use_external_skeleton = false; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 67f4a88228..c148f95461 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -42,10 +42,9 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { return false; } - Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.find(p_name); + Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); if (E) { - E->get().value = p_value; - RenderingServer::get_singleton()->instance_set_blend_shape_weight(get_instance(), E->get().idx, E->get().value); + set_blend_shape_value(E->get(), p_value); return true; } @@ -67,9 +66,9 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { return false; } - const Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.find(p_name); + const Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); if (E) { - r_ret = E->get().value; + r_ret = get_blend_shape_value(E->get()); return true; } @@ -86,7 +85,7 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { List<String> ls; - for (const KeyValue<StringName, BlendShapeTrack> &E : blend_shape_tracks) { + for (const KeyValue<StringName, int> &E : blend_shape_properties) { ls.push_back(E.key); } @@ -114,25 +113,17 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { mesh = p_mesh; - blend_shape_tracks.clear(); if (mesh.is_valid()) { - for (int i = 0; i < mesh->get_blend_shape_count(); i++) { - BlendShapeTrack mt; - mt.idx = i; - mt.value = 0; - blend_shape_tracks["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = mt; - } - mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); - surface_override_materials.resize(mesh->get_surface_count()); - + _mesh_changed(); set_base(mesh->get_rid()); } else { + blend_shape_tracks.clear(); + blend_shape_properties.clear(); set_base(RID()); + update_gizmos(); } - update_gizmos(); - notify_property_list_changed(); } @@ -140,17 +131,48 @@ Ref<Mesh> MeshInstance3D::get_mesh() const { return mesh; } +int MeshInstance3D::get_blend_shape_count() const { + if (mesh.is_null()) { + return 0; + } + return mesh->get_blend_shape_count(); +} +int MeshInstance3D::find_blend_shape_by_name(const StringName &p_name) { + if (mesh.is_null()) { + return -1; + } + for (int i = 0; i < mesh->get_blend_shape_count(); i++) { + if (mesh->get_blend_shape_name(i) == p_name) { + return i; + } + } + return -1; +} +float MeshInstance3D::get_blend_shape_value(int p_blend_shape) const { + ERR_FAIL_COND_V(mesh.is_null(), 0.0); + ERR_FAIL_INDEX_V(p_blend_shape, (int)blend_shape_tracks.size(), 0); + return blend_shape_tracks[p_blend_shape]; +} +void MeshInstance3D::set_blend_shape_value(int p_blend_shape, float p_value) { + ERR_FAIL_COND(mesh.is_null()); + ERR_FAIL_INDEX(p_blend_shape, (int)blend_shape_tracks.size()); + blend_shape_tracks[p_blend_shape] = p_value; + RenderingServer::get_singleton()->instance_set_blend_shape_weight(get_instance(), p_blend_shape, p_value); +} + void MeshInstance3D::_resolve_skeleton_path() { Ref<SkinReference> new_skin_reference; if (!skeleton_path.is_empty()) { Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node(skeleton_path)); if (skeleton) { - new_skin_reference = skeleton->register_skin(skin_internal); if (skin_internal.is_null()) { + new_skin_reference = skeleton->register_skin(skeleton->create_skin_from_rest_transforms()); //a skin was created for us skin_internal = new_skin_reference->get_skin(); notify_property_list_changed(); + } else { + new_skin_reference = skeleton->register_skin(skin_internal); } } } @@ -355,6 +377,19 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { void MeshInstance3D::_mesh_changed() { ERR_FAIL_COND(mesh.is_null()); surface_override_materials.resize(mesh->get_surface_count()); + + uint32_t initialize_bs_from = blend_shape_tracks.size(); + blend_shape_tracks.resize(mesh->get_blend_shape_count()); + + for (uint32_t i = 0; i < blend_shape_tracks.size(); i++) { + blend_shape_properties["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = i; + if (i < initialize_bs_from) { + set_blend_shape_value(i, blend_shape_tracks[i]); + } else { + set_blend_shape_value(i, 0); + } + } + update_gizmos(); } @@ -457,6 +492,11 @@ void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("create_multiple_convex_collisions"), &MeshInstance3D::create_multiple_convex_collisions); ClassDB::set_method_flags("MeshInstance3D", "create_multiple_convex_collisions", METHOD_FLAGS_DEFAULT); + ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &MeshInstance3D::get_blend_shape_count); + ClassDB::bind_method(D_METHOD("find_blend_shape_by_name", "name"), &MeshInstance3D::find_blend_shape_by_name); + ClassDB::bind_method(D_METHOD("get_blend_shape_value", "blend_shape_idx"), &MeshInstance3D::get_blend_shape_value); + ClassDB::bind_method(D_METHOD("set_blend_shape_value", "blend_shape_idx", "value"), &MeshInstance3D::set_blend_shape_value); + ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents); ClassDB::set_method_flags("MeshInstance3D", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index beb7f6cf95..8f21726601 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -31,8 +31,8 @@ #ifndef MESH_INSTANCE_H #define MESH_INSTANCE_H +#include "core/templates/local_vector.h" #include "scene/3d/visual_instance_3d.h" - class Skin; class SkinReference; @@ -46,12 +46,8 @@ protected: Ref<SkinReference> skin_ref; NodePath skeleton_path = NodePath(".."); - struct BlendShapeTrack { - int idx = 0; - float value = 0.0; - }; - - Map<StringName, BlendShapeTrack> blend_shape_tracks; + LocalVector<float> blend_shape_tracks; + Map<StringName, int> blend_shape_properties; Vector<Ref<Material>> surface_override_materials; void _mesh_changed(); @@ -75,6 +71,11 @@ public: void set_skeleton_path(const NodePath &p_skeleton); NodePath get_skeleton_path(); + int get_blend_shape_count() const; + int find_blend_shape_by_name(const StringName &p_name); + float get_blend_shape_value(int p_blend_shape) const; + void set_blend_shape_value(int p_blend_shape, float p_value); + int get_surface_override_material_count() const; void set_surface_override_material(int p_surface, const Ref<Material> &p_material); Ref<Material> get_surface_override_material(int p_surface) const; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 272a06bce4..c96204cf60 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -371,6 +371,9 @@ void Node3D::update_gizmos() { if (data.gizmos.is_empty()) { return; } + if (data.gizmos_dirty) { + return; + } data.gizmos_dirty = true; MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_update_gizmos)); #endif @@ -467,6 +470,7 @@ Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const { void Node3D::_update_gizmos() { #ifdef TOOLS_ENABLED if (data.gizmos_disabled || !is_inside_world() || !data.gizmos_dirty) { + data.gizmos_dirty = false; return; } data.gizmos_dirty = false; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 976bff4fbc..8cb348d6e3 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1040,8 +1040,8 @@ void RigidDynamicBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 79504b10bb..fbc3767151 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -171,6 +171,45 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); #endif //_3D_DISABLED + + for (PropertyInfo &E : *p_list) { + _validate_property(E); + } +} + +void Skeleton3D::_validate_property(PropertyInfo &property) const { + PackedStringArray spr = property.name.split("/"); + if (spr.size() == 3 && spr[0] == "bones") { + if (spr[2] == "rest") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (is_show_rest_only()) { + if (spr[2] == "enabled") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "rotation") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "scale") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } else if (!is_bone_enabled(spr[1].to_int())) { + if (spr[2] == "position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "rotation") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "scale") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } + } + + Node3D::_validate_property(property); } void Skeleton3D::_update_process_order() { @@ -557,18 +596,6 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { _make_dirty(); } -void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].disable_rest = p_disable; -} - -bool Skeleton3D::is_bone_rest_disabled(int p_bone) const { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, false); - return bones[p_bone].disable_rest; -} - int Skeleton3D::get_bone_parent(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, -1); @@ -723,23 +750,6 @@ Transform3D Skeleton3D::get_bone_pose(int p_bone) const { return bones[p_bone].pose_cache; } -void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - //ERR_FAIL_COND( !is_inside_scene() ); - - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D()); - bones.write[p_bone].custom_pose = p_custom_pose; - - _make_dirty(); -} - -Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].custom_pose; -} - void Skeleton3D::_make_dirty() { if (dirty) { return; @@ -938,59 +948,57 @@ void Skeleton3D::_skin_changed() { _make_dirty(); } -Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - if (E->get()->skin == p_skin) { - return Ref<SkinReference>(E->get()); +Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { + Ref<Skin> skin; + + skin.instantiate(); + skin->set_bind_count(bones.size()); + _update_process_order(); // Just in case. + + // Pose changed, rebuild cache of inverses. + const Bone *bonesptr = bones.ptr(); + int len = bones.size(); + + // Calculate global rests and invert them. + LocalVector<int> bones_to_process; + bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + const Bone &b = bonesptr[current_bone_idx]; + bones_to_process.erase(current_bone_idx); + LocalVector<int> child_bones_vector; + child_bones_vector = get_bone_children(current_bone_idx); + int child_bones_size = child_bones_vector.size(); + if (b.parent < 0) { + skin->set_bind_pose(current_bone_idx, b.rest); + } + for (int i = 0; i < child_bones_size; i++) { + int child_bone_idx = child_bones_vector[i]; + const Bone &cb = bonesptr[child_bone_idx]; + skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); + // Add the bone's children to the list of bones to be processed. + bones_to_process.push_back(child_bones_vector[i]); } } - Ref<Skin> skin = p_skin; - - if (skin.is_null()) { - // Need to create one from existing code, this is for compatibility only - // when skeletons did not support skins. It is also used by gizmo - // to display the skeleton. - - skin.instantiate(); - skin->set_bind_count(bones.size()); - _update_process_order(); // Just in case. - - // Pose changed, rebuild cache of inverses. - const Bone *bonesptr = bones.ptr(); - int len = bones.size(); - - // Calculate global rests and invert them. - LocalVector<int> bones_to_process; - bones_to_process = get_parentless_bones(); - while (bones_to_process.size() > 0) { - int current_bone_idx = bones_to_process[0]; - const Bone &b = bonesptr[current_bone_idx]; - bones_to_process.erase(current_bone_idx); - LocalVector<int> child_bones_vector; - child_bones_vector = get_bone_children(current_bone_idx); - int child_bones_size = child_bones_vector.size(); - if (b.parent < 0) { - skin->set_bind_pose(current_bone_idx, b.rest); - } - for (int i = 0; i < child_bones_size; i++) { - int child_bone_idx = child_bones_vector[i]; - const Bone &cb = bonesptr[child_bone_idx]; - skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); - // Add the bone's children to the list of bones to be processed. - bones_to_process.push_back(child_bones_vector[i]); - } - } + for (int i = 0; i < len; i++) { + // The inverse is what is actually required. + skin->set_bind_bone(i, i); + skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); + } + + return skin; +} - for (int i = 0; i < len; i++) { - // The inverse is what is actually required. - skin->set_bind_bone(i, i); - skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); +Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { + ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); + + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + if (E->get()->skin == p_skin) { + return Ref<SkinReference>(E->get()); } } - ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); - Ref<SkinReference> skin_ref; skin_ref.instantiate(); @@ -998,11 +1006,11 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_ref->bind_count = 0; skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create(); skin_ref->skeleton_node = this; - skin_ref->skin = skin; + skin_ref->skin = p_skin; skin_bindings.insert(skin_ref.operator->()); - skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); + skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); _make_dirty(); // Skin needs to be updated, so update skeleton. @@ -1038,61 +1046,33 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; - if (b.disable_rest) { - if (bone_enabled) { - b.update_pose_cache(); - Transform3D pose = b.pose_cache; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = pose; - b.pose_global_no_override = b.pose_global; - } + if (bone_enabled) { + b.update_pose_cache(); + Transform3D pose = b.pose_cache; + + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = Transform3D(); - b.pose_global_no_override = b.pose_global; - } + b.pose_global = pose; + b.pose_global_no_override = b.pose_global; } - } else { - if (bone_enabled) { - b.update_pose_cache(); - Transform3D pose = b.pose_cache; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest * pose; - b.pose_global_no_override = b.pose_global; - } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest; - b.pose_global_no_override = b.pose_global; - } + b.pose_global = b.rest; + b.pose_global_no_override = b.pose_global; } } if (b.local_pose_override_amount >= CMP_EPSILON) { Transform3D override_local_pose; if (b.parent >= 0) { - override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override); + override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override; } else { - override_local_pose = (b.rest * b.local_pose_override); + override_local_pose = b.local_pose_override; } b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount); } @@ -1133,8 +1113,8 @@ Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform.affine_inverse() * p_global_pose; + Transform3D conversion_transform = get_bone_global_pose(parent_bone_idx).affine_inverse(); + return conversion_transform * p_global_pose; } else { return p_global_pose; } @@ -1145,8 +1125,7 @@ Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform * p_local_pose; + return bones[parent_bone_idx].pose_global * p_local_pose; } else { return p_local_pose; } @@ -1236,13 +1215,11 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); + ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms); ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin); ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton3D::localize_rests); - ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest); - ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled); - ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose); @@ -1267,9 +1244,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override); - ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); - ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); - ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index aacee3da4e..f7bc3df94e 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -76,7 +76,6 @@ private: bool enabled; int parent; - bool disable_rest = false; Transform3D rest; _FORCE_INLINE_ void update_pose_cache() { @@ -95,9 +94,6 @@ private: Transform3D pose_global; Transform3D pose_global_no_override; - bool custom_pose_enable = false; - Transform3D custom_pose; - real_t global_pose_override_amount = 0.0; bool global_pose_override_reset = false; Transform3D global_pose_override; @@ -119,8 +115,6 @@ private: Bone() { parent = -1; enabled = true; - disable_rest = false; - custom_pose_enable = false; global_pose_override_amount = 0; global_pose_override_reset = false; #ifndef _3D_DISABLED @@ -159,6 +153,7 @@ protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const override; void _notification(int p_what); static void _bind_methods(); @@ -199,9 +194,6 @@ public: void remove_bone_child(int p_bone, int p_child); Vector<int> get_parentless_bones(); - void set_bone_disable_rest(int p_bone, bool p_disable); - bool is_bone_rest_disabled(int p_bone) const; - int get_bone_count() const; void set_bone_rest(int p_bone, const Transform3D &p_rest); @@ -228,9 +220,6 @@ public: Quaternion get_bone_pose_rotation(int p_bone) const; Vector3 get_bone_pose_scale(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); - Transform3D get_bone_custom_pose(int p_bone) const; - void clear_bones_global_pose_override(); Transform3D get_bone_global_pose_override(int p_bone) const; void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); @@ -241,6 +230,8 @@ public: void localize_rests(); // used for loaders and tools + Ref<Skin> create_skin_from_rest_transforms(); + Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); void force_update_all_dirty_bones(); diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index ebfb58e9fe..9dbee58f0e 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -47,13 +47,45 @@ void XRCamera3D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { // need to find our XROrigin3D parent and let it know we're no longer its camera! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin != nullptr) { - origin->clear_tracked_camera_if(this); + if (origin != nullptr && origin->get_tracked_camera() == this) { + origin->set_tracked_camera(nullptr); } }; break; }; }; +void XRCamera3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { + if (p_tracker_name == tracker_name) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + tracker = xr_server->get_tracker(p_tracker_name); + if (tracker.is_valid()) { + tracker->connect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); + + Ref<XRPose> pose = tracker->get_pose(pose_name); + if (pose.is_valid()) { + set_transform(pose->get_adjusted_transform()); + } + } + } +} + +void XRCamera3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { + if (p_tracker_name == tracker_name) { + if (tracker.is_valid()) { + tracker->disconnect("pose_changed", callable_mp(this, &XRCamera3D::_pose_changed)); + } + tracker.unref(); + } +} + +void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) { + if (p_pose->get_name() == pose_name) { + set_transform(p_pose->get_adjusted_transform()); + } +} + TypedArray<String> XRCamera3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); @@ -172,195 +204,215 @@ Vector<Plane> XRCamera3D::get_frustum() const { return cm.get_projection_planes(get_camera_transform()); }; -//////////////////////////////////////////////////////////////////////////////////////////////////// +XRCamera3D::XRCamera3D() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); -void XRController3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); - }; break; - case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - }; break; - case NOTIFICATION_INTERNAL_PROCESS: { - // get our XRServer - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - // find the tracker for our controller - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (!tracker.is_valid()) { - // this controller is currently turned off - is_active = false; - button_states = 0; - } else { - is_active = true; - set_transform(tracker->get_transform(true)); - - int joy_id = tracker->get_joy_id(); - if (joy_id >= 0) { - int mask = 1; - // check button states - for (int i = 0; i < 16; i++) { - bool was_pressed = (button_states & mask) == mask; - bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)i); - - if (!was_pressed && is_pressed) { - emit_signal(SNAME("button_pressed"), i); - button_states += mask; - } else if (was_pressed && !is_pressed) { - emit_signal(SNAME("button_released"), i); - button_states -= mask; - }; - - mask = mask << 1; - }; - - } else { - button_states = 0; - }; - - // check for an updated mesh - Ref<Mesh> trackerMesh = tracker->get_mesh(); - if (mesh != trackerMesh) { - mesh = trackerMesh; - emit_signal(SNAME("mesh_updated"), mesh); - } - }; - }; break; - default: - break; - }; -}; + xr_server->connect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker)); + xr_server->connect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker)); + xr_server->connect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker)); +} -void XRController3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_controller_id", "controller_id"), &XRController3D::set_controller_id); - ClassDB::bind_method(D_METHOD("get_controller_id"), &XRController3D::get_controller_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_controller_id", "get_controller_id"); - ClassDB::bind_method(D_METHOD("get_controller_name"), &XRController3D::get_controller_name); +XRCamera3D::~XRCamera3D() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); - // passthroughs to information about our related joystick - ClassDB::bind_method(D_METHOD("get_joystick_id"), &XRController3D::get_joystick_id); - ClassDB::bind_method(D_METHOD("is_button_pressed", "button"), &XRController3D::is_button_pressed); - ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &XRController3D::get_joystick_axis); + xr_server->disconnect("tracker_added", callable_mp(this, &XRCamera3D::_changed_tracker)); + xr_server->disconnect("tracker_updated", callable_mp(this, &XRCamera3D::_changed_tracker)); + xr_server->disconnect("tracker_removed", callable_mp(this, &XRCamera3D::_removed_tracker)); +} - ClassDB::bind_method(D_METHOD("get_is_active"), &XRController3D::get_is_active); - ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); +//////////////////////////////////////////////////////////////////////////////////////////////////// +// XRNode3D is a node that has it's transform updated by an XRPositionalTracker. +// Note that trackers are only available in runtime and only after an XRInterface registers one. +// So we bind by name and as long as a tracker isn't available, our node remains inactive. - ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); - ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); - ADD_PROPERTY_DEFAULT("rumble", 0.0); +void XRNode3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_tracker", "tracker_name"), &XRNode3D::set_tracker); + ClassDB::bind_method(D_METHOD("get_tracker"), &XRNode3D::get_tracker); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tracker", PROPERTY_HINT_ENUM_SUGGESTION), "set_tracker", "get_tracker"); - ClassDB::bind_method(D_METHOD("get_mesh"), &XRController3D::get_mesh); + ClassDB::bind_method(D_METHOD("set_pose_name", "pose"), &XRNode3D::set_pose_name); + ClassDB::bind_method(D_METHOD("get_pose_name"), &XRNode3D::get_pose_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "pose", PROPERTY_HINT_ENUM_SUGGESTION), "set_pose_name", "get_pose_name"); - ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); + ClassDB::bind_method(D_METHOD("get_is_active"), &XRNode3D::get_is_active); + ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRNode3D::get_has_tracking_data); + ClassDB::bind_method(D_METHOD("get_pose"), &XRNode3D::get_pose); + ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse); }; -void XRController3D::set_controller_id(int p_controller_id) { - // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. - // Note that setting this to 0 means this node is not bound to a controller yet. - controller_id = p_controller_id; - update_configuration_warnings(); -}; +void XRNode3D::_validate_property(PropertyInfo &property) const { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); -int XRController3D::get_controller_id() const { - return controller_id; -}; + if (property.name == "tracker") { + PackedStringArray names = xr_server->get_suggested_tracker_names(); + String hint_string; + for (const String &name : names) { + hint_string += name + ","; + } + property.hint_string = hint_string; + } else if (property.name == "pose") { + PackedStringArray names = xr_server->get_suggested_pose_names(tracker_name); + String hint_string; + for (const String &name : names) { + hint_string += name + ","; + } + property.hint_string = hint_string; + } +} -String XRController3D::get_controller_name() const { - // get our XRServer - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, String()); +void XRNode3D::set_tracker(const StringName p_tracker_name) { + if (tracker.is_valid() && tracker->get_tracker_name() == p_tracker_name) { + // didn't change + return; + } - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (!tracker.is_valid()) { - return String("Not connected"); - }; + // just in case + _unbind_tracker(); - return tracker->get_tracker_name(); -}; + // copy the name + tracker_name = p_tracker_name; + pose_name = "default"; -int XRController3D::get_joystick_id() const { - // get our XRServer - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0); + // see if it's already available + _bind_tracker(); - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (!tracker.is_valid()) { - // No tracker? no joystick id... (0 is our first joystick) - return -1; - }; + update_configuration_warnings(); + notify_property_list_changed(); +} - return tracker->get_joy_id(); -}; +StringName XRNode3D::get_tracker() const { + return tracker_name; +} -bool XRController3D::is_button_pressed(int p_button) const { - int joy_id = get_joystick_id(); - if (joy_id == -1) { - return false; - }; +void XRNode3D::set_pose_name(const StringName p_pose_name) { + pose_name = p_pose_name; - return Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)p_button); -}; + // Update pose if we are bound to a tracker with a valid pose + Ref<XRPose> pose = get_pose(); + if (pose.is_valid()) { + set_transform(pose->get_adjusted_transform()); + } +} -float XRController3D::get_joystick_axis(int p_axis) const { - int joy_id = get_joystick_id(); - if (joy_id == -1) { - return 0.0; - }; +StringName XRNode3D::get_pose_name() const { + return pose_name; +} - return Input::get_singleton()->get_joy_axis(joy_id, (JoyAxis)p_axis); -}; +bool XRNode3D::get_is_active() const { + if (tracker.is_null()) { + return false; + } else if (!tracker->has_pose(pose_name)) { + return false; + } else { + return true; + } +} -real_t XRController3D::get_rumble() const { - // get our XRServer +bool XRNode3D::get_has_tracking_data() const { + if (tracker.is_null()) { + return false; + } else if (!tracker->has_pose(pose_name)) { + return false; + } else { + return tracker->get_pose(pose_name)->get_has_tracking_data(); + } +} + +void XRNode3D::trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) { + // TODO need to link trackers to the interface that registered them so we can call this on the correct interface. + // For now this works fine as in 99% of the cases we only have our primary interface active XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0.0); + if (xr_server != nullptr) { + Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_valid()) { + xr_interface->trigger_haptic_pulse(p_action_name, tracker_name, p_frequency, p_amplitude, p_duration_sec, p_delay_sec); + } + } +} - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (!tracker.is_valid()) { - return 0.0; - }; +Ref<XRPose> XRNode3D::get_pose() { + if (tracker.is_valid()) { + return tracker->get_pose(pose_name); + } else { + return Ref<XRPose>(); + } +} - return tracker->get_rumble(); -}; +void XRNode3D::_bind_tracker() { + ERR_FAIL_COND_MSG(tracker.is_valid(), "Unbind the current tracker first"); -void XRController3D::set_rumble(real_t p_rumble) { - // get our XRServer XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); + if (xr_server != nullptr) { + tracker = xr_server->get_tracker(tracker_name); + if (tracker.is_null()) { + // It is possible and valid if the tracker isn't available (yet), in this case we just exit + return; + } + + tracker->connect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed)); + + Ref<XRPose> pose = get_pose(); + if (pose.is_valid()) { + set_transform(pose->get_adjusted_transform()); + } + } +} - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); +void XRNode3D::_unbind_tracker() { if (tracker.is_valid()) { - tracker->set_rumble(p_rumble); - }; -}; + tracker->disconnect("pose_changed", callable_mp(this, &XRNode3D::_pose_changed)); -Ref<Mesh> XRController3D::get_mesh() const { - return mesh; + tracker.unref(); + } } -bool XRController3D::get_is_active() const { - return is_active; -}; +void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { + if (p_tracker_name == p_tracker_name) { + // just in case unref our current tracker + _unbind_tracker(); -XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { - // get our XRServer + // get our new tracker + _bind_tracker(); + } +} + +void XRNode3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { + if (p_tracker_name == p_tracker_name) { + // unref our tracker, it's no longer available + _unbind_tracker(); + } +} + +void XRNode3D::_pose_changed(const Ref<XRPose> &p_pose) { + if (p_pose.is_valid() && p_pose->get_name() == pose_name) { + set_transform(p_pose->get_adjusted_transform()); + } +} + +XRNode3D::XRNode3D() { XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, XRPositionalTracker::TRACKER_HAND_UNKNOWN); + ERR_FAIL_NULL(xr_server); - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (!tracker.is_valid()) { - return XRPositionalTracker::TRACKER_HAND_UNKNOWN; - }; + xr_server->connect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker)); + xr_server->connect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker)); + xr_server->connect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker)); +} - return tracker->get_tracker_hand(); -}; +XRNode3D::~XRNode3D() { + _unbind_tracker(); + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + xr_server->disconnect("tracker_added", callable_mp(this, &XRNode3D::_changed_tracker)); + xr_server->disconnect("tracker_updated", callable_mp(this, &XRNode3D::_changed_tracker)); + xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker)); +} -TypedArray<String> XRController3D::get_configuration_warnings() const { +TypedArray<String> XRNode3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { @@ -370,142 +422,179 @@ TypedArray<String> XRController3D::get_configuration_warnings() const { warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent.")); } - if (controller_id == 0) { - warnings.push_back(TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.")); + if (tracker_name == "") { + warnings.push_back(TTR("No tracker name is set.")); + } + + if (pose_name == "") { + warnings.push_back(TTR("No pose is set.")); } } return warnings; -}; +} //////////////////////////////////////////////////////////////////////////////////////////////////// -void XRAnchor3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); - }; break; - case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - }; break; - case NOTIFICATION_INTERNAL_PROCESS: { - // get our XRServer - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - // find the tracker for our anchor - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (!tracker.is_valid()) { - // this anchor is currently not available - is_active = false; - } else { - is_active = true; - Transform3D transform; - - // we'll need our world_scale - real_t world_scale = xr_server->get_world_scale(); - - // get our info from our tracker - transform.basis = tracker->get_orientation(); - transform.origin = tracker->get_position(); // <-- already adjusted to world scale - - // our basis is scaled to the size of the plane the anchor is tracking - // extract the size from our basis and reset the scale - size = transform.basis.get_scale() * world_scale; - transform.basis.orthonormalize(); - - // apply our reference frame and set our transform - set_transform(xr_server->get_reference_frame() * transform); - - // check for an updated mesh - Ref<Mesh> trackerMesh = tracker->get_mesh(); - if (mesh != trackerMesh) { - mesh = trackerMesh; - emit_signal(SNAME("mesh_updated"), mesh); - } - }; - }; break; - default: - break; - }; -}; - -void XRAnchor3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_anchor_id", "anchor_id"), &XRAnchor3D::set_anchor_id); - ClassDB::bind_method(D_METHOD("get_anchor_id"), &XRAnchor3D::get_anchor_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_anchor_id", "get_anchor_id"); - ClassDB::bind_method(D_METHOD("get_anchor_name"), &XRAnchor3D::get_anchor_name); +void XRController3D::_bind_methods() { + // passthroughs to information about our related joystick + ClassDB::bind_method(D_METHOD("is_button_pressed", "name"), &XRController3D::is_button_pressed); + ClassDB::bind_method(D_METHOD("get_value", "name"), &XRController3D::get_value); + ClassDB::bind_method(D_METHOD("get_axis", "name"), &XRController3D::get_axis); - ClassDB::bind_method(D_METHOD("get_is_active"), &XRAnchor3D::get_is_active); - ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size); + ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); - ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane); + ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); + ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); + ADD_PROPERTY_DEFAULT("rumble", 0.0); - ClassDB::bind_method(D_METHOD("get_mesh"), &XRAnchor3D::get_mesh); - ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); + ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); + ADD_SIGNAL(MethodInfo("input_axis_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value"))); }; -void XRAnchor3D::set_anchor_id(int p_anchor_id) { - // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. - // Note that setting this to 0 means this node is not bound to an anchor yet. - anchor_id = p_anchor_id; - update_configuration_warnings(); -}; +void XRController3D::_bind_tracker() { + XRNode3D::_bind_tracker(); + if (tracker.is_valid()) { + // bind to input signals + tracker->connect("button_pressed", callable_mp(this, &XRController3D::_button_pressed)); + tracker->connect("button_released", callable_mp(this, &XRController3D::_button_released)); + tracker->connect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed)); + tracker->connect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed)); + } +} -int XRAnchor3D::get_anchor_id() const { - return anchor_id; -}; +void XRController3D::_unbind_tracker() { + if (tracker.is_valid()) { + // unbind input signals + tracker->disconnect("button_pressed", callable_mp(this, &XRController3D::_button_pressed)); + tracker->disconnect("button_released", callable_mp(this, &XRController3D::_button_released)); + tracker->disconnect("input_value_changed", callable_mp(this, &XRController3D::_input_value_changed)); + tracker->disconnect("input_axis_changed", callable_mp(this, &XRController3D::_input_axis_changed)); + } -Vector3 XRAnchor3D::get_size() const { - return size; -}; + XRNode3D::_unbind_tracker(); +} -String XRAnchor3D::get_anchor_name() const { - // get our XRServer - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, String()); +void XRController3D::_button_pressed(const String &p_name) { + // just pass it on... + emit_signal("button_pressed", p_name); +} - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (!tracker.is_valid()) { - return String("Not connected"); - }; +void XRController3D::_button_released(const String &p_name) { + // just pass it on... + emit_signal("button_released", p_name); +} - return tracker->get_tracker_name(); -}; +void XRController3D::_input_value_changed(const String &p_name, float p_value) { + // just pass it on... + emit_signal("input_value_changed", p_name, p_value); +} -bool XRAnchor3D::get_is_active() const { - return is_active; -}; +void XRController3D::_input_axis_changed(const String &p_name, Vector2 p_value) { + // just pass it on... + emit_signal("input_axis_changed", p_name, p_value); +} -TypedArray<String> XRAnchor3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +bool XRController3D::is_button_pressed(const StringName &p_name) const { + if (tracker.is_valid()) { + // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type + bool pressed = tracker->get_input(p_name); + return pressed; + } else { + return false; + } +} - if (is_visible() && is_inside_tree()) { - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - warnings.push_back(TTR("XRAnchor3D must have an XROrigin3D node as its parent.")); - } +float XRController3D::get_value(const StringName &p_name) const { + if (tracker.is_valid()) { + // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert + Variant input = tracker->get_input(p_name); + switch (input.get_type()) { + case Variant::BOOL: { + bool value = input; + return value ? 1.0 : 0.0; + } break; + case Variant::FLOAT: { + float value = input; + return value; + } break; + default: + return 0.0; + }; + } else { + return 0.0; + } +} - if (anchor_id == 0) { - warnings.push_back(TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.")); +Vector2 XRController3D::get_axis(const StringName &p_name) const { + if (tracker.is_valid()) { + // Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert + Variant input = tracker->get_input(p_name); + switch (input.get_type()) { + case Variant::BOOL: { + bool value = input; + return Vector2(value ? 1.0 : 0.0, 0.0); + } break; + case Variant::FLOAT: { + float value = input; + return Vector2(value, 0.0); + } break; + case Variant::VECTOR2: { + Vector2 axis = input; + return axis; + } + default: + return Vector2(); } + } else { + return Vector2(); } +} - return warnings; -}; +real_t XRController3D::get_rumble() const { + if (!tracker.is_valid()) { + return 0.0; + } + + return tracker->get_rumble(); +} + +void XRController3D::set_rumble(real_t p_rumble) { + if (tracker.is_valid()) { + tracker->set_rumble(p_rumble); + } +} + +XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { + // get our XRServer + if (!tracker.is_valid()) { + return XRPositionalTracker::TRACKER_HAND_UNKNOWN; + } + + return tracker->get_tracker_hand(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void XRAnchor3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size); + ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane); +} + +Vector3 XRAnchor3D::get_size() const { + return size; +} Plane XRAnchor3D::get_plane() const { Vector3 location = get_position(); Basis orientation = get_transform().basis; - Plane plane(location, orientation.get_axis(1).normalized()); + Plane plane(orientation.get_axis(1).normalized(), location); return plane; -}; - -Ref<Mesh> XRAnchor3D::get_mesh() const { - return mesh; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -525,23 +614,21 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const { } return warnings; -}; +} void XROrigin3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale); ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); -}; +} void XROrigin3D::set_tracked_camera(XRCamera3D *p_tracked_camera) { tracked_camera = p_tracked_camera; -}; +} -void XROrigin3D::clear_tracked_camera_if(XRCamera3D *p_tracked_camera) { - if (tracked_camera == p_tracked_camera) { - tracked_camera = nullptr; - }; -}; +XRCamera3D *XROrigin3D::get_tracked_camera() const { + return tracked_camera; +} real_t XROrigin3D::get_world_scale() const { // get our XRServer @@ -549,7 +636,7 @@ real_t XROrigin3D::get_world_scale() const { ERR_FAIL_NULL_V(xr_server, 1.0); return xr_server->get_world_scale(); -}; +} void XROrigin3D::set_world_scale(real_t p_world_scale) { // get our XRServer @@ -557,7 +644,7 @@ void XROrigin3D::set_world_scale(real_t p_world_scale) { ERR_FAIL_NULL(xr_server); xr_server->set_world_scale(p_world_scale); -}; +} void XROrigin3D::_notification(int p_what) { // get our XRServer @@ -596,4 +683,4 @@ void XROrigin3D::_notification(int p_what) { interface->notification(p_what); } } -}; +} diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 6e54ff83d7..5e7d06093d 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -45,8 +45,18 @@ class XRCamera3D : public Camera3D { GDCLASS(XRCamera3D, Camera3D); protected: + // The name and pose for our HMD tracker is currently the only hardcoded bit. + // If we ever are able to support multiple HMDs we may need to make this settable. + StringName tracker_name = "head"; + StringName pose_name = "default"; + Ref<XRPositionalTracker> tracker; + void _notification(int p_what); + void _changed_tracker(const StringName p_tracker_name, int p_tracker_type); + void _removed_tracker(const StringName p_tracker_name, int p_tracker_type); + void _pose_changed(const Ref<XRPose> &p_pose); + public: TypedArray<String> get_configuration_warnings() const override; @@ -55,48 +65,88 @@ public: virtual Vector3 project_position(const Point2 &p_point, real_t p_z_depth) const override; virtual Vector<Plane> get_frustum() const override; - XRCamera3D() {} - ~XRCamera3D() {} + XRCamera3D(); + ~XRCamera3D(); }; /* - XRController3D is a helper node that automatically updates its position based on tracker data. + XRNode3D is a helper node that implements binding to a tracker. It must be a child node of our XROrigin node */ -class XRController3D : public Node3D { - GDCLASS(XRController3D, Node3D); +class XRNode3D : public Node3D { + GDCLASS(XRNode3D, Node3D); private: - int controller_id = 1; + StringName tracker_name; + StringName pose_name = "default"; bool is_active = true; - int button_states = 0; - Ref<Mesh> mesh; protected: - void _notification(int p_what); + Ref<XRPositionalTracker> tracker; + static void _bind_methods(); -public: - void set_controller_id(int p_controller_id); - int get_controller_id() const; - String get_controller_name() const; + virtual void _bind_tracker(); + virtual void _unbind_tracker(); + void _changed_tracker(const StringName p_tracker_name, int p_tracker_type); + void _removed_tracker(const StringName p_tracker_name, int p_tracker_type); - int get_joystick_id() const; - bool is_button_pressed(int p_button) const; - float get_joystick_axis(int p_axis) const; + void _pose_changed(const Ref<XRPose> &p_pose); - real_t get_rumble() const; - void set_rumble(real_t p_rumble); +public: + virtual void _validate_property(PropertyInfo &property) const override; + void set_tracker(const StringName p_tracker_name); + StringName get_tracker() const; + + void set_pose_name(const StringName p_pose); + StringName get_pose_name() const; bool get_is_active() const; - XRPositionalTracker::TrackerHand get_tracker_hand() const; + bool get_has_tracking_data() const; - Ref<Mesh> get_mesh() const; + void trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec = 0); + + Ref<XRPose> get_pose(); TypedArray<String> get_configuration_warnings() const override; + XRNode3D(); + ~XRNode3D(); +}; + +/* + XRController3D is a helper node that automatically updates its position based on tracker data. + + It must be a child node of our XROrigin node +*/ + +class XRController3D : public XRNode3D { + GDCLASS(XRController3D, XRNode3D); + +private: +protected: + static void _bind_methods(); + + virtual void _bind_tracker() override; + virtual void _unbind_tracker() override; + + void _button_pressed(const String &p_name); + void _button_released(const String &p_name); + void _input_value_changed(const String &p_name, float p_value); + void _input_axis_changed(const String &p_name, Vector2 p_value); + +public: + bool is_button_pressed(const StringName &p_name) const; + float get_value(const StringName &p_name) const; + Vector2 get_axis(const StringName &p_name) const; + + real_t get_rumble() const; + void set_rumble(real_t p_rumble); + + XRPositionalTracker::TrackerHand get_tracker_hand() const; + XRController3D() {} ~XRController3D() {} }; @@ -106,33 +156,19 @@ public: It must be a child node of our XROrigin3D node */ -class XRAnchor3D : public Node3D { - GDCLASS(XRAnchor3D, Node3D); +class XRAnchor3D : public XRNode3D { + GDCLASS(XRAnchor3D, XRNode3D); private: - int anchor_id = 1; - bool is_active = true; Vector3 size; - Ref<Mesh> mesh; protected: - void _notification(int p_what); static void _bind_methods(); public: - void set_anchor_id(int p_anchor_id); - int get_anchor_id() const; - String get_anchor_name() const; - - bool get_is_active() const; Vector3 get_size() const; - Plane get_plane() const; - Ref<Mesh> get_mesh() const; - - TypedArray<String> get_configuration_warnings() const override; - XRAnchor3D() {} ~XRAnchor3D() {} }; @@ -159,7 +195,7 @@ public: TypedArray<String> get_configuration_warnings() const override; void set_tracked_camera(XRCamera3D *p_tracked_camera); - void clear_tracked_camera_if(XRCamera3D *p_tracked_camera); + XRCamera3D *get_tracked_camera() const; real_t get_world_scale() const; void set_world_scale(real_t p_world_scale); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 8407e0dd99..1f000d5f39 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -257,6 +257,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node ObjectID id = resource.is_valid() ? resource->get_instance_id() : child->get_instance_id(); int bone_idx = -1; + int blend_shape_idx = -1; #ifndef _3D_DISABLED if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton3D>(child)) { @@ -266,6 +267,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov continue; } } + + if (a->track_get_type(i) == Animation::TYPE_BLEND_SHAPE) { + MeshInstance3D *mi_3d = Object::cast_to<MeshInstance3D>(child); + if (!mi_3d) { + continue; + } + if (a->track_get_path(i).get_subname_count() != 1) { + continue; + } + + blend_shape_idx = mi_3d->find_blend_shape_by_name(a->track_get_path(i).get_subname(0)); + if (blend_shape_idx == -1) { + continue; + } + } + #endif // _3D_DISABLED { @@ -277,6 +294,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov TrackNodeCacheKey key; key.id = id; key.bone_idx = bone_idx; + key.blend_shape_idx = blend_shape_idx; if (!node_cache_map.has(key)) { node_cache_map[key] = TrackNodeCache(); @@ -334,6 +352,13 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov } } } + + if (a->track_get_type(i) == Animation::TYPE_BLEND_SHAPE) { + // special cases and caches for transform tracks + node_cache->node_blend_shape = Object::cast_to<MeshInstance3D>(child); + node_cache->blend_shape_idx = blend_shape_idx; + } + #endif // _3D_DISABLED if (a->track_get_type(i) == Animation::TYPE_VALUE) { @@ -481,6 +506,31 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } #endif // _3D_DISABLED } break; + case Animation::TYPE_BLEND_SHAPE: { +#ifndef _3D_DISABLED + if (!nc->node_blend_shape) { + continue; + } + + float blend; + + Error err = a->blend_shape_track_interpolate(i, p_time, &blend); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); + nc->accum_pass = accum_pass; + cache_update[cache_update_size++] = nc; + nc->blend_shape_accum = blend; + } else { + nc->blend_shape_accum = Math::lerp(nc->blend_shape_accum, blend, p_interp); + } +#endif // _3D_DISABLED + } break; case Animation::TYPE_VALUE: { if (!nc->node) { continue; @@ -947,6 +997,8 @@ void AnimationPlayer::_animation_update_transforms() { nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum); } + } else if (nc->node_blend_shape) { + nc->node_blend_shape->set_blend_shape_value(nc->blend_shape_idx, nc->blend_shape_accum); } else if (nc->node_3d) { if (nc->loc_used) { nc->node_3d->set_position(nc->loc_accum); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index f8e72a67b6..21e309efe0 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -32,6 +32,7 @@ #define ANIMATION_PLAYER_H #include "scene/2d/node_2d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/animation.h" @@ -99,6 +100,8 @@ private: #ifndef _3D_DISABLED Node3D *node_3d = nullptr; Skeleton3D *skeleton = nullptr; + MeshInstance3D *node_blend_shape = nullptr; + int blend_shape_idx = -1; #endif // _3D_DISABLED int bone_idx = -1; // accumulated transforms @@ -110,6 +113,7 @@ private: Vector3 loc_accum; Quaternion rot_accum; Vector3 scale_accum; + float blend_shape_accum = 0; uint64_t accum_pass = 0; bool audio_playing = false; @@ -147,10 +151,15 @@ private: struct TrackNodeCacheKey { ObjectID id; int bone_idx = -1; + int blend_shape_idx = -1; inline bool operator<(const TrackNodeCacheKey &p_right) const { if (id == p_right.id) { - return bone_idx < p_right.bone_idx; + if (blend_shape_idx == p_right.blend_shape_idx) { + return bone_idx < p_right.bone_idx; + } else { + return blend_shape_idx < p_right.blend_shape_idx; + } } else { return id < p_right.id; } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 2ed8268289..ccb5fa9472 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -640,6 +640,37 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { #endif // _3D_DISABLED } break; + case Animation::TYPE_BLEND_SHAPE: { +#ifndef _3D_DISABLED + + if (path.get_subname_count() != 1) { + ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track does not contain a blend shape subname: '" + String(path) + "'"); + continue; + } + MeshInstance3D *mesh_3d = Object::cast_to<MeshInstance3D>(child); + + if (!mesh_3d) { + ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track does not point to MeshInstance3D: '" + String(path) + "'"); + continue; + } + + StringName blend_shape_name = path.get_subname(0); + int blend_shape_idx = mesh_3d->find_blend_shape_by_name(blend_shape_name); + if (blend_shape_idx == -1) { + ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track points to a non-existing name: '" + String(blend_shape_name) + "'"); + continue; + } + + TrackCacheBlendShape *track_bshape = memnew(TrackCacheBlendShape); + + track_bshape->mesh_3d = mesh_3d; + track_bshape->shape_index = blend_shape_idx; + + track_bshape->object = mesh_3d; + track_bshape->object_id = mesh_3d->get_instance_id(); + track = track_bshape; +#endif + } break; case Animation::TYPE_METHOD: { TrackCacheMethod *track_method = memnew(TrackCacheMethod); @@ -1089,6 +1120,28 @@ void AnimationTree::_process_graph(real_t p_delta) { } #endif // _3D_DISABLED } break; + case Animation::TYPE_BLEND_SHAPE: { +#ifndef _3D_DISABLED + TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); + + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->value = 0; + } + + float value; + + Error err = a->blend_shape_track_interpolate(i, time, &value); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + t->value = Math::lerp(t->value, value, blend); + +#endif // _3D_DISABLED + } break; case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); @@ -1360,9 +1413,6 @@ void AnimationTree::_process_graph(real_t p_delta) { root_motion_transform = xform; - if (t->skeleton && t->bone_idx >= 0) { - root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse(); - } } else if (t->skeleton && t->bone_idx >= 0) { if (t->loc_used) { t->skeleton->set_bone_pose_position(t->bone_idx, t->loc); @@ -1387,6 +1437,15 @@ void AnimationTree::_process_graph(real_t p_delta) { } #endif // _3D_DISABLED } break; + case Animation::TYPE_BLEND_SHAPE: { +#ifndef _3D_DISABLED + TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); + + if (t->mesh_3d) { + t->mesh_3d->set_blend_shape_value(t->shape_index, t->value); + } +#endif // _3D_DISABLED + } break; case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index ed207dfe0c..21b49937a7 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -210,6 +210,13 @@ private: } }; + struct TrackCacheBlendShape : public TrackCache { + MeshInstance3D *mesh_3d = nullptr; + float value = 0; + int shape_index = -1; + TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; } + }; + struct TrackCacheValue : public TrackCache { Variant value; Vector<StringName> subpath; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index da54903871..c1db684d9b 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1146,7 +1146,7 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { } void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - if (draw_breakpoints) { + if (draw_breakpoints && breakpoint_icon.is_valid()) { bool hovering = p_region.has_point(get_local_mouse_pos()); bool breakpointed = is_line_breakpointed(p_line); @@ -1162,7 +1162,7 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 } } - if (draw_bookmarks && is_line_bookmarked(p_line)) { + if (draw_bookmarks && is_line_bookmarked(p_line) && bookmark_icon.is_valid()) { int horizontal_padding = p_region.size.x / 2; int vertical_padding = p_region.size.y / 4; @@ -1172,7 +1172,7 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 bookmark_icon->draw_rect(get_canvas_item(), bookmark_region, false, bookmark_color); } - if (draw_executing_lines && is_line_executing(p_line)) { + if (draw_executing_lines && is_line_executing(p_line) && executing_line_icon.is_valid()) { int horizontal_padding = p_region.size.x / 10; int vertical_padding = p_region.size.y / 4; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 38da40a402..973074397b 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -740,7 +740,7 @@ bool Control::has_point(const Point2 &p_point) const { return Rect2(Point2(), get_size()).has_point(p_point); } -void Control::set_drag_forwarding(Node *p_target) { +void Control::set_drag_forwarding(Object *p_target) { if (p_target) { data.drag_owner = p_target->get_instance_id(); } else { diff --git a/scene/gui/control.h b/scene/gui/control.h index be692b6a0c..b551a2e299 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -355,7 +355,7 @@ public: virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; virtual bool has_point(const Point2 &p_point) const; - virtual void set_drag_forwarding(Node *p_target); + virtual void set_drag_forwarding(Object *p_target); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index dc9a3f337e..f03eebd543 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -3623,6 +3623,8 @@ int TextEdit::get_caret_wrap_index() const { } String TextEdit::get_word_under_caret() const { + ERR_FAIL_INDEX_V(caret.line, text.size(), ""); + ERR_FAIL_INDEX_V(caret.column, text[caret.line].length() + 1, ""); PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i = i + 2) { if (words[i] <= caret.column && words[i + 1] > caret.column) { diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 35a098c6fd..fe11de128a 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -341,7 +341,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x; - double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x); + double drift_from_unscaled_center = 0; + if (bottomright.y != topleft.y) { // To avoid division by zero. + drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x); + } + src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; src_rect.size.x = width_texture; dst_rect.position.x += (width_total - width_filled) * 0.5; @@ -351,7 +355,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y; - double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y); + double drift_from_unscaled_center = 0; + if (bottomright.y != topleft.y) { // To avoid division by zero. + drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y); + } + src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; src_rect.size.y = width_texture; dst_rect.position.y += (width_total - width_filled) * 0.5; diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 8734037a57..989aabc549 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "video_player.h" -#include "scene/scene_string_names.h" #include "core/os/os.h" +#include "scene/scene_string_names.h" #include "servers/audio_server.h" int VideoPlayer::sp_get_channel_count() const { @@ -55,7 +55,7 @@ bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) { return false; } -// Called from main thread (eg VideoStreamPlaybackWebm::update) +// Called from main thread (e.g. VideoStreamPlaybackTheora::update). int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) { ERR_FAIL_NULL_V(p_udata, 0); ERR_FAIL_NULL_V(p_data, 0); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index bf8f7291be..61c25ae9db 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -450,6 +450,7 @@ void register_scene_types() { GDREGISTER_CLASS(Camera3D); GDREGISTER_CLASS(AudioListener3D); GDREGISTER_CLASS(XRCamera3D); + GDREGISTER_VIRTUAL_CLASS(XRNode3D); GDREGISTER_CLASS(XRController3D); GDREGISTER_CLASS(XRAnchor3D); GDREGISTER_CLASS(XROrigin3D); @@ -684,6 +685,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(TileSetSource); GDREGISTER_CLASS(TileSetAtlasSource); GDREGISTER_CLASS(TileSetScenesCollectionSource); + GDREGISTER_CLASS(TileMapPattern); GDREGISTER_CLASS(TileData); GDREGISTER_CLASS(TileMap); GDREGISTER_CLASS(ParallaxBackground); @@ -940,10 +942,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("Path", "Path3D"); ClassDB::add_compatibility_class("PathFollow", "PathFollow3D"); ClassDB::add_compatibility_class("PhysicalBone", "PhysicalBone3D"); - ClassDB::add_compatibility_class("Physics2DDirectBodyStateSW", "PhysicsDirectBodyState2DSW"); ClassDB::add_compatibility_class("Physics2DDirectBodyState", "PhysicsDirectBodyState2D"); ClassDB::add_compatibility_class("Physics2DDirectSpaceState", "PhysicsDirectSpaceState2D"); - ClassDB::add_compatibility_class("Physics2DServerSW", "PhysicsServer2DSW"); ClassDB::add_compatibility_class("Physics2DServer", "PhysicsServer2D"); ClassDB::add_compatibility_class("Physics2DShapeQueryParameters", "PhysicsShapeQueryParameters2D"); ClassDB::add_compatibility_class("Physics2DTestMotionResult", "PhysicsTestMotionResult2D"); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 6612c5ff3c..08b78a39b1 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -49,6 +49,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { add_track(TYPE_ROTATION_3D); } else if (type == "scale_3d") { add_track(TYPE_SCALE_3D); + } else if (type == "blend_shape") { + add_track(TYPE_BLEND_SHAPE); } else if (type == "value") { add_track(TYPE_VALUE); } else if (type == "method") { @@ -146,6 +148,25 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { sk.value.y = ofs[3]; sk.value.z = ofs[4]; } + } else if (track_get_type(track) == TYPE_BLEND_SHAPE) { + BlendShapeTrack *st = static_cast<BlendShapeTrack *>(tracks[track]); + Vector<real_t> values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % BLEND_SHAPE_TRACK_SIZE, false); + + const real_t *r = values.ptr(); + + int64_t count = vcount / BLEND_SHAPE_TRACK_SIZE; + st->blend_shapes.resize(count); + + TKey<float> *sw = st->blend_shapes.ptrw(); + for (int i = 0; i < count; i++) { + TKey<float> &sk = sw[i]; + const real_t *ofs = &r[i * BLEND_SHAPE_TRACK_SIZE]; + sk.time = ofs[0]; + sk.transition = ofs[1]; + sk.value = ofs[2]; + } } else if (track_get_type(track) == TYPE_VALUE) { ValueTrack *vt = static_cast<ValueTrack *>(tracks[track]); @@ -369,6 +390,9 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { case TYPE_SCALE_3D: r_ret = "scale_3d"; break; + case TYPE_BLEND_SHAPE: + r_ret = "blend_shape"; + break; case TYPE_VALUE: r_ret = "value"; break; @@ -464,6 +488,25 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { r_ret = keys; return true; + } else if (track_get_type(track) == TYPE_BLEND_SHAPE) { + Vector<real_t> keys; + int kk = track_get_key_count(track); + keys.resize(kk * BLEND_SHAPE_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + float bs; + blend_shape_track_get_key(track, i, &bs); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); + w[idx++] = bs; + } + + r_ret = keys; + return true; } else if (track_get_type(track) == TYPE_VALUE) { const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]); @@ -682,6 +725,10 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { ScaleTrack *st = memnew(ScaleTrack); tracks.insert(p_at_pos, st); } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = memnew(BlendShapeTrack); + tracks.insert(p_at_pos, bst); + } break; case TYPE_VALUE: { tracks.insert(p_at_pos, memnew(ValueTrack)); @@ -731,6 +778,11 @@ void Animation::remove_track(int p_track) { _clear(st->scales); } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + _clear(bst->blend_shapes); + + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); _clear(vt->values); @@ -993,6 +1045,53 @@ Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_ return OK; } +int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_blend_shape) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, -1); + + BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + + TKey<float> tkey; + tkey.time = p_time; + tkey.value = p_blend_shape; + + int ret = _insert(p_time, st->blend_shapes, tkey); + emit_changed(); + return ret; +} + +Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blend_shape) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, st->blend_shapes.size(), ERR_INVALID_PARAMETER); + + *r_blend_shape = st->blend_shapes[p_key].value; + + return OK; +} + +Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER); + + BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t); + + bool ok = false; + + float tk = _interpolate(st->blend_shapes, p_time, st->interpolation, st->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + void Animation::track_remove_key_at_time(int p_track, double p_time) { int idx = track_find_key(p_track, p_time, true); ERR_FAIL_COND(idx < 0); @@ -1022,6 +1121,12 @@ void Animation::track_remove_key(int p_track, int p_idx) { st->scales.remove(p_idx); } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size()); + bst->blend_shapes.remove(p_idx); + + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_idx, vt->values.size()); @@ -1087,12 +1192,24 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { } break; case TYPE_SCALE_3D: { - ScaleTrack *rt = static_cast<ScaleTrack *>(t); - int k = _find(rt->scales, p_time); - if (k < 0 || k >= rt->scales.size()) { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + int k = _find(st->scales, p_time); + if (k < 0 || k >= st->scales.size()) { + return -1; + } + if (st->scales[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + int k = _find(bst->blend_shapes, p_time); + if (k < 0 || k >= bst->blend_shapes.size()) { return -1; } - if (rt->scales[k].time != p_time && p_exact) { + if (bst->blend_shapes[k].time != p_time && p_exact) { return -1; } return k; @@ -1186,6 +1303,12 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke track_set_key_transition(p_track, idx, p_transition); } break; + case TYPE_BLEND_SHAPE: { + ERR_FAIL_COND((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT)); + int idx = blend_shape_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); + + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1279,6 +1402,10 @@ int Animation::track_get_key_count(int p_track) const { ScaleTrack *st = static_cast<ScaleTrack *>(t); return st->scales.size(); } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + return bst->blend_shapes.size(); + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); return vt->values.size(); @@ -1328,6 +1455,12 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { return st->scales[p_key_idx].value; } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), Variant()); + + return bst->blend_shapes[p_key_idx].value; + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), Variant()); @@ -1400,6 +1533,11 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); return st->scales[p_key_idx].time; } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1); + return bst->blend_shapes[p_key_idx].time; + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), -1); @@ -1467,6 +1605,15 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { _insert(p_time, tt->scales, key); return; } + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size()); + TKey<float> key = tt->blend_shapes[p_key_idx]; + key.time = p_time; + tt->blend_shapes.remove(p_key_idx); + _insert(p_time, tt->blend_shapes, key); + return; + } case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); @@ -1537,6 +1684,11 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); return st->scales[p_key_idx].transition; } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1); + return bst->blend_shapes[p_key_idx].transition; + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), -1); @@ -1592,6 +1744,14 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p st->scales.write[p_key_idx].value = p_value; } break; + case TYPE_BLEND_SHAPE: { + ERR_FAIL_COND((p_value.get_type() != Variant::FLOAT) && (p_value.get_type() != Variant::INT)); + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size()); + + bst->blend_shapes.write[p_key_idx].value = p_value; + + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); @@ -1673,6 +1833,11 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr ERR_FAIL_INDEX(p_key_idx, st->scales.size()); st->scales.write[p_key_idx].transition = p_transition; } break; + case TYPE_BLEND_SHAPE: { + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size()); + bst->blend_shapes.write[p_key_idx].transition = p_transition; + } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); @@ -2185,6 +2350,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); } break; + case TYPE_BLEND_SHAPE: { + const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); + _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices); + + } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast<const ValueTrack *>(t); _track_get_key_indices_in_range(vt->values, from_time, length, p_indices); @@ -2250,6 +2421,11 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); } break; + case TYPE_BLEND_SHAPE: { + const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices); + + } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast<const ValueTrack *>(t); _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices); @@ -2867,6 +3043,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key); ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key); ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key); + ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key); ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); @@ -2943,6 +3120,7 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_POSITION_3D); BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); BIND_ENUM_CONSTANT(TYPE_SCALE_3D); + BIND_ENUM_CONSTANT(TYPE_BLEND_SHAPE); BIND_ENUM_CONSTANT(TYPE_METHOD); BIND_ENUM_CONSTANT(TYPE_BEZIER); BIND_ENUM_CONSTANT(TYPE_AUDIO); @@ -3089,6 +3267,41 @@ bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Ve return true; } +bool Animation::_blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error) { + float v0 = t0.value; + float v1 = t1.value; + float v2 = t2.value; + + if (Math::is_equal_approx(v1, v2, p_allowed_unit_error)) { + //0 and 2 are close, let's see if 1 is close + if (!Math::is_equal_approx(v0, v1, p_allowed_unit_error)) { + //not close, not optimizable + return false; + } + + } else { + /* + TODO eventually discuss a way to optimize these better. + float pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; //beyond segment range + } + + float s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + + if (d > pd.length() * p_allowed_linear_error) { + return false; //beyond allowed error for colinearity + } +*/ + } + + return true; +} + void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D); @@ -3197,6 +3410,41 @@ void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) { } } +void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_err) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_BLEND_SHAPE); + BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(tracks[p_idx]); + bool prev_erased = false; + TKey<float> first_erased; + first_erased.value = 0.0; + + for (int i = 1; i < tt->blend_shapes.size() - 1; i++) { + TKey<float> &t0 = tt->blend_shapes.write[i - 1]; + TKey<float> &t1 = tt->blend_shapes.write[i]; + TKey<float> &t2 = tt->blend_shapes.write[i + 1]; + + bool erase = _blend_shape_track_optimize_key(t0, t1, t2, p_allowed_linear_err); + + if (prev_erased && !_blend_shape_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { + //avoid error to go beyond first erased key + erase = false; + } + + if (erase) { + if (!prev_erased) { + first_erased = t1; + prev_erased = true; + } + + tt->blend_shapes.remove(i); + i--; + + } else { + prev_erased = false; + } + } +} + void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { for (int i = 0; i < tracks.size(); i++) { if (tracks[i]->type == TYPE_POSITION_3D) { @@ -3205,6 +3453,8 @@ void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_e _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle); } else if (tracks[i]->type == TYPE_SCALE_3D) { _scale_track_optimize(i, p_allowed_linear_err); + } else if (tracks[i]->type == TYPE_BLEND_SHAPE) { + _blend_shape_track_optimize(i, p_allowed_linear_err); } } } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 614e12a560..4ee0741d87 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -45,6 +45,7 @@ public: TYPE_POSITION_3D, ///< Position 3D track TYPE_ROTATION_3D, ///< Rotation 3D track TYPE_SCALE_3D, ///< Scale 3D track + TYPE_BLEND_SHAPE, ///< Blend Shape track TYPE_METHOD, ///< Call any method on a specific node. TYPE_BEZIER, ///< Bezier curve TYPE_AUDIO, @@ -91,6 +92,7 @@ private: const int32_t POSITION_TRACK_SIZE = 5; const int32_t ROTATION_TRACK_SIZE = 6; const int32_t SCALE_TRACK_SIZE = 5; + const int32_t BLEND_SHAPE_TRACK_SIZE = 3; /* POSITION TRACK */ @@ -115,6 +117,13 @@ private: ScaleTrack() { type = TYPE_SCALE_3D; } }; + /* BLEND SHAPE TRACK */ + + struct BlendShapeTrack : public Track { + Vector<TKey<float>> blend_shapes; + BlendShapeTrack() { type = TYPE_BLEND_SHAPE; } + }; + /* PROPERTY VALUE TRACK */ struct ValueTrack : public Track { @@ -247,10 +256,12 @@ private: bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm); bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle); bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error); + bool _blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error); void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err); void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle); void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err); + void _blend_shape_track_optimize(int p_idx, real_t p_allowed_unit_error); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -308,6 +319,10 @@ public: Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const; Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const; + int blend_shape_track_insert_key(int p_track, double p_time, float p_blend); + Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const; + Error blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const; + void track_set_interpolation_type(int p_track, InterpolationType p_interp); InterpolationType track_get_interpolation_type(int p_track) const; diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index 0fb6bb2c62..3667ab7c14 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -2,8 +2,6 @@ Import("env") -import os -import os.path from platform_methods import run_in_subprocess import default_theme_builders diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 9a3f081a8b..be0d3a140e 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1291,7 +1291,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density"); ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 2572c5de33..076b8312b6 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -997,7 +997,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); -struct EditorSceneImporterMeshLightmapSurface { +struct EditorSceneFormatImporterMeshLightmapSurface { Ref<Material> material; LocalVector<SurfaceTool::Vertex> vertices; Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX; @@ -1015,7 +1015,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, LocalVector<float> uv; LocalVector<Pair<int, int>> uv_indices; - Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces; + Vector<EditorSceneFormatImporterMeshLightmapSurface> lightmap_surfaces; // Keep only the scale Basis basis = p_base_transform.get_basis(); @@ -1027,7 +1027,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, Basis normal_basis = transform.basis.inverse().transposed(); for (int i = 0; i < get_surface_count(); i++) { - EditorSceneImporterMeshLightmapSurface s; + EditorSceneFormatImporterMeshLightmapSurface s; s.primitive = get_surface_primitive_type(i); ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap."); diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index e615615924..dedea3e282 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -149,6 +149,11 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { return; } + // Make sure the transform cache is the correct size + if (fabrik_transforms.size() != fabrik_data_chain.size()) { + fabrik_transforms.resize(fabrik_data_chain.size()); + } + // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more // Also, while we are here, apply magnet positions. for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { @@ -162,27 +167,24 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) { return; } - - Transform3D local_pose_override = stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[i].bone_idx); + fabrik_transforms[i] = stack->skeleton->get_bone_global_pose(fabrik_data_chain[i].bone_idx); // Apply magnet positions: if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) { int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx); - Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx)); - local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); + Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx)); + fabrik_transforms[i].origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); } else { - local_pose_override.origin += fabrik_data_chain[i].magnet_position; + fabrik_transforms[i].origin += fabrik_data_chain[i].magnet_position; } - - stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, local_pose_override, stack->strength, true); } + Transform3D origin_global_pose_trans = stack->skeleton->get_bone_global_pose_no_override(fabrik_data_chain[0].bone_idx); target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform()); - origin_global_pose = stack->skeleton->local_pose_to_global_pose( - fabrik_data_chain[0].bone_idx, stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[0].bone_idx)); + origin_global_pose = origin_global_pose_trans; final_joint_idx = fabrik_data_chain.size() - 1; - real_t target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length(); + real_t target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); chain_iterations = 0; while (target_distance > chain_tolerance) { @@ -190,7 +192,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { chain_forwards(); // update the target distance - target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length(); + target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); // update chain iterations chain_iterations += 1; @@ -205,7 +207,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { void SkeletonModification3DFABRIK::chain_backwards() { int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx; - Transform3D final_joint_trans = stack->skeleton->local_pose_to_global_pose(final_bone_idx, stack->skeleton->get_bone_local_pose_override(final_bone_idx)); + Transform3D final_joint_trans = fabrik_transforms[final_joint_idx]; // Get the direction the final bone is facing in. stack->skeleton->update_bone_rest_forward_vector(final_bone_idx); @@ -220,52 +222,46 @@ void SkeletonModification3DFABRIK::chain_backwards() { // set the position of the final joint to the target position final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length); - final_joint_trans = stack->skeleton->global_pose_to_local_pose(final_bone_idx, final_joint_trans); - stack->skeleton->set_bone_local_pose_override(final_bone_idx, final_joint_trans, stack->strength, true); + fabrik_transforms[final_joint_idx] = final_joint_trans; // for all other joints, move them towards the target int i = final_joint_idx; while (i >= 1) { - int next_bone_idx = fabrik_data_chain[i].bone_idx; - Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); + Transform3D next_bone_trans = fabrik_transforms[i]; i -= 1; - int current_bone_idx = fabrik_data_chain[i].bone_idx; - Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); + Transform3D current_trans = fabrik_transforms[i]; real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin)); current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length); - // Apply it back to the skeleton - stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true); + // Save the result + fabrik_transforms[i] = current_trans; } } void SkeletonModification3DFABRIK::chain_forwards() { // Set root at the initial position. - int origin_bone_idx = fabrik_data_chain[0].bone_idx; - Transform3D root_transform = stack->skeleton->local_pose_to_global_pose(origin_bone_idx, stack->skeleton->get_bone_local_pose_override(origin_bone_idx)); + Transform3D root_transform = fabrik_transforms[0]; + root_transform.origin = origin_global_pose.origin; - stack->skeleton->set_bone_local_pose_override(origin_bone_idx, stack->skeleton->global_pose_to_local_pose(origin_bone_idx, root_transform), stack->strength, true); + fabrik_transforms[0] = origin_global_pose; for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) { - int current_bone_idx = fabrik_data_chain[i].bone_idx; - Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); - int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; - Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); + Transform3D current_trans = fabrik_transforms[i]; + Transform3D next_bone_trans = fabrik_transforms[i + 1]; real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin)); next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length); - // Apply it back to the skeleton - stack->skeleton->set_bone_local_pose_override(next_bone_idx, stack->skeleton->global_pose_to_local_pose(next_bone_idx, next_bone_trans), stack->strength, true); + // Save the result + fabrik_transforms[i + 1] = next_bone_trans; } } void SkeletonModification3DFABRIK::chain_apply() { for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { int current_bone_idx = fabrik_data_chain[i].bone_idx; - Transform3D current_trans = stack->skeleton->get_bone_local_pose_override(current_bone_idx); - current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, current_trans); + Transform3D current_trans = fabrik_transforms[i]; // If this is the last bone in the chain... if (i == fabrik_data_chain.size() - 1) { @@ -280,8 +276,7 @@ void SkeletonModification3DFABRIK::chain_apply() { current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale()); } } else { // every other bone in the chain... - int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; - Transform3D next_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); + Transform3D next_trans = fabrik_transforms[i + 1]; // Get the forward direction that the basis is facing in right now. stack->skeleton->update_bone_rest_forward_vector(current_bone_idx); @@ -290,9 +285,7 @@ void SkeletonModification3DFABRIK::chain_apply() { current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin)); current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll); } - current_trans = stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans); - current_trans.origin = Vector3(0, 0, 0); - stack->skeleton->set_bone_local_pose_override(current_bone_idx, current_trans, stack->strength, true); + stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true); } // Update all the bones so the next modification has up-to-date data. @@ -374,6 +367,7 @@ int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() { void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); fabrik_data_chain.resize(p_length); + fabrik_transforms.resize(p_length); execution_error_found = false; notify_property_list_changed(); } @@ -513,8 +507,11 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join Transform3D node_trans = tip_node->get_global_transform(); node_trans = stack->skeleton->world_transform_to_global_pose(node_trans); - node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans); - fabrik_data_chain[p_joint_idx].length = node_trans.origin.length(); + //node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans); + //fabrik_data_chain[p_joint_idx].length = node_trans.origin.length(); + + fabrik_data_chain[p_joint_idx].length = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx).origin.distance_to(node_trans.origin); + } else { // Use child bone(s) to update joint length, if possible Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx); if (bone_children.size() <= 0) { @@ -522,10 +519,13 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join return; } + Transform3D bone_trans = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx); + real_t final_length = 0; for (int i = 0; i < bone_children.size(); i++) { Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]); - final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length(); + final_length += bone_trans.origin.distance_to(child_transform.origin); + //final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length(); } fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size(); } diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index 9b5da883d4..6c58b8a07a 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -55,6 +55,8 @@ private: }; LocalVector<FABRIK_Joint_Data> fabrik_data_chain; + LocalVector<Transform3D> fabrik_transforms; + NodePath target_node; ObjectID target_node_cache; diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp index 1fb7dad2ad..a6bcb0176a 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.cpp +++ b/scene/resources/skeleton_modification_3d_jiggle.cpp @@ -172,7 +172,12 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D return; } - Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx)); + Transform3D bone_local_pos = stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx); + if (bone_local_pos == Transform3D()) { + bone_local_pos = stack->skeleton->get_bone_pose(jiggle_data_chain[p_joint_idx].bone_idx); + } + + Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, bone_local_pos); Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin; jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta; diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp index afdb077e71..f3b0f41d60 100644 --- a/scene/resources/skeleton_modification_3d_lookat.cpp +++ b/scene/resources/skeleton_modification_3d_lookat.cpp @@ -96,8 +96,10 @@ void SkeletonModification3DLookAt::_execute(real_t p_delta) { if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { return; } - Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx); + if (new_bone_trans == Transform3D()) { + new_bone_trans = stack->skeleton->get_bone_pose(bone_idx); + } Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin; // Lock the rotation to a plane relative to the bone by changing the target position diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp index ae7a3bab7e..93ec155a88 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp @@ -178,7 +178,16 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) { } Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform()); - bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx)); + Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx); + if (bone_one_local_pos == Transform3D()) { + bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx); + } + Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx); + if (bone_two_local_pos == Transform3D()) { + bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx); + } + + bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0)); bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis); stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx); @@ -186,7 +195,7 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) { stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true); stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx); - bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx)); + bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0)); bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis); stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); @@ -194,8 +203,17 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) { stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true); stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx); } else { - bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx)); - bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx)); + Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx); + if (bone_one_local_pos == Transform3D()) { + bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx); + } + Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx); + if (bone_two_local_pos == Transform3D()) { + bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx); + } + + bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); + bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); } Transform3D bone_two_tip_trans; @@ -455,7 +473,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { joint_two_length = 0; for (int i = 0; i < bone_two_children.size(); i++) { joint_two_length += bone_two_rest_trans.origin.distance_to( - stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin); + stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin); } joint_two_length = joint_two_length / bone_two_children.size(); } else { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index a8cd872408..455af8a40c 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1174,9 +1174,11 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun Vector<int> lod; ERR_FAIL_COND_V(simplify_func == nullptr, lod); + ERR_FAIL_COND_V(p_target_index_count < 0, lod); ERR_FAIL_COND_V(vertex_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod); + ERR_FAIL_COND_V(index_array.size() < (unsigned int)p_target_index_count, lod); lod.resize(index_array.size()); LocalVector<float> vertices; //uses floats diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 275b430f2a..a45b6b8eb6 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -31,6 +31,7 @@ #include "tile_set.h" #include "core/core_string_names.h" +#include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/templates/local_vector.h" @@ -39,6 +40,189 @@ #include "scene/resources/convex_polygon_shape_2d.h" #include "servers/navigation_server_2d.h" +/////////////////////////////// TileMapPattern ////////////////////////////////////// + +void TileMapPattern::_set_tile_data(const Vector<int> &p_data) { + int c = p_data.size(); + const int *r = p_data.ptr(); + + int offset = 3; + ERR_FAIL_COND_MSG(c % offset != 0, "Corrupted tile data."); + + clear(); + + for (int i = 0; i < c; i += offset) { + const uint8_t *ptr = (const uint8_t *)&r[i]; + uint8_t local[12]; + for (int j = 0; j < 12; j++) { + local[j] = ptr[j]; + } + +#ifdef BIG_ENDIAN_ENABLED + SWAP(local[0], local[3]); + SWAP(local[1], local[2]); + SWAP(local[4], local[7]); + SWAP(local[5], local[6]); + SWAP(local[8], local[11]); + SWAP(local[9], local[10]); +#endif + + int16_t x = decode_uint16(&local[0]); + int16_t y = decode_uint16(&local[2]); + uint16_t source_id = decode_uint16(&local[4]); + uint16_t atlas_coords_x = decode_uint16(&local[6]); + uint16_t atlas_coords_y = decode_uint16(&local[8]); + uint16_t alternative_tile = decode_uint16(&local[10]); + set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + } + emit_signal(SNAME("changed")); +} + +Vector<int> TileMapPattern::_get_tile_data() const { + // Export tile data to raw format + Vector<int> data; + data.resize(pattern.size() * 3); + int *w = data.ptrw(); + + // Save in highest format + + int idx = 0; + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + uint8_t *ptr = (uint8_t *)&w[idx]; + encode_uint16((int16_t)(E.key.x), &ptr[0]); + encode_uint16((int16_t)(E.key.y), &ptr[2]); + encode_uint16(E.value.source_id, &ptr[4]); + encode_uint16(E.value.coord_x, &ptr[6]); + encode_uint16(E.value.coord_y, &ptr[8]); + encode_uint16(E.value.alternative_tile, &ptr[10]); + idx += 3; + } + + return data; +} + +void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords)); + + size = size.max(p_coords + Vector2i(1, 1)); + pattern[p_coords] = TileMapCell(p_source_id, p_atlas_coords, p_alternative_tile); + emit_changed(); +} + +bool TileMapPattern::has_cell(const Vector2i &p_coords) const { + return pattern.has(p_coords); +} + +void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { + ERR_FAIL_COND(!pattern.has(p_coords)); + + pattern.erase(p_coords); + if (p_update_size) { + size = Vector2i(); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + size = size.max(E.key + Vector2i(1, 1)); + } + } + emit_changed(); +} + +int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSet::INVALID_SOURCE); + + return pattern[p_coords].source_id; +} + +Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_ATLAS_COORDS); + + return pattern[p_coords].get_atlas_coords(); +} + +int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_TILE_ALTERNATIVE); + + return pattern[p_coords].alternative_tile; +} + +TypedArray<Vector2i> TileMapPattern::get_used_cells() const { + // Returns the cells used in the tilemap. + TypedArray<Vector2i> a; + a.resize(pattern.size()); + int i = 0; + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i p(E.key.x, E.key.y); + a[i++] = p; + } + + return a; +} + +Vector2i TileMapPattern::get_size() const { + return size; +} + +void TileMapPattern::set_size(const Vector2i &p_size) { + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i coords = E.key; + if (p_size.x <= coords.x || p_size.y <= coords.y) { + ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords)); + }; + } + + size = p_size; + emit_changed(); +} + +bool TileMapPattern::is_empty() const { + return pattern.is_empty(); +}; + +void TileMapPattern::clear() { + size = Vector2i(); + pattern.clear(); + emit_changed(); +}; + +bool TileMapPattern::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "tile_data") { + if (p_value.is_array()) { + _set_tile_data(p_value); + return true; + } + return false; + } + return false; +} + +bool TileMapPattern::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "tile_data") { + r_ret = _get_tile_data(); + return true; + } + return false; +} + +void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); +} + +void TileMapPattern::_bind_methods() { + ClassDB::bind_method(D_METHOD("_set_tile_data", "data"), &TileMapPattern::_set_tile_data); + ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMapPattern::_get_tile_data); + + ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell); + ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile); + + ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapPattern::get_used_cells); + ClassDB::bind_method(D_METHOD("get_size"), &TileMapPattern::get_size); + ClassDB::bind_method(D_METHOD("set_size", "size"), &TileMapPattern::set_size); + ClassDB::bind_method(D_METHOD("is_empty"), &TileMapPattern::is_empty); +} + /////////////////////////////// TileSet ////////////////////////////////////// const int TileSet::INVALID_SOURCE = -1; @@ -982,6 +1166,36 @@ void TileSet::clear_tile_proxies() { emit_changed(); } +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."); + } + ERR_FAIL_COND_V(p_index > (int)patterns.size(), -1); + if (p_index < 0) { + p_index = patterns.size(); + } + patterns.insert(p_index, p_pattern); + emit_changed(); + return p_index; +} + +Ref<TileMapPattern> TileSet::get_pattern(int p_index) { + ERR_FAIL_INDEX_V(p_index, (int)patterns.size(), Ref<TileMapPattern>()); + return patterns[p_index]; +} + +void TileSet::remove_pattern(int p_index) { + ERR_FAIL_INDEX(p_index, (int)patterns.size()); + patterns.remove(p_index); + emit_changed(); +} + +int TileSet::get_patterns_count() { + return patterns.size(); +} + Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { @@ -2483,6 +2697,12 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { return true; } return false; + } else if (components.size() == 1 && components[0].begins_with("pattern_") && components[0].trim_prefix("pattern_").is_valid_int()) { + int pattern_index = components[0].trim_prefix("pattern_").to_int(); + for (int i = patterns.size(); i <= pattern_index; i++) { + add_pattern(p_value); + } + return true; } #ifndef DISABLE_DEPRECATED @@ -2606,6 +2826,13 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { return true; } return false; + } else if (components.size() == 1 && components[0].begins_with("pattern_") && components[0].trim_prefix("pattern_").is_valid_int()) { + int pattern_index = components[0].trim_prefix("pattern_").to_int(); + if (pattern_index < 0 || pattern_index >= (int)patterns.size()) { + return false; + } + r_ret = patterns[pattern_index]; + return true; } return false; @@ -2686,6 +2913,11 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + + // Patterns. + for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NOEDITOR)); + } } void TileSet::_validate_property(PropertyInfo &property) const { @@ -2799,6 +3031,12 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("cleanup_invalid_tile_proxies"), &TileSet::cleanup_invalid_tile_proxies); ClassDB::bind_method(D_METHOD("clear_tile_proxies"), &TileSet::clear_tile_proxies); + // Patterns + ClassDB::bind_method(D_METHOD("add_pattern", "pattern", "index"), &TileSet::add_pattern, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_pattern", "index"), &TileSet::get_pattern, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_pattern", "index"), &TileSet::remove_pattern); + ClassDB::bind_method(D_METHOD("get_patterns_count"), &TileSet::get_patterns_count); + ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); ADD_ARRAY("occlusion_layers", "occlusion_layer_"); @@ -3565,6 +3803,10 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s } PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) { + ERR_FAIL_COND_V(p_margins.x < 0 || p_margins.y < 0, PackedVector2Array()); + ERR_FAIL_COND_V(p_separation.x < 0 || p_separation.y < 0, PackedVector2Array()); + ERR_FAIL_COND_V(p_texture_region_size.x <= 0 || p_texture_region_size.y <= 0, PackedVector2Array()); + // Compute the new atlas grid size. Size2 new_grid_size; if (p_texture.is_valid()) { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 351bdff89d..530c90920f 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -60,6 +60,84 @@ class TileSetPluginAtlasRendering; class TileSetPluginAtlasPhysics; class TileSetPluginAtlasNavigation; +union TileMapCell { + struct { + int32_t source_id : 16; + int16_t coord_x : 16; + int16_t coord_y : 16; + int32_t alternative_tile : 16; + }; + + uint64_t _u64t; + TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE + source_id = p_source_id; + set_atlas_coords(p_atlas_coords); + alternative_tile = p_alternative_tile; + } + + Vector2i get_atlas_coords() const { + return Vector2i(coord_x, coord_y); + } + + void set_atlas_coords(const Vector2i &r_coords) { + coord_x = r_coords.x; + coord_y = r_coords.y; + } + + bool operator<(const TileMapCell &p_other) const { + if (source_id == p_other.source_id) { + if (coord_x == p_other.coord_x) { + if (coord_y == p_other.coord_y) { + return alternative_tile < p_other.alternative_tile; + } else { + return coord_y < p_other.coord_y; + } + } else { + return coord_x < p_other.coord_x; + } + } else { + return source_id < p_other.source_id; + } + } + + bool operator!=(const TileMapCell &p_other) const { + return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile); + } +}; + +class TileMapPattern : public Resource { + GDCLASS(TileMapPattern, Resource); + + Vector2i size; + Map<Vector2i, TileMapCell> pattern; + + void _set_tile_data(const Vector<int> &p_data); + Vector<int> _get_tile_data() const; + +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; + + static void _bind_methods(); + +public: + void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0); + bool has_cell(const Vector2i &p_coords) const; + void remove_cell(const Vector2i &p_coords, bool p_update_size = true); + int get_cell_source_id(const Vector2i &p_coords) const; + Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const; + int get_cell_alternative_tile(const Vector2i &p_coords) const; + + TypedArray<Vector2i> get_used_cells() const; + + Vector2i get_size() const; + void set_size(const Vector2i &p_size); + bool is_empty() const; + + void clear(); +}; + class TileSet : public Resource { GDCLASS(TileSet, Resource); @@ -245,6 +323,8 @@ private: int next_source_id = 0; // --------------------- + LocalVector<Ref<TileMapPattern>> patterns; + void _compute_next_source_id(); void _source_changed(); @@ -384,6 +464,12 @@ public: void cleanup_invalid_tile_proxies(); void clear_tile_proxies(); + // Patterns. + int add_pattern(Ref<TileMapPattern> p_pattern, int p_index = -1); + Ref<TileMapPattern> get_pattern(int p_index); + void remove_pattern(int p_index); + int get_patterns_count(); + // Helpers Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index e45dfdcb1b..c3d9ef7b04 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -918,6 +918,7 @@ bool VisualShaderNodeCurveTexture::is_use_prop_slots() const { } VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() { + set_input_port_default_value(0, 0.0); simple_decl = true; allow_v_resize = false; } @@ -1002,6 +1003,7 @@ bool VisualShaderNodeCurveXYZTexture::is_use_prop_slots() const { } VisualShaderNodeCurveXYZTexture::VisualShaderNodeCurveXYZTexture() { + set_input_port_default_value(0, 0.0); simple_decl = true; allow_v_resize = false; } |