summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/tile_map.cpp107
-rw-r--r--scene/2d/tile_map.h77
-rw-r--r--scene/3d/xr_nodes.cpp627
-rw-r--r--scene/3d/xr_nodes.h110
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/rich_text_label.cpp2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/text_paragraph.cpp4
-rw-r--r--scene/resources/text_paragraph.h2
-rw-r--r--scene/resources/tile_set.cpp238
-rw-r--r--scene/resources/tile_set.h86
13 files changed, 776 insertions, 487 deletions
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/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 56edf100f6..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,130 +422,171 @@ 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();
@@ -502,10 +595,6 @@ Plane XRAnchor3D::get_plane() const {
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/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/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 4588966d88..93d1eb5ba4 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -486,7 +486,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
remaining_characters -= cell_ch;
table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
- table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wraped_size().x));
+ table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x));
}
idx++;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index f03eebd543..55c18980f9 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4039,7 +4039,7 @@ int TextEdit::get_visible_line_count() const {
}
int TextEdit::get_total_visible_line_count() const {
- /* Returns the total number of (lines + wraped - hidden). */
+ /* Returns the total number of (lines + wrapped - hidden). */
if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
return text.size();
}
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/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index b2e18e2451..fae1de94d3 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -84,7 +84,7 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
- ClassDB::bind_method(D_METHOD("get_non_wraped_size"), &TextParagraph::get_non_wraped_size);
+ ClassDB::bind_method(D_METHOD("get_non_wrapped_size"), &TextParagraph::get_non_wrapped_size);
ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size);
ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
@@ -417,7 +417,7 @@ float TextParagraph::get_width() const {
return width;
}
-Size2 TextParagraph::get_non_wraped_size() const {
+Size2 TextParagraph::get_non_wrapped_size() const {
const_cast<TextParagraph *>(this)->_shape_lines();
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 69c50559df..701c9a17cd 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -120,7 +120,7 @@ public:
void set_max_lines_visible(int p_lines);
int get_max_lines_visible() const;
- Size2 get_non_wraped_size() const;
+ Size2 get_non_wrapped_size() const;
Size2 get_size() const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index b988b2ab3e..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_");
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>());