diff options
Diffstat (limited to 'scene/resources')
-rw-r--r-- | scene/resources/tile_set.cpp | 238 | ||||
-rw-r--r-- | scene/resources/tile_set.h | 86 |
2 files changed, 324 insertions, 0 deletions
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>()); |