summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/area_2d.cpp8
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/2d/tile_map.cpp107
-rw-r--r--scene/2d/tile_map.h77
-rw-r--r--scene/3d/area_3d.cpp8
-rw-r--r--scene/3d/audio_stream_player_3d.cpp4
-rw-r--r--scene/3d/bone_attachment_3d.cpp6
-rw-r--r--scene/3d/bone_attachment_3d.h1
-rw-r--r--scene/3d/mesh_instance_3d.cpp78
-rw-r--r--scene/3d/mesh_instance_3d.h15
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp236
-rw-r--r--scene/3d/skeleton_3d.h15
-rw-r--r--scene/3d/xr_nodes.cpp629
-rw-r--r--scene/3d/xr_nodes.h110
-rw-r--r--scene/animation/animation_player.cpp52
-rw-r--r--scene/animation/animation_player.h11
-rw-r--r--scene/animation/animation_tree.cpp65
-rw-r--r--scene/animation/animation_tree.h7
-rw-r--r--scene/gui/code_edit.cpp6
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/gui/texture_progress_bar.cpp12
-rw-r--r--scene/gui/video_player.cpp4
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/animation.cpp258
-rw-r--r--scene/resources/animation.h15
-rw-r--r--scene/resources/default_theme/SCsub2
-rw-r--r--scene/resources/environment.cpp2
-rw-r--r--scene/resources/importer_mesh.cpp6
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp78
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h2
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp7
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp28
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/tile_set.cpp242
-rw-r--r--scene/resources/tile_set.h86
-rw-r--r--scene/resources/visual_shader_nodes.cpp2
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;
}