diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/tile_map.cpp | 107 | ||||
-rw-r--r-- | scene/2d/tile_map.h | 77 | ||||
-rw-r--r-- | scene/3d/xr_nodes.cpp | 627 | ||||
-rw-r--r-- | scene/3d/xr_nodes.h | 110 | ||||
-rw-r--r-- | scene/gui/control.cpp | 2 | ||||
-rw-r--r-- | scene/gui/control.h | 2 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 2 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 2 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 4 | ||||
-rw-r--r-- | scene/resources/text_paragraph.cpp | 4 | ||||
-rw-r--r-- | scene/resources/text_paragraph.h | 2 | ||||
-rw-r--r-- | scene/resources/tile_set.cpp | 238 | ||||
-rw-r--r-- | scene/resources/tile_set.h | 86 |
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>()); |