Added godot 4's tilemap as a new layered tilemap module.

This commit is contained in:
Relintai 2024-03-01 10:38:08 +01:00
parent 81717d4505
commit eec9c78867
32 changed files with 29298 additions and 0 deletions

View File

@ -0,0 +1,11 @@
Import('env')
env.add_source_files(env.modules_sources,"register_types.cpp")
env.add_source_files(env.modules_sources,"tile_map.cpp")
env.add_source_files(env.modules_sources,"tile_set.cpp")
env.add_source_files(env.modules_sources, "geometry_parser/*.cpp")
if env["tools"]:
env.add_source_files(env.modules_sources, "tile_map_editor_plugin.cpp")
env.add_source_files(env.modules_sources, "tile_set_editor_plugin.cpp")

View File

@ -0,0 +1,15 @@
def can_build(env, platform):
return False
def configure(env):
pass
def get_doc_classes():
return [
"TileMap",
"TileSet"
]
def get_doc_path():
return "doc_classes"

View File

@ -0,0 +1,368 @@
/**************************************************************************/
/* atlas_merging_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "atlas_merging_dialog.h"
#include "editor/editor_properties_vector.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/control.h"
#include "scene/gui/split_container.h"
#include "scene/resources/image_texture.h"
void AtlasMergingDialog::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
_set(p_property, p_value);
}
void AtlasMergingDialog::_generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns) {
merged.instantiate();
merged_mapping.clear();
if (p_atlas_sources.size() >= 2) {
Ref<Image> output_image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8);
// Compute the new texture region size.
Vector2i new_texture_region_size;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
new_texture_region_size = new_texture_region_size.max(atlas_source->get_texture_region_size());
}
// Generate the new texture.
Vector2i atlas_offset;
int line_height = 0;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
Ref<Image> input_image = atlas_source->get_texture()->get_image();
if (input_image->get_format() != Image::FORMAT_RGBA8) {
input_image->convert(Image::FORMAT_RGBA8);
}
merged_mapping.push_back(HashMap<Vector2i, Vector2i>());
// Layout the tiles.
Vector2i atlas_size;
for (int tile_index = 0; tile_index < atlas_source->get_tiles_count(); tile_index++) {
Vector2i tile_id = atlas_source->get_tile_id(tile_index);
atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id));
Rect2i new_tile_rect_in_atlas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
// Copy the texture.
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
Vector2i new_position = new_tile_rect_in_atlas.position * new_texture_region_size;
if (frame > 0) {
new_position += src_rect.size * Vector2i(frame, 0);
atlas_size.x = MAX(frame + 1, atlas_size.x);
}
Rect2 dst_rect_wide = Rect2i(new_position, new_tile_rect_in_atlas.size * new_texture_region_size);
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
}
output_image->blit_rect(input_image, src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
// Add to the mapping.
merged_mapping[source_index][tile_id] = new_tile_rect_in_atlas.position;
}
// Compute the atlas offset.
line_height = MAX(atlas_size.y, line_height);
atlas_offset.x += atlas_size.x;
if (atlas_offset.x >= p_max_columns) {
atlas_offset.x = 0;
atlas_offset.y += line_height;
line_height = 0;
}
}
merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture(ImageTexture::create_from_image(output_image));
merged->set_texture_region_size(new_texture_region_size);
// Copy the tiles to the merged TileSetAtlasSource.
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
for (KeyValue<Vector2i, Vector2i> tile_mapping : merged_mapping[source_index]) {
// Create tiles and alternatives, then copy their properties.
for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) {
int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index);
int changed_id = -1;
if (alternative_id == 0) {
merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
int count = atlas_source->get_tile_animation_frames_count(tile_mapping.key);
merged->set_tile_animation_frames_count(tile_mapping.value, count);
for (int i = 0; i < count; i++) {
merged->set_tile_animation_frame_duration(tile_mapping.value, i, atlas_source->get_tile_animation_frame_duration(tile_mapping.key, i));
}
merged->set_tile_animation_speed(tile_mapping.value, atlas_source->get_tile_animation_speed(tile_mapping.key));
merged->set_tile_animation_mode(tile_mapping.value, atlas_source->get_tile_animation_mode(tile_mapping.key));
} else {
changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index);
}
// Copy the properties.
TileData *src_tile_data = atlas_source->get_tile_data(tile_mapping.key, alternative_id);
List<PropertyInfo> properties;
src_tile_data->get_property_list(&properties);
TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, changed_id == -1 ? alternative_id : changed_id);
for (PropertyInfo property : properties) {
if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
Variant value = src_tile_data->get(property.name);
Variant default_value = ClassDB::class_get_default_property_value("TileData", property.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
continue;
}
dst_tile_data->set(property.name, value);
}
}
}
}
}
}
void AtlasMergingDialog::_update_texture() {
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
if (selected.size() >= 2) {
Vector<Ref<TileSetAtlasSource>> to_merge;
for (int i = 0; i < selected.size(); i++) {
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
to_merge.push_back(tile_set->get_source(source_id));
}
_generate_merged(to_merge, next_line_after_column);
preview->set_texture(merged->get_texture());
preview->show();
select_2_atlases_label->hide();
get_ok_button()->set_disabled(false);
merge_button->set_disabled(false);
} else {
_generate_merged(Vector<Ref<TileSetAtlasSource>>(), next_line_after_column);
preview->set_texture(Ref<Texture2D>());
preview->hide();
select_2_atlases_label->show();
get_ok_button()->set_disabled(true);
merge_button->set_disabled(true);
}
}
void AtlasMergingDialog::_merge_confirmed(const String &p_path) {
ERR_FAIL_COND(!merged.is_valid());
Ref<ImageTexture> output_image_texture = merged->get_texture();
output_image_texture->get_image()->save_png(p_path);
ResourceLoader::import(p_path);
Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D");
merged->set_texture(new_texture_resource);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Merge TileSetAtlasSource"));
int next_id = tile_set->get_next_source_id();
undo_redo->add_do_method(*tile_set, "add_source", merged, next_id);
undo_redo->add_undo_method(*tile_set, "remove_source", next_id);
if (delete_original_atlases) {
// Delete originals if needed.
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
for (int i = 0; i < selected.size(); i++) {
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
undo_redo->add_do_method(*tile_set, "remove_source", source_id);
undo_redo->add_undo_method(*tile_set, "add_source", tas, source_id);
// Add the tile proxies.
for (int tile_index = 0; tile_index < tas->get_tiles_count(); tile_index++) {
Vector2i tile_id = tas->get_tile_id(tile_index);
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", source_id, tile_id, next_id, merged_mapping[i][tile_id]);
if (tile_set->has_coords_level_tile_proxy(source_id, tile_id)) {
Array a = tile_set->get_coords_level_tile_proxy(source_id, tile_id);
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", a[0], a[1]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", source_id, tile_id);
}
}
}
}
undo_redo->commit_action();
commited_actions_count++;
hide();
}
void AtlasMergingDialog::ok_pressed() {
delete_original_atlases = false;
editor_file_dialog->popup_file_dialog();
}
void AtlasMergingDialog::cancel_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < commited_actions_count; i++) {
undo_redo->undo();
}
commited_actions_count = 0;
}
void AtlasMergingDialog::custom_action(const String &p_action) {
if (p_action == "merge") {
delete_original_atlases = true;
editor_file_dialog->popup_file_dialog();
}
}
bool AtlasMergingDialog::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "next_line_after_column" && p_value.get_type() == Variant::INT) {
next_line_after_column = p_value;
_update_texture();
return true;
}
return false;
}
bool AtlasMergingDialog::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "next_line_after_column") {
r_ret = next_line_after_column;
return true;
}
return false;
}
void AtlasMergingDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
_update_texture();
}
} break;
}
}
void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) {
ERR_FAIL_COND(!p_tile_set.is_valid());
tile_set = p_tile_set;
atlas_merging_atlases_list->clear();
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
int source_id = p_tile_set->get_source_id(i);
Ref<TileSetAtlasSource> atlas_source = p_tile_set->get_source(source_id);
if (atlas_source.is_valid()) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (texture.is_valid()) {
String item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
atlas_merging_atlases_list->add_item(item_text, texture);
atlas_merging_atlases_list->set_item_metadata(-1, source_id);
}
}
}
get_ok_button()->set_disabled(true);
merge_button->set_disabled(true);
commited_actions_count = 0;
}
AtlasMergingDialog::AtlasMergingDialog() {
// Atlas merging window.
set_title(TTR("Atlas Merging"));
set_hide_on_ok(false);
// Ok buttons
set_ok_button_text(TTR("Merge (Keep original Atlases)"));
get_ok_button()->set_disabled(true);
merge_button = add_button(TTR("Merge"), true, "merge");
merge_button->set_disabled(true);
HSplitContainer *atlas_merging_h_split_container = memnew(HSplitContainer);
atlas_merging_h_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_h_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_child(atlas_merging_h_split_container);
// Atlas sources item list.
atlas_merging_atlases_list = memnew(ItemList);
atlas_merging_atlases_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200));
atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI);
atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2));
atlas_merging_h_split_container->add_child(atlas_merging_atlases_list);
VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer);
atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_right_panel->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_h_split_container->add_child(atlas_merging_right_panel);
// Settings.
Label *settings_label = memnew(Label);
settings_label->set_text(TTR("Settings:"));
atlas_merging_right_panel->add_child(settings_label);
columns_editor_property = memnew(EditorPropertyInteger);
columns_editor_property->set_label(TTR("Next Line After Column"));
columns_editor_property->set_object_and_property(this, "next_line_after_column");
columns_editor_property->update_property();
columns_editor_property->connect("property_changed", callable_mp(this, &AtlasMergingDialog::_property_changed));
atlas_merging_right_panel->add_child(columns_editor_property);
// Preview.
Label *preview_label = memnew(Label);
preview_label->set_text(TTR("Preview:"));
atlas_merging_right_panel->add_child(preview_label);
preview = memnew(TextureRect);
preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
preview->hide();
preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
atlas_merging_right_panel->add_child(preview);
select_2_atlases_label = memnew(Label);
select_2_atlases_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
select_2_atlases_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
select_2_atlases_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
select_2_atlases_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
select_2_atlases_label->set_text(TTR("Please select two atlases or more."));
atlas_merging_right_panel->add_child(select_2_atlases_label);
// The file dialog to choose the texture path.
editor_file_dialog = memnew(EditorFileDialog);
editor_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
editor_file_dialog->add_filter("*.png");
editor_file_dialog->connect("file_selected", callable_mp(this, &AtlasMergingDialog::_merge_confirmed));
add_child(editor_file_dialog);
}

View File

@ -0,0 +1,87 @@
/**************************************************************************/
/* atlas_merging_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ATLAS_MERGING_DIALOG_H
#define ATLAS_MERGING_DIALOG_H
#include "editor/editor_properties.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/2d/tile_set.h"
class EditorFileDialog;
class EditorPropertyVector2i;
class AtlasMergingDialog : public ConfirmationDialog {
GDCLASS(AtlasMergingDialog, ConfirmationDialog);
private:
int commited_actions_count = 0;
bool delete_original_atlases = true;
Ref<TileSetAtlasSource> merged;
LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping;
Ref<TileSet> tile_set;
// Settings.
int next_line_after_column = 30;
// GUI.
ItemList *atlas_merging_atlases_list = nullptr;
EditorPropertyVector2i *texture_region_size_editor_property = nullptr;
EditorPropertyInteger *columns_editor_property = nullptr;
TextureRect *preview = nullptr;
Label *select_2_atlases_label = nullptr;
EditorFileDialog *editor_file_dialog = nullptr;
Button *merge_button = nullptr;
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
void _generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns);
void _update_texture();
void _merge_confirmed(const String &p_path);
protected:
virtual void ok_pressed() override;
virtual void cancel_pressed() override;
virtual void custom_action(const String &) override;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _notification(int p_what);
public:
void update_tile_set(Ref<TileSet> p_tile_set);
AtlasMergingDialog();
};
#endif // ATLAS_MERGING_DIALOG_H

View File

@ -0,0 +1,735 @@
/**************************************************************************/
/* tile_atlas_view.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_atlas_view.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/gui/view_panner.h"
void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
if (panner->gui_input(p_event)) {
accept_event();
}
}
void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
panning += p_scroll_vec;
_update_zoom_and_panning(true);
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
_update_zoom_and_panning(true);
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
// Update the texture.
Vector2i size;
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
size = texture->get_size();
}
return size;
}
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
Vector2i size;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Vector2i line_size;
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
}
size.x = MAX(size.x, line_size.x);
size.y += line_size.y;
}
return size;
}
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
float zoom = zoom_widget->get_zoom();
// Compute the minimum sizes.
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom);
Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom);
// Set the texture for the base tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
// Set the scales.
if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
base_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
} else {
base_tiles_drawing_root->set_scale(Vector2(1, 1));
}
if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
} else {
alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
}
// Update the margin container's margins.
const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
for (int i = 0; i < 4; i++) {
margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom);
}
// Update the backgrounds.
background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
// Zoom on the position.
if (p_zoom_on_mouse_pos) {
// Offset the panning relative to the center of panel.
Vector2 relative_mpos = get_local_mouse_position() - get_size() / 2;
panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;
} else {
// Center of panel.
panning = panning * zoom / previous_zoom;
}
button_center_view->set_disabled(panning.is_zero_approx());
previous_zoom = zoom;
center_container->set_begin(panning - center_container->get_minimum_size() / 2);
center_container->set_size(center_container->get_minimum_size());
}
void TileAtlasView::_zoom_widget_changed() {
_update_zoom_and_panning();
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_center_view() {
panning = Vector2();
button_center_view->set_disabled(true);
_update_zoom_and_panning();
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
base_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
}
}
}
}
void TileAtlasView::_draw_base_tiles() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw the texture where there is no tile.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (rect.size.x > 0 && rect.size.y > 0) {
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
}
}
}
}
// Draw dark overlay after for performance reasons.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (rect.size.x > 0 && rect.size.y > 0) {
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
// Draw the texture around the grid.
Rect2i rect;
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Bottom
int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Left
rect.position = Vector2i(0, margins.y);
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Right.
int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
// Draw the tile.
TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
}
}
// Draw Dark overlay on separation in its own pass.
if (separation.x > 0 || separation.y > 0) {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
if (separation.x > 0) {
Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
//base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
if (separation.y > 0) {
Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
//base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
}
}
}
RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {
Ref<Material> mat = p_for_data->get_material();
if (mat.is_null()) {
return p_base_item->get_canvas_item();
} else if (p_material_map.has(mat)) {
return p_material_map[mat];
} else {
RID ci_rid = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());
RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());
p_material_map[mat] = ci_rid;
return ci_rid;
}
}
void TileAtlasView::_clear_material_canvas_items() {
for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {
RS::get_singleton()->free(kv.value);
}
material_tiles_draw.clear();
for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {
RS::get_singleton()->free(kv.value);
}
material_alternatives_draw.clear();
}
void TileAtlasView::_draw_base_tiles_texture_grid() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw each tile texture region.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
if (base_tile_coords == Vector2i(x, y)) {
// Draw existing tile.
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
}
} else {
// Draw the grid.
base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
}
}
}
}
}
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
Vector2i tile_shape_size = tile_set->get_tile_size();
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Color color = grid_color;
if (frame > 0) {
color.a *= 0.3;
}
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame);
Transform2D tile_xform;
tile_xform.set_origin(Rect2(texture_region).get_center() + in_tile_base_offset);
tile_xform.set_scale(tile_shape_size);
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
}
}
}
}
void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
alternative_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
Vector2i coords = Vector2i(coords3.x, coords3.y);
int alternative_id = coords3.z;
if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
}
}
}
void TileAtlasView::_draw_alternatives() {
// Draw the alternative tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2 current_pos;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
bool transposed = tile_data->get_transpose();
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);
// Update the y to max value.
Vector2i offset_pos;
if (transposed) {
offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin());
y_increment = MAX(y_increment, texture_region_size.x);
} else {
offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin());
y_increment = MAX(y_increment, texture_region_size.y);
}
// Draw the tile.
TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;
}
}
}
}
void TileAtlasView::_draw_background_left() {
background_left->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_left->get_size()), true);
}
void TileAtlasView::_draw_background_right() {
background_right->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_right->get_size()), true);
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
tile_set = p_tile_set;
tile_set_atlas_source = p_tile_set_atlas_source;
_clear_material_canvas_items();
if (!tile_set) {
return;
}
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
source_id = p_source_id;
// Show or hide the view.
bool valid = tile_set_atlas_source->get_texture().is_valid();
hbox->set_visible(valid);
missing_source_label->set_visible(!valid);
// Update the rect cache.
_update_alternative_tiles_rect_cache();
// Update everything.
_update_zoom_and_panning();
base_tiles_drawing_root->set_size(_compute_base_tiles_control_size());
alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size());
// Update.
base_tiles_draw->queue_redraw();
base_tiles_texture_grid->queue_redraw();
base_tiles_shape_grid->queue_redraw();
alternatives_draw->queue_redraw();
background_left->queue_redraw();
background_right->queue_redraw();
}
float TileAtlasView::get_zoom() const {
return zoom_widget->get_zoom();
};
void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) {
zoom_widget->set_zoom(p_zoom);
panning = p_panning;
_update_zoom_and_panning();
};
void TileAtlasView::set_padding(Side p_side, int p_padding) {
ERR_FAIL_COND(p_padding < 0);
margin_container_paddings[p_side] = p_padding;
}
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (!texture.is_valid()) {
return TileSetSource::INVALID_ATLAS_COORDS;
}
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
// Compute index in atlas.
Vector2 pos = p_pos - margins;
Vector2i ret = (pos / (texture_region_size + separation)).floor();
// Clamp.
if (p_clamp) {
Vector2i size = tile_set_atlas_source->get_atlas_grid_size();
ret.x = CLAMP(ret.x, 0, size.x - 1);
ret.y = CLAMP(ret.y, 0, size.y - 1);
}
return ret;
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
alternative_tiles_rect_cache.clear();
Rect2i current;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
int line_height = 0;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);
bool transposed = tile_data->get_transpose();
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
// Update the rect.
if (!alternative_tiles_rect_cache.has(tile_id)) {
alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>();
}
alternative_tiles_rect_cache[tile_id][alternative_id] = current;
current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
}
current.position.x = 0;
current.position.y += line_height;
}
}
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
for (const KeyValue<Vector2, HashMap<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
if (E_alternative.value.has_point(p_pos)) {
return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
}
}
}
return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache[p_coords].has(p_alternative_tile), Rect2i(), vformat("No cached rect for tile coords:%s alternative_id:%d", p_coords, p_alternative_tile));
return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
}
void TileAtlasView::queue_redraw() {
base_tiles_draw->queue_redraw();
base_tiles_texture_grid->queue_redraw();
base_tiles_shape_grid->queue_redraw();
alternatives_draw->queue_redraw();
background_left->queue_redraw();
background_right->queue_redraw();
}
void TileAtlasView::_update_theme_item_cache() {
Control::_update_theme_item_cache();
theme_cache.center_view_icon = get_editor_theme_icon(SNAME("CenterView"));
theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
}
void TileAtlasView::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_ENTER_TREE: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
} break;
case NOTIFICATION_THEME_CHANGED: {
button_center_view->set_icon(theme_cache.center_view_icon);
} break;
}
}
void TileAtlasView::_bind_methods() {
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
}
TileAtlasView::TileAtlasView() {
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
Panel *panel = memnew(Panel);
panel->set_clip_contents(true);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_h_size_flags(SIZE_EXPAND_FILL);
panel->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(panel);
zoom_widget = memnew(EditorZoomWidget);
add_child(zoom_widget);
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
zoom_widget->set_shortcut_context(this);
button_center_view = memnew(Button);
button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);
button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);
button_center_view->connect("pressed", callable_mp(this, &TileAtlasView::_center_view));
button_center_view->set_flat(true);
button_center_view->set_disabled(true);
button_center_view->set_tooltip_text(TTR("Center View"));
add_child(button_center_view);
panner.instantiate();
panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
panner->set_enable_rmb(true);
center_container = memnew(CenterContainer);
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->set_anchors_preset(Control::PRESET_CENTER);
center_container->connect("gui_input", callable_mp(this, &TileAtlasView::gui_input));
center_container->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
center_container->set_focus_mode(FOCUS_CLICK);
panel->add_child(center_container);
missing_source_label = memnew(Label);
missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));
center_container->add_child(missing_source_label);
margin_container = memnew(MarginContainer);
margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->add_child(margin_container);
hbox = memnew(HBoxContainer);
hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_theme_constant_override("separation", 10);
hbox->hide();
margin_container->add_child(hbox);
VBoxContainer *left_vbox = memnew(VBoxContainer);
left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_child(left_vbox);
VBoxContainer *right_vbox = memnew(VBoxContainer);
right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_child(right_vbox);
// Base tiles.
Label *base_tile_label = memnew(Label);
base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
base_tile_label->set_text(TTR("Base Tiles"));
base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
left_vbox->add_child(base_tile_label);
base_tiles_root_control = memnew(Control);
base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
left_vbox->add_child(base_tiles_root_control);
background_left = memnew(Control);
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
base_tiles_root_control->add_child(background_left);
base_tiles_drawing_root = memnew(Control);
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
base_tiles_root_control->add_child(base_tiles_drawing_root);
base_tiles_draw = memnew(Control);
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles));
base_tiles_drawing_root->add_child(base_tiles_draw);
base_tiles_texture_grid = memnew(Control);
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
base_tiles_shape_grid = memnew(Control);
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_label->set_text(TTR("Alternative Tiles"));
alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
right_vbox->add_child(alternative_tiles_label);
alternative_tiles_root_control = memnew(Control);
alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
right_vbox->add_child(alternative_tiles_root_control);
background_right = memnew(Control);
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right));
alternative_tiles_root_control->add_child(background_right);
alternative_tiles_drawing_root = memnew(Control);
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
alternatives_draw = memnew(Control);
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
alternative_tiles_drawing_root->add_child(alternatives_draw);
}
TileAtlasView::~TileAtlasView() {
_clear_material_canvas_items();
}

View File

@ -0,0 +1,174 @@
/**************************************************************************/
/* tile_atlas_view.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_ATLAS_VIEW_H
#define TILE_ATLAS_VIEW_H
#include "editor/gui/editor_zoom_widget.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/center_container.h"
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/2d/tile_set.h"
class ViewPanner;
class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
private:
TileSet *tile_set = nullptr;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int source_id = TileSet::INVALID_SOURCE;
enum DragType {
DRAG_TYPE_NONE,
DRAG_TYPE_PAN,
};
DragType drag_type = DRAG_TYPE_NONE;
float previous_zoom = 1.0;
EditorZoomWidget *zoom_widget = nullptr;
Button *button_center_view = nullptr;
CenterContainer *center_container = nullptr;
Vector2 panning;
void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false);
void _zoom_widget_changed();
void _center_view();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
Ref<ViewPanner> panner;
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
MarginContainer *margin_container = nullptr;
int margin_container_paddings[4] = { 0, 0, 0, 0 };
HBoxContainer *hbox = nullptr;
Label *missing_source_label = nullptr;
// Background
Control *background_left = nullptr;
void _draw_background_left();
Control *background_right = nullptr;
void _draw_background_right();
// Left side.
Control *base_tiles_root_control = nullptr;
void _base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *base_tiles_drawing_root = nullptr;
Control *base_tiles_draw = nullptr;
HashMap<Ref<Material>, RID> material_tiles_draw;
HashMap<Ref<Material>, RID> material_alternatives_draw;
void _draw_base_tiles();
RID _get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map);
void _clear_material_canvas_items();
Control *base_tiles_texture_grid = nullptr;
void _draw_base_tiles_texture_grid();
Control *base_tiles_shape_grid = nullptr;
void _draw_base_tiles_shape_grid();
Size2i _compute_base_tiles_control_size();
// Right side.
Control *alternative_tiles_root_control = nullptr;
void _alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *alternative_tiles_drawing_root = nullptr;
Control *alternatives_draw = nullptr;
void _draw_alternatives();
Size2i _compute_alternative_tiles_control_size();
struct ThemeCache {
Ref<Texture2D> center_view_icon;
Ref<Texture2D> checkerboard;
} theme_cache;
protected:
virtual void _update_theme_item_cache() override;
void _notification(int p_what);
static void _bind_methods();
public:
// Global.
void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
float get_zoom() const;
void set_transform(float p_zoom, Vector2i p_panning);
void set_padding(Side p_side, int p_padding);
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const;
void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
base_tiles_drawing_root->add_child(p_control);
} else {
base_tiles_root_control->add_child(p_control);
}
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
// Right side.
Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const;
Rect2i get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile);
void add_control_over_alternative_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
alternative_tiles_drawing_root->add_child(p_control);
} else {
alternative_tiles_root_control->add_child(p_control);
}
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
// Redraw everything.
void queue_redraw();
TileAtlasView();
~TileAtlasView();
};
#endif // TILE_ATLAS_VIEW_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,423 @@
/**************************************************************************/
/* tile_data_editors.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_DATA_EDITORS_H
#define TILE_DATA_EDITORS_H
#include "tile_atlas_view.h"
#include "editor/editor_properties.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/panel_container.h"
class Label;
class MenuButton;
class SpinBox;
class EditorUndoRedoManager;
class TileDataEditor : public VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
private:
bool _tile_set_changed_update_needed = false;
void _tile_set_changed_plan_update();
void _tile_set_changed_deferred_update();
protected:
Ref<TileSet> tile_set;
TileData *_get_tile_data(TileMapCell p_cell);
virtual void _tile_set_changed(){};
static void _bind_methods();
public:
void set_tile_set(Ref<TileSet> p_tile_set);
// Input to handle painting.
virtual Control *get_toolbar() { return nullptr; };
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){};
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){};
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){};
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){};
// Used to draw the tile data property value over a tile.
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false){};
};
class DummyObject : public Object {
GDCLASS(DummyObject, Object)
private:
HashMap<String, Variant> properties;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
public:
bool has_dummy_property(const StringName &p_name);
void add_dummy_property(const StringName &p_name);
void remove_dummy_property(const StringName &p_name);
void clear_dummy_properties();
};
class GenericTilePolygonEditor : public VBoxContainer {
GDCLASS(GenericTilePolygonEditor, VBoxContainer);
private:
Ref<TileSet> tile_set;
LocalVector<Vector<Point2>> polygons;
bool multiple_polygon_mode = false;
bool use_undo_redo = true;
// UI
int hovered_polygon_index = -1;
int hovered_point_index = -1;
int hovered_segment_index = -1;
Vector2 hovered_segment_point;
enum DragType {
DRAG_TYPE_NONE,
DRAG_TYPE_DRAG_POINT,
DRAG_TYPE_CREATE_POINT,
DRAG_TYPE_PAN,
};
DragType drag_type = DRAG_TYPE_NONE;
int drag_polygon_index = 0;
int drag_point_index = 0;
Vector2 drag_last_pos;
PackedVector2Array drag_old_polygon;
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tools_button_group;
Button *button_expand = nullptr;
Button *button_create = nullptr;
Button *button_edit = nullptr;
Button *button_delete = nullptr;
MenuButton *button_advanced_menu = nullptr;
enum Snap {
SNAP_NONE,
SNAP_HALF_PIXEL,
SNAP_GRID,
};
int current_snap_option = SNAP_HALF_PIXEL;
MenuButton *button_pixel_snap = nullptr;
SpinBox *snap_subdivision = nullptr;
Vector<Point2> in_creation_polygon;
Panel *panel = nullptr;
Control *base_control = nullptr;
EditorZoomWidget *editor_zoom_widget = nullptr;
Button *button_center_view = nullptr;
Vector2 panning;
Ref<Texture2D> background_texture;
Rect2 background_region;
Vector2 background_offset;
bool background_h_flip = false;
bool background_v_flip = false;
bool background_transpose = false;
Color background_modulate;
Color polygon_color = Color(1.0, 0.0, 0.0);
enum AdvancedMenuOption {
RESET_TO_DEFAULT_TILE,
CLEAR_TILE,
ROTATE_RIGHT,
ROTATE_LEFT,
FLIP_HORIZONTALLY,
FLIP_VERTICALLY,
};
void _base_control_draw();
void _zoom_changed();
void _advanced_menu_item_pressed(int p_item_pressed);
void _center_view();
void _base_control_gui_input(Ref<InputEvent> p_event);
void _set_snap_option(int p_index);
void _store_snap_options();
void _toggle_expand(bool p_expand);
void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist);
void _snap_point(Point2 &r_point);
void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index);
void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_use_undo_redo(bool p_use_undo_redo);
void set_tile_set(Ref<TileSet> p_tile_set);
void set_background(Ref<Texture2D> p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0));
int get_polygon_count();
int add_polygon(const Vector<Point2> &p_polygon, int p_index = -1);
void remove_polygon(int p_index);
void clear_polygons();
void set_polygon(int p_polygon_index, const Vector<Point2> &p_polygon);
Vector<Point2> get_polygon(int p_polygon_index);
void set_polygons_color(Color p_color);
void set_multiple_polygon_mode(bool p_multiple_polygon_mode);
GenericTilePolygonEditor();
};
class TileDataDefaultEditor : public TileDataEditor {
GDCLASS(TileDataDefaultEditor, TileDataEditor);
private:
// Toolbar
HBoxContainer *toolbar = memnew(HBoxContainer);
Button *picker_button = nullptr;
// UI
Ref<Texture2D> tile_bool_checked;
Ref<Texture2D> tile_bool_unchecked;
Label *label = nullptr;
EditorProperty *property_editor = nullptr;
// Painting state.
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
DRAG_TYPE_PAINT_RECT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_pos;
Vector2 drag_last_pos;
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
Variant drag_painted_value;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
protected:
DummyObject *dummy_object = memnew(DummyObject);
StringName type;
String property;
Variant::Type property_type;
void _notification(int p_what);
virtual Variant _get_painted_value();
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value);
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value);
public:
virtual Control *get_toolbar() override { return toolbar; };
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void setup_property_editor(Variant::Type p_type, const String &p_property, const String &p_label = "", const Variant &p_default_value = Variant());
Variant::Type get_property_type();
TileDataDefaultEditor();
~TileDataDefaultEditor();
};
class TileDataTextureOriginEditor : public TileDataDefaultEditor {
GDCLASS(TileDataTextureOriginEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataPositionEditor : public TileDataDefaultEditor {
GDCLASS(TileDataPositionEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataYSortEditor : public TileDataDefaultEditor {
GDCLASS(TileDataYSortEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataOcclusionShapeEditor : public TileDataDefaultEditor {
GDCLASS(TileDataOcclusionShapeEditor, TileDataDefaultEditor);
private:
int occlusion_layer = -1;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
void _polygon_changed(const PackedVector2Array &p_polygon);
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_occlusion_layer(int p_occlusion_layer) { occlusion_layer = p_occlusion_layer; }
TileDataOcclusionShapeEditor();
};
class TileDataCollisionEditor : public TileDataDefaultEditor {
GDCLASS(TileDataCollisionEditor, TileDataDefaultEditor);
int physics_layer = -1;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
DummyObject *dummy_object = memnew(DummyObject);
HashMap<StringName, EditorProperty *> property_editors;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
void _property_selected(const StringName &p_path, int p_focusable);
void _polygons_changed();
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_physics_layer(int p_physics_layer) { physics_layer = p_physics_layer; }
TileDataCollisionEditor();
~TileDataCollisionEditor();
};
class TileDataTerrainsEditor : public TileDataEditor {
GDCLASS(TileDataTerrainsEditor, TileDataEditor);
private:
// Toolbar
HBoxContainer *toolbar = memnew(HBoxContainer);
Button *picker_button = nullptr;
// Painting state.
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT_TERRAIN_SET,
DRAG_TYPE_PAINT_TERRAIN_SET_RECT,
DRAG_TYPE_PAINT_TERRAIN_BITS,
DRAG_TYPE_PAINT_TERRAIN_BITS_RECT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_pos;
Vector2 drag_last_pos;
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
Variant drag_painted_value;
// UI
Label *label = nullptr;
DummyObject *dummy_object = memnew(DummyObject);
EditorPropertyEnum *terrain_set_property_editor = nullptr;
EditorPropertyEnum *terrain_property_editor = nullptr;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
void _update_terrain_selector();
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual Control *get_toolbar() override { return toolbar; };
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
TileDataTerrainsEditor();
~TileDataTerrainsEditor();
};
class TileDataNavigationEditor : public TileDataDefaultEditor {
GDCLASS(TileDataNavigationEditor, TileDataDefaultEditor);
private:
int navigation_layer = -1;
PackedVector2Array navigation_polygon;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
void _polygon_changed(const PackedVector2Array &p_polygon);
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_navigation_layer(int p_navigation_layer) { navigation_layer = p_navigation_layer; }
TileDataNavigationEditor();
};
#endif // TILE_DATA_EDITORS_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,406 @@
/**************************************************************************/
/* tile_map_layer_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_LAYER_EDITOR_H
#define TILE_MAP_LAYER_EDITOR_H
#include "tile_atlas_view.h"
#include "core/os/thread.h"
#include "core/typedefs.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/gui/tree.h"
class TileMapLayerEditor;
class TileMapLayerSubEditorPlugin : public Object {
protected:
ObjectID edited_tile_map_layer_id;
TileMapLayer *_get_edited_layer() const;
public:
struct TabData {
Control *toolbar = nullptr;
Control *panel = nullptr;
};
virtual Vector<TabData> get_tabs() const {
return Vector<TabData>();
};
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
virtual void tile_set_changed(){};
virtual void edit(ObjectID p_tile_map_layer_id){};
};
class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
public:
enum {
TRANSFORM_ROTATE_LEFT,
TRANSFORM_ROTATE_RIGHT,
TRANSFORM_FLIP_H,
TRANSFORM_FLIP_V,
};
private:
///// Toolbar /////
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tool_buttons_group;
Button *select_tool_button = nullptr;
Button *paint_tool_button = nullptr;
Button *line_tool_button = nullptr;
Button *rect_tool_button = nullptr;
Button *bucket_tool_button = nullptr;
HBoxContainer *tools_settings = nullptr;
VSeparator *tools_settings_vsep = nullptr;
Button *picker_button = nullptr;
Button *erase_button = nullptr;
HBoxContainer *transform_toolbar = nullptr;
Button *transform_button_rotate_left = nullptr;
Button *transform_button_rotate_right = nullptr;
Button *transform_button_flip_h = nullptr;
Button *transform_button_flip_v = nullptr;
VSeparator *tools_settings_vsep_2 = nullptr;
CheckBox *bucket_contiguous_checkbox = nullptr;
Button *random_tile_toggle = nullptr;
HBoxContainer *scatter_controls_container = nullptr;
float scattering = 0.0;
Label *scatter_label = nullptr;
SpinBox *scatter_spinbox = nullptr;
void _on_random_tile_checkbox_toggled(bool p_pressed);
void _on_scattering_spinbox_changed(double p_value);
void _update_toolbar();
void _update_transform_buttons();
void _set_transform_buttons_state(const Vector<Button *> &p_enabled_buttons, const Vector<Button *> &p_disabled_buttons, const String &p_why_disabled);
///// Tilemap editing. /////
bool has_mouse = false;
void _mouse_exited_viewport();
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_SELECT,
DRAG_TYPE_MOVE,
DRAG_TYPE_PAINT,
DRAG_TYPE_LINE,
DRAG_TYPE_RECT,
DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
DRAG_TYPE_CLIPBOARD_PASTE,
};
DragType drag_type = DRAG_TYPE_NONE;
bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
HashMap<Vector2i, TileMapCell> drag_modified;
TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
HashMap<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
void _apply_transform(int p_type);
int _get_transformed_alternative(int p_alternative_id, int p_transform);
///// Selection system. /////
RBSet<Vector2i> tile_map_selection;
Ref<TileMapPattern> tile_map_clipboard;
Ref<TileMapPattern> selection_pattern;
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
TypedArray<Vector2i> _get_tile_map_selection() const;
RBSet<TileMapCell> tile_set_selection;
void _update_selection_pattern_from_tilemap_selection();
void _update_selection_pattern_from_tileset_tiles_selection();
void _update_selection_pattern_from_tileset_pattern_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
void _fix_invalid_tiles_in_tile_map_selection();
void patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
///// Bottom panel common ////
void _tab_changed();
///// Bottom panel tiles ////
VBoxContainer *tiles_bottom_panel = nullptr;
Label *missing_source_label = nullptr;
Label *invalid_source_label = nullptr;
ItemList *sources_list = nullptr;
MenuButton *source_sort_button = nullptr;
Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
void _update_source_display();
// Atlas sources.
TileMapCell hovered_tile;
TileAtlasView *tile_atlas_view = nullptr;
HSplitContainer *atlas_sources_split_container = nullptr;
bool tile_set_dragging_selection = false;
Vector2i tile_set_drag_start_mouse_pos;
Control *tile_atlas_control = nullptr;
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_control_draw();
Control *alternative_tiles_control = nullptr;
void _tile_alternatives_control_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
void _update_atlas_view();
void _set_source_sort(int p_sort);
// Scenes collection sources.
ItemList *scene_tiles_list = nullptr;
void _update_scenes_collection_view();
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
void _scenes_list_multi_selected(int p_index, bool p_selected);
void _scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
///// Bottom panel patterns ////
VBoxContainer *patterns_bottom_panel = nullptr;
ItemList *patterns_item_list = nullptr;
Label *patterns_help_label = nullptr;
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
bool select_last_pattern = false;
void _update_patterns_list();
// General
void _update_theme();
List<BaseButton *> viewport_shortcut_buttons;
// Update callback
virtual void tile_set_changed() override;
protected:
static void _bind_methods();
public:
virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTilesPlugin();
~TileMapLayerEditorTilesPlugin();
};
class TileMapLayerEditorTerrainsPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTerrainsPlugin, TileMapLayerSubEditorPlugin);
private:
// Toolbar.
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tool_buttons_group;
Button *paint_tool_button = nullptr;
Button *line_tool_button = nullptr;
Button *rect_tool_button = nullptr;
Button *bucket_tool_button = nullptr;
HBoxContainer *tools_settings = nullptr;
VSeparator *tools_settings_vsep = nullptr;
Button *picker_button = nullptr;
Button *erase_button = nullptr;
VSeparator *tools_settings_vsep_2 = nullptr;
CheckBox *bucket_contiguous_checkbox = nullptr;
void _update_toolbar();
// Main vbox.
VBoxContainer *main_vbox_container = nullptr;
// TileMap editing.
bool has_mouse = false;
void _mouse_exited_viewport();
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
DRAG_TYPE_LINE,
DRAG_TYPE_RECT,
DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
};
DragType drag_type = DRAG_TYPE_NONE;
bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
HashMap<Vector2i, TileMapCell> drag_modified;
// Painting
HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
enum SelectedType {
SELECTED_TYPE_CONNECT = 0,
SELECTED_TYPE_PATH,
SELECTED_TYPE_PATTERN,
};
SelectedType selected_type;
int selected_terrain_set = -1;
int selected_terrain = -1;
TileSet::TerrainsPattern selected_terrains_pattern;
void _update_selection();
// Bottom panel.
Tree *terrains_tree = nullptr;
ItemList *terrains_tile_list = nullptr;
// Cache.
LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
List<BaseButton *> viewport_shortcut_buttons;
// Update functions.
void _update_terrains_cache();
void _update_terrains_tree();
void _update_tiles_list();
void _update_theme();
// Update callback
virtual void tile_set_changed() override;
public:
virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTerrainsPlugin();
~TileMapLayerEditorTerrainsPlugin();
};
class TileMapLayerEditor : public VBoxContainer {
GDCLASS(TileMapLayerEditor, VBoxContainer);
private:
bool tileset_changed_needs_update = false;
ObjectID edited_tile_map_layer_id;
TileMapLayer *_get_edited_layer() const;
// Vector to keep plugins.
Vector<TileMapLayerSubEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
HFlowContainer *tile_map_toolbar = nullptr;
OptionButton *layers_selection_button = nullptr;
void _layers_selection_item_selected(int p_index);
Button *toggle_highlight_selected_layer_button = nullptr;
void _highlight_selected_layer_button_toggled(bool p_pressed);
Button *toggle_grid_button = nullptr;
void _on_grid_toggled(bool p_pressed);
MenuButton *advanced_menu_button = nullptr;
void _advanced_menu_button_id_pressed(int p_id);
// Bottom panel.
Label *missing_tileset_label = nullptr;
TabBar *tabs_bar = nullptr;
LocalVector<TileMapLayerSubEditorPlugin::TabData> tabs_data;
LocalVector<TileMapLayerSubEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
Ref<Texture2D> missing_tile_texture;
Ref<Texture2D> warning_pattern_texture;
// CallBack.
void _tile_map_layer_changed();
void _tab_changed(int p_tab_changed);
// Updates.
void _layers_select_next_or_previous(bool p_next);
void _update_highlighting_toggle();
// Inspector undo/redo callback.
void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
protected:
void _notification(int p_what);
static void _bind_methods();
void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMapLayer *p_tile_map_layer);
void update_layers_selector();
TileMapLayerEditor();
~TileMapLayerEditor();
// Static functions.
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
};
#endif // TILE_MAP_LAYER_EDITOR_H

View File

@ -0,0 +1,494 @@
/**************************************************************************/
/* tile_proxies_manager_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_proxies_manager_dialog.h"
#include "editor/editor_properties_vector.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/separator.h"
void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list) {
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
ItemList *item_list = Object::cast_to<ItemList>(p_item_list);
popup_menu->reset_size();
popup_menu->set_position(get_position() + item_list->get_global_mouse_position());
popup_menu->popup();
}
void TileProxiesManagerDialog::_menu_id_pressed(int p_id) {
if (p_id == 0) {
// Delete.
_delete_selected_bindings();
}
}
void TileProxiesManagerDialog::_delete_selected_bindings() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Tile Proxies"));
Vector<int> source_level_selected = source_level_list->get_selected_items();
for (int i = 0; i < source_level_selected.size(); i++) {
int key = source_level_list->get_item_metadata(source_level_selected[i]);
int val = tile_set->get_source_level_tile_proxy(key);
undo_redo->add_do_method(*tile_set, "remove_source_level_tile_proxy", key);
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", key, val);
}
Vector<int> coords_level_selected = coords_level_list->get_selected_items();
for (int i = 0; i < coords_level_selected.size(); i++) {
Array key = coords_level_list->get_item_metadata(coords_level_selected[i]);
Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]);
undo_redo->add_do_method(*tile_set, "remove_coords_level_tile_proxy", key[0], key[1]);
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", key[0], key[1], val[0], val[1]);
}
Vector<int> alternative_level_selected = alternative_level_list->get_selected_items();
for (int i = 0; i < alternative_level_selected.size(); i++) {
Array key = alternative_level_list->get_item_metadata(alternative_level_selected[i]);
Array val = tile_set->get_alternative_level_tile_proxy(key[0], key[1], key[2]);
undo_redo->add_do_method(*tile_set, "remove_alternative_level_tile_proxy", key[0], key[1], key[2]);
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", key[0], key[1], key[2], val[0], val[1], val[2]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
commited_actions_count += 1;
}
void TileProxiesManagerDialog::_update_lists() {
source_level_list->clear();
coords_level_list->clear();
alternative_level_list->clear();
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s", proxy[0]).rpad(5) + "-> " + vformat("%s", proxy[1]);
int id = source_level_list->add_item(text);
source_level_list->set_item_metadata(id, proxy[0]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s, %s", proxy[0], proxy[1]).rpad(17) + "-> " + vformat("%s, %s", proxy[2], proxy[3]);
int id = coords_level_list->add_item(text);
coords_level_list->set_item_metadata(id, proxy.slice(0, 2));
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s, %s, %s", proxy[0], proxy[1], proxy[2]).rpad(24) + "-> " + vformat("%s, %s, %s", proxy[3], proxy[4], proxy[5]);
int id = alternative_level_list->add_item(text);
alternative_level_list->set_item_metadata(id, proxy.slice(0, 3));
}
}
void TileProxiesManagerDialog::_update_enabled_property_editors() {
if (from.source_id == TileSet::INVALID_SOURCE) {
from.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
to.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
coords_from_property_editor->hide();
coords_to_property_editor->hide();
alternative_from_property_editor->hide();
alternative_to_property_editor->hide();
} else if (from.get_atlas_coords().x == -1 || from.get_atlas_coords().y == -1) {
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
coords_from_property_editor->show();
coords_to_property_editor->show();
alternative_from_property_editor->hide();
alternative_to_property_editor->hide();
} else {
coords_from_property_editor->show();
coords_to_property_editor->show();
alternative_from_property_editor->show();
alternative_to_property_editor->show();
}
source_from_property_editor->update_property();
source_to_property_editor->update_property();
coords_from_property_editor->update_property();
coords_to_property_editor->update_property();
alternative_from_property_editor->update_property();
alternative_to_property_editor->update_property();
}
void TileProxiesManagerDialog::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing) {
_set(p_path, p_value);
}
void TileProxiesManagerDialog::_add_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (from.source_id != TileSet::INVALID_SOURCE && to.source_id != TileSet::INVALID_SOURCE) {
Vector2i from_coords = from.get_atlas_coords();
Vector2i to_coords = to.get_atlas_coords();
if (from_coords.x >= 0 && from_coords.y >= 0 && to_coords.x >= 0 && to_coords.y >= 0) {
if (from.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE && to.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE) {
undo_redo->create_action(TTR("Create Alternative-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile, to.source_id, to.get_atlas_coords(), to.alternative_tile);
if (tile_set->has_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile)) {
Array a = tile_set->get_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", to.source_id, to.get_atlas_coords(), to.alternative_tile, a[0], a[1], a[2]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile);
}
} else {
undo_redo->create_action(TTR("Create Coords-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", from.source_id, from.get_atlas_coords(), to.source_id, to.get_atlas_coords());
if (tile_set->has_coords_level_tile_proxy(from.source_id, from.get_atlas_coords())) {
Array a = tile_set->get_coords_level_tile_proxy(from.source_id, from.get_atlas_coords());
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", to.source_id, to.get_atlas_coords(), a[0], a[1]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", from.source_id, from.get_atlas_coords());
}
}
} else {
undo_redo->create_action(TTR("Create source-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_source_level_tile_proxy", from.source_id, to.source_id);
if (tile_set->has_source_level_tile_proxy(from.source_id)) {
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", to.source_id, tile_set->get_source_level_tile_proxy(from.source_id));
} else {
undo_redo->add_undo_method(*tile_set, "remove_source_level_tile_proxy", from.source_id);
}
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
commited_actions_count++;
}
}
void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Invalid Tile Proxies"));
undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies");
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
}
void TileProxiesManagerDialog::_clear_all_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Tile Proxies"));
undo_redo->add_do_method(*tile_set, "clear_tile_proxies");
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
}
bool TileProxiesManagerDialog::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "from_source") {
from.source_id = MAX(int(p_value), -1);
} else if (p_name == "from_coords") {
from.set_atlas_coords(Vector2i(p_value).max(Vector2i(-1, -1)));
} else if (p_name == "from_alternative") {
from.alternative_tile = MAX(int(p_value), -1);
} else if (p_name == "to_source") {
to.source_id = MAX(int(p_value), 0);
} else if (p_name == "to_coords") {
to.set_atlas_coords(Vector2i(p_value).max(Vector2i(0, 0)));
} else if (p_name == "to_alternative") {
to.alternative_tile = MAX(int(p_value), 0);
} else {
return false;
}
_update_enabled_property_editors();
return true;
}
bool TileProxiesManagerDialog::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "from_source") {
r_ret = from.source_id;
} else if (p_name == "from_coords") {
r_ret = from.get_atlas_coords();
} else if (p_name == "from_alternative") {
r_ret = from.alternative_tile;
} else if (p_name == "to_source") {
r_ret = to.source_id;
} else if (p_name == "to_coords") {
r_ret = to.get_atlas_coords();
} else if (p_name == "to_alternative") {
r_ret = to.alternative_tile;
} else {
return false;
}
return true;
}
void TileProxiesManagerDialog::_unhandled_key_input(Ref<InputEvent> p_event) {
ERR_FAIL_COND(p_event.is_null());
if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) {
if (!is_inside_tree() || !is_visible()) {
return;
}
if (popup_menu->activate_item_by_event(p_event, false)) {
set_input_as_handled();
}
}
}
void TileProxiesManagerDialog::cancel_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < commited_actions_count; i++) {
undo_redo->undo();
}
commited_actions_count = 0;
}
void TileProxiesManagerDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_lists"), &TileProxiesManagerDialog::_update_lists);
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileProxiesManagerDialog::_unhandled_key_input);
}
void TileProxiesManagerDialog::update_tile_set(Ref<TileSet> p_tile_set) {
ERR_FAIL_COND(!p_tile_set.is_valid());
tile_set = p_tile_set;
commited_actions_count = 0;
_update_lists();
}
TileProxiesManagerDialog::TileProxiesManagerDialog() {
// Tile proxy management window.
set_title(TTR("Tile Proxies Management"));
set_process_unhandled_key_input(true);
to.source_id = 0;
to.set_atlas_coords(Vector2i());
to.alternative_tile = 0;
VBoxContainer *vbox_container = memnew(VBoxContainer);
vbox_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vbox_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_child(vbox_container);
Label *source_level_label = memnew(Label);
source_level_label->set_text(TTR("Source-level proxies"));
vbox_container->add_child(source_level_label);
source_level_list = memnew(ItemList);
source_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
source_level_list->set_select_mode(ItemList::SELECT_MULTI);
source_level_list->set_allow_rmb_select(true);
source_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(source_level_list));
vbox_container->add_child(source_level_list);
Label *coords_level_label = memnew(Label);
coords_level_label->set_text(TTR("Coords-level proxies"));
vbox_container->add_child(coords_level_label);
coords_level_list = memnew(ItemList);
coords_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
coords_level_list->set_select_mode(ItemList::SELECT_MULTI);
coords_level_list->set_allow_rmb_select(true);
coords_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(coords_level_list));
vbox_container->add_child(coords_level_list);
Label *alternative_level_label = memnew(Label);
alternative_level_label->set_text(TTR("Alternative-level proxies"));
vbox_container->add_child(alternative_level_label);
alternative_level_list = memnew(ItemList);
alternative_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_level_list->set_select_mode(ItemList::SELECT_MULTI);
alternative_level_list->set_allow_rmb_select(true);
alternative_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(alternative_level_list));
vbox_container->add_child(alternative_level_list);
popup_menu = memnew(PopupMenu);
popup_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_delete"));
popup_menu->connect("id_pressed", callable_mp(this, &TileProxiesManagerDialog::_menu_id_pressed));
add_child(popup_menu);
// Add proxy panel.
HSeparator *h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
Label *add_label = memnew(Label);
add_label->set_text(TTR("Add a new tile proxy:"));
vbox_container->add_child(add_label);
HBoxContainer *hboxcontainer = memnew(HBoxContainer);
vbox_container->add_child(hboxcontainer);
// From
VBoxContainer *vboxcontainer_from = memnew(VBoxContainer);
vboxcontainer_from->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hboxcontainer->add_child(vboxcontainer_from);
source_from_property_editor = memnew(EditorPropertyInteger);
source_from_property_editor->set_label(TTR("From Source"));
source_from_property_editor->set_object_and_property(this, "from_source");
source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
source_from_property_editor->set_selectable(false);
source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
source_from_property_editor->setup(-1, 99999, 1, false, true, false);
vboxcontainer_from->add_child(source_from_property_editor);
coords_from_property_editor = memnew(EditorPropertyVector2i);
coords_from_property_editor->set_label(TTR("From Coords"));
coords_from_property_editor->set_object_and_property(this, "from_coords");
coords_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
coords_from_property_editor->set_selectable(false);
coords_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
coords_from_property_editor->setup(-1, 99999, true);
coords_from_property_editor->hide();
vboxcontainer_from->add_child(coords_from_property_editor);
alternative_from_property_editor = memnew(EditorPropertyInteger);
alternative_from_property_editor->set_label(TTR("From Alternative"));
alternative_from_property_editor->set_object_and_property(this, "from_alternative");
alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
alternative_from_property_editor->set_selectable(false);
alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
alternative_from_property_editor->setup(-1, 99999, 1, false, true, false);
alternative_from_property_editor->hide();
vboxcontainer_from->add_child(alternative_from_property_editor);
// To
VBoxContainer *vboxcontainer_to = memnew(VBoxContainer);
vboxcontainer_to->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hboxcontainer->add_child(vboxcontainer_to);
source_to_property_editor = memnew(EditorPropertyInteger);
source_to_property_editor->set_label(TTR("To Source"));
source_to_property_editor->set_object_and_property(this, "to_source");
source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
source_to_property_editor->set_selectable(false);
source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
source_to_property_editor->setup(-1, 99999, 1, false, true, false);
vboxcontainer_to->add_child(source_to_property_editor);
coords_to_property_editor = memnew(EditorPropertyVector2i);
coords_to_property_editor->set_label(TTR("To Coords"));
coords_to_property_editor->set_object_and_property(this, "to_coords");
coords_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
coords_to_property_editor->set_selectable(false);
coords_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
coords_to_property_editor->setup(-1, 99999, true);
coords_to_property_editor->hide();
vboxcontainer_to->add_child(coords_to_property_editor);
alternative_to_property_editor = memnew(EditorPropertyInteger);
alternative_to_property_editor->set_label(TTR("To Alternative"));
alternative_to_property_editor->set_object_and_property(this, "to_alternative");
alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
alternative_to_property_editor->set_selectable(false);
alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
alternative_to_property_editor->setup(-1, 99999, 1, false, true, false);
alternative_to_property_editor->hide();
vboxcontainer_to->add_child(alternative_to_property_editor);
Button *add_button = memnew(Button);
add_button->set_text(TTR("Add"));
add_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
add_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_add_button_pressed));
vbox_container->add_child(add_button);
h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
// Generic actions.
Label *generic_actions_label = memnew(Label);
generic_actions_label->set_text(TTR("Global actions:"));
vbox_container->add_child(generic_actions_label);
hboxcontainer = memnew(HBoxContainer);
vbox_container->add_child(hboxcontainer);
Button *clear_invalid_button = memnew(Button);
clear_invalid_button->set_text(TTR("Clear Invalid"));
clear_invalid_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_invalid_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_invalid_button_pressed));
hboxcontainer->add_child(clear_invalid_button);
Button *clear_all_button = memnew(Button);
clear_all_button->set_text(TTR("Clear All"));
clear_all_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_all_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_all_button_pressed));
hboxcontainer->add_child(clear_all_button);
h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
}

View File

@ -0,0 +1,89 @@
/**************************************************************************/
/* tile_proxies_manager_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_PROXIES_MANAGER_DIALOG_H
#define TILE_PROXIES_MANAGER_DIALOG_H
#include "editor/editor_properties.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
class EditorPropertyVector2i;
class EditorUndoRedoManager;
class TileProxiesManagerDialog : public ConfirmationDialog {
GDCLASS(TileProxiesManagerDialog, ConfirmationDialog);
private:
int commited_actions_count = 0;
Ref<TileSet> tile_set;
TileMapCell from;
TileMapCell to;
// GUI
ItemList *source_level_list = nullptr;
ItemList *coords_level_list = nullptr;
ItemList *alternative_level_list = nullptr;
EditorPropertyInteger *source_from_property_editor = nullptr;
EditorPropertyVector2i *coords_from_property_editor = nullptr;
EditorPropertyInteger *alternative_from_property_editor = nullptr;
EditorPropertyInteger *source_to_property_editor = nullptr;
EditorPropertyVector2i *coords_to_property_editor = nullptr;
EditorPropertyInteger *alternative_to_property_editor = nullptr;
PopupMenu *popup_menu = nullptr;
void _right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list);
void _menu_id_pressed(int p_id);
void _delete_selected_bindings();
void _update_lists();
void _update_enabled_property_editors();
void _property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing);
void _add_button_pressed();
void _clear_invalid_button_pressed();
void _clear_all_button_pressed();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _unhandled_key_input(Ref<InputEvent> p_event);
virtual void cancel_pressed() override;
static void _bind_methods();
public:
void update_tile_set(Ref<TileSet> p_tile_set);
TileProxiesManagerDialog();
};
#endif // TILE_PROXIES_MANAGER_DIALOG_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,336 @@
/**************************************************************************/
/* tile_set_atlas_source_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_SET_ATLAS_SOURCE_EDITOR_H
#define TILE_SET_ATLAS_SOURCE_EDITOR_H
#include "tile_atlas_view.h"
#include "tile_data_editors.h"
#include "scene/gui/split_container.h"
#include "scene/resources/2d/tile_set.h"
class Popup;
class TileSet;
class Tree;
class VSeparator;
class TileSetAtlasSourceEditor : public HSplitContainer {
GDCLASS(TileSetAtlasSourceEditor, HSplitContainer);
public:
// A class to store which tiles are selected.
struct TileSelection {
Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS;
int alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
bool operator<(const TileSelection &p_other) const {
if (tile == p_other.tile) {
return alternative < p_other.alternative;
} else {
return tile < p_other.tile;
}
}
};
// -- Proxy object for an atlas source, needed by the inspector --
class TileSetAtlasSourceProxyObject : public Object {
GDCLASS(TileSetAtlasSourceProxyObject, Object);
private:
Ref<TileSet> tile_set;
Ref<TileSetAtlasSource> tile_set_atlas_source;
int source_id = TileSet::INVALID_SOURCE;
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_id(int p_id);
int get_id() const;
void edit(Ref<TileSet> p_tile_set, Ref<TileSetAtlasSource> p_tile_set_atlas_source, int p_source_id);
Ref<TileSetAtlasSource> get_edited() { return tile_set_atlas_source; };
};
// -- Proxy object for a tile, needed by the inspector --
class AtlasTileProxyObject : public Object {
GDCLASS(AtlasTileProxyObject, Object);
private:
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr;
Ref<TileSetAtlasSource> tile_set_atlas_source;
RBSet<TileSelection> tiles = RBSet<TileSelection>();
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:
Ref<TileSetAtlasSource> get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; };
RBSet<TileSelection> get_edited_tiles() const { return tiles; };
// Update the proxyed object.
void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles = RBSet<TileSelection>());
AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) {
tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor;
}
};
class TileAtlasControl : public Control {
TileSetAtlasSourceEditor *editor = nullptr;
public:
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
TileAtlasControl(TileSetAtlasSourceEditor *p_editor) { editor = p_editor; }
};
friend class TileAtlasControl;
private:
bool read_only = false;
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int tile_set_atlas_source_id = TileSet::INVALID_SOURCE;
Ref<Texture2D> atlas_source_texture;
bool tile_set_changed_needs_update = false;
// -- Properties painting --
ScrollContainer *tile_data_editors_scroll = nullptr;
VBoxContainer *tile_data_painting_editor_container = nullptr;
Label *tile_data_editors_label = nullptr;
Button *tile_data_editor_dropdown_button = nullptr;
Popup *tile_data_editors_popup = nullptr;
Tree *tile_data_editors_tree = nullptr;
void _tile_data_editor_dropdown_button_draw();
void _tile_data_editor_dropdown_button_pressed();
// -- Tile data editors --
String current_property;
Control *current_tile_data_editor_toolbar = nullptr;
HashMap<String, TileDataEditor *> tile_data_editors;
TileDataEditor *current_tile_data_editor = nullptr;
void _tile_data_editors_tree_selected();
// -- Inspector --
AtlasTileProxyObject *tile_proxy_object = nullptr;
EditorInspector *tile_inspector = nullptr;
Label *tile_inspector_no_tile_selected_label = nullptr;
String selected_property;
void _inspector_property_selected(const String &p_property);
TileSetAtlasSourceProxyObject *atlas_source_proxy_object = nullptr;
EditorInspector *atlas_source_inspector = nullptr;
// -- Atlas view --
TileAtlasView *tile_atlas_view = nullptr;
VBoxContainer *tile_create_help = nullptr;
// Dragging
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_CREATE_TILES,
DRAG_TYPE_CREATE_TILES_USING_RECT,
DRAG_TYPE_CREATE_BIG_TILE,
DRAG_TYPE_REMOVE_TILES,
DRAG_TYPE_REMOVE_TILES_USING_RECT,
DRAG_TYPE_MOVE_TILE,
DRAG_TYPE_RECT_SELECT,
DRAG_TYPE_MAY_POPUP_MENU,
// Warning: keep in this order.
DRAG_TYPE_RESIZE_TOP_LEFT,
DRAG_TYPE_RESIZE_TOP,
DRAG_TYPE_RESIZE_TOP_RIGHT,
DRAG_TYPE_RESIZE_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM,
DRAG_TYPE_RESIZE_BOTTOM_LEFT,
DRAG_TYPE_RESIZE_LEFT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2i drag_start_mouse_pos;
Vector2i drag_last_mouse_pos;
Vector2i drag_current_tile;
Rect2i drag_start_tile_shape;
RBSet<Vector2i> drag_modified_tiles;
void _end_dragging();
HashMap<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas);
// Popup functions.
enum MenuOptions {
TILE_CREATE,
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
ADVANCED_CLEANUP_TILES,
};
Vector2i menu_option_coords;
int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
void _menu_option(int p_option);
// Tool buttons.
Ref<ButtonGroup> tools_button_group;
Button *tool_setup_atlas_source_button = nullptr;
Button *tool_select_button = nullptr;
Button *tool_paint_button = nullptr;
Label *tool_tile_id_label = nullptr;
// Tool settings.
HBoxContainer *tool_settings = nullptr;
HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr;
Button *tools_settings_erase_button = nullptr;
MenuButton *tool_advanced_menu_button = nullptr;
TextureRect *outside_tiles_warning = nullptr;
// Selection.
RBSet<TileSelection> selection;
void _set_selection_from_array(const Array &p_selection);
Array _get_selection_as_array();
// A control on the tile atlas to draw and handle input events.
Vector2i hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
PopupMenu *base_tile_popup_menu = nullptr;
PopupMenu *empty_base_tile_popup_menu = nullptr;
Ref<Texture2D> resize_handle;
Ref<Texture2D> resize_handle_disabled;
Control *tile_atlas_control = nullptr;
Control *tile_atlas_control_unscaled = nullptr;
void _tile_atlas_control_draw();
void _tile_atlas_control_unscaled_draw();
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_view_transform_changed();
// A control over the alternative tiles.
Vector3i hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
PopupMenu *alternative_tile_popup_menu = nullptr;
Control *alternative_tiles_control = nullptr;
Control *alternative_tiles_control_unscaled = nullptr;
void _tile_alternatives_control_draw();
void _tile_alternatives_control_unscaled_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
// -- Update functions --
void _update_tile_id_label();
void _update_source_inspector();
void _update_fix_selected_and_hovered_tiles();
void _update_atlas_source_inspector();
void _update_tile_inspector();
void _update_tile_data_editors();
void _update_current_tile_data_editor();
void _update_manage_tile_properties_button();
void _update_atlas_view();
void _update_toolbar();
// -- Misc --
void _auto_create_tiles();
void _auto_remove_tiles();
void _cancel_auto_create_tiles();
AcceptDialog *confirm_auto_create_tiles = nullptr;
Vector<Ref<TileSetAtlasSource>> atlases_to_auto_create_tiles;
Vector2i _get_drag_offset_tile_coords(const Vector2i &p_offset) const;
void _update_source_texture();
void _check_outside_tiles();
void _cleanup_outside_tiles();
void _tile_set_changed();
void _tile_proxy_object_changed(const String &p_what);
void _atlas_source_proxy_object_changed(const String &p_what);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
protected:
void _notification(int p_what);
static void _bind_methods();
// -- input events --
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_new_atlases(const Vector<Ref<TileSetAtlasSource>> &p_atlases);
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
};
class EditorPropertyTilePolygon : public EditorProperty {
GDCLASS(EditorPropertyTilePolygon, EditorProperty);
StringName count_property;
String element_pattern;
String base_type;
void _add_focusable_children(Node *p_node);
GenericTilePolygonEditor *generic_tile_polygon_editor = nullptr;
void _polygons_changed();
public:
virtual void update_property() override;
void setup_single_mode(const StringName &p_property, const String &p_base_type);
void setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type);
EditorPropertyTilePolygon();
};
class EditorInspectorPluginTileData : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginTileData, EditorInspectorPlugin);
void _occlusion_polygon_set_callback();
void _polygons_changed(Object *p_generic_tile_polygon_editor, Object *p_object, const String &p_path);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
#endif // TILE_SET_ATLAS_SOURCE_EDITOR_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,145 @@
/**************************************************************************/
/* tile_set_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_SET_EDITOR_H
#define TILE_SET_EDITOR_H
#include "atlas_merging_dialog.h"
#include "scene/gui/tab_bar.h"
#include "scene/resources/2d/tile_set.h"
#include "tile_proxies_manager_dialog.h"
#include "tile_set_atlas_source_editor.h"
#include "tile_set_scenes_collection_source_editor.h"
class AcceptDialog;
class SpinBox;
class HBoxContainer;
class SplitContainer;
class EditorFileDialog;
class EditorInspectorPlugin;
class TileSetEditor : public Control {
GDCLASS(TileSetEditor, Control);
static TileSetEditor *singleton;
private:
bool read_only = false;
Ref<TileSet> tile_set;
bool tile_set_changed_needs_update = false;
HSplitContainer *split_container = nullptr;
// TabBar.
HBoxContainer *tile_set_toolbar = nullptr;
TabBar *tabs_bar = nullptr;
// Tiles.
Label *no_source_selected_label = nullptr;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor = nullptr;
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void _load_texture_files(const Vector<String> &p_paths);
void _update_sources_list(int force_selected_id = -1);
// Sources management.
Button *sources_delete_button = nullptr;
MenuButton *sources_add_button = nullptr;
MenuButton *source_sort_button = nullptr;
MenuButton *sources_advanced_menu_button = nullptr;
ItemList *sources_list = nullptr;
Ref<Texture2D> missing_texture_texture;
void _source_selected(int p_source_index);
void _source_delete_pressed();
void _source_add_id_pressed(int p_id_pressed);
void _sources_advanced_menu_id_pressed(int p_id_pressed);
void _set_source_sort(int p_sort);
EditorFileDialog *texture_file_dialog = nullptr;
AtlasMergingDialog *atlas_merging_dialog = nullptr;
TileProxiesManagerDialog *tile_proxies_manager_dialog = nullptr;
bool first_edit = true;
// Patterns.
ItemList *patterns_item_list = nullptr;
Label *patterns_help_label = nullptr;
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
bool select_last_pattern = false;
void _update_patterns_list();
// Expanded editor.
PanelContainer *expanded_area = nullptr;
Control *expanded_editor = nullptr;
ObjectID expanded_editor_parent;
LocalVector<SplitContainer *> disable_on_expand;
void _tile_set_changed();
void _tab_changed(int p_tab_changed);
void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
protected:
void _notification(int p_what);
public:
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
void edit(Ref<TileSet> p_tile_set);
void add_expanded_editor(Control *p_editor);
void remove_expanded_editor();
void register_split(SplitContainer *p_split);
TileSetEditor();
};
class TileSourceInspectorPlugin : public EditorInspectorPlugin {
GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
AcceptDialog *id_edit_dialog = nullptr;
Label *id_label = nullptr;
SpinBox *id_input = nullptr;
Object *edited_source = nullptr;
void _show_id_edit_dialog(Object *p_for_source);
void _confirm_change_id();
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
#endif // TILE_SET_EDITOR_H

View File

@ -0,0 +1,591 @@
/**************************************************************************/
/* tile_set_scenes_collection_source_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_set_scenes_collection_source_editor.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/plugins/tiles/tile_set_editor.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/item_list.h"
#include "scene/gui/label.h"
#include "scene/gui/split_container.h"
#include "core/core_string_names.h"
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id(int p_id) {
ERR_FAIL_COND(p_id < 0);
if (source_id == p_id) {
return;
}
ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Scenes Collection source ID. Another TileSet source exists with id %d.", p_id));
int previous_source = source_id;
source_id = p_id; // source_id must be updated before, because it's used by the source list update.
tile_set->set_source_id(previous_source, p_id);
emit_signal(SNAME("changed"), "id");
}
int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id() {
return source_id;
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name == "name") {
// Use the resource_name property to store the source's name.
name = "resource_name";
}
bool valid = false;
tile_set_scenes_collection_source->set(name, p_value, &valid);
if (valid) {
emit_signal(SNAME("changed"), String(name).utf8().get_data());
}
return valid;
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!tile_set_scenes_collection_source) {
return false;
}
String name = p_name;
if (name == "name") {
// Use the resource_name property to store the source's name.
name = "resource_name";
}
bool valid = false;
r_ret = tile_set_scenes_collection_source->get(name, &valid);
return valid;
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() {
// -- Shape and layout --
ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id);
ClassDB::bind_method(D_METHOD("get_id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id);
ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
if (tile_set == p_tile_set && tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && source_id == p_source_id) {
return;
}
// Disconnect to changes.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
}
tile_set = p_tile_set;
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
source_id = p_source_id;
// Connect to changes.
if (tile_set_scenes_collection_source) {
if (!tile_set_scenes_collection_source->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
tile_set_scenes_collection_source->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
}
}
notify_property_list_changed();
}
// -- Proxy object used by the tile inspector --
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
if (!tile_set_scenes_collection_source) {
return false;
}
if (p_name == "id") {
int as_int = int(p_value);
ERR_FAIL_COND_V(as_int < 0, false);
ERR_FAIL_COND_V(tile_set_scenes_collection_source->has_scene_tile_id(as_int), false);
tile_set_scenes_collection_source->set_scene_tile_id(scene_id, as_int);
scene_id = as_int;
emit_signal(SNAME("changed"), "id");
for (int i = 0; i < tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_count(); i++) {
if (int(tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_metadata(i)) == scene_id) {
tile_set_scenes_collection_source_editor->scene_tiles_list->select(i);
break;
}
}
return true;
} else if (p_name == "scene") {
tile_set_scenes_collection_source->set_scene_tile_scene(scene_id, p_value);
emit_signal(SNAME("changed"), "scene");
return true;
} else if (p_name == "display_placeholder") {
tile_set_scenes_collection_source->set_scene_tile_display_placeholder(scene_id, p_value);
emit_signal(SNAME("changed"), "display_placeholder");
return true;
}
return false;
}
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!tile_set_scenes_collection_source) {
return false;
}
if (p_name == "id") {
r_ret = scene_id;
return true;
} else if (p_name == "scene") {
r_ret = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
return true;
} else if (p_name == "display_placeholder") {
r_ret = tile_set_scenes_collection_source->get_scene_tile_display_placeholder(scene_id);
return true;
}
return false;
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
if (!tile_set_scenes_collection_source) {
return;
}
p_list->push_back(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"));
p_list->push_back(PropertyInfo(Variant::BOOL, "display_placeholder", PROPERTY_HINT_NONE, ""));
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_scene_id) {
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
return;
}
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
scene_id = p_scene_id;
notify_property_list_changed();
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_bind_methods() {
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
}
void TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed(const String &p_what) {
if (p_what == "id") {
emit_signal(SNAME("source_id_changed"), scenes_collection_source_proxy_object->get_id());
}
}
void TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed() {
tile_set_scenes_collection_source_changed_needs_update = true;
}
void TileSetScenesCollectionSourceEditor::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud) {
int index = p_ud;
if (index >= 0 && index < scene_tiles_list->get_item_count()) {
scene_tiles_list->set_item_icon(index, p_preview);
}
}
void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_index) {
Ref<PackedScene> packed_scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_tiles_list->get_item_metadata(p_index));
if (packed_scene.is_valid()) {
EditorNode::get_singleton()->open_request(packed_scene->get_path());
}
}
void TileSetScenesCollectionSourceEditor::_source_add_pressed() {
if (!scene_select_dialog) {
scene_select_dialog = memnew(EditorFileDialog);
add_child(scene_select_dialog);
scene_select_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
scene_select_dialog->connect("file_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scene_file_selected));
for (const String &E : Vector<String>{ "tscn", "scn" }) {
scene_select_dialog->add_filter("*." + E, E.to_upper());
}
}
scene_select_dialog->popup_file_dialog();
}
void TileSetScenesCollectionSourceEditor::_scene_file_selected(const String &p_path) {
Ref<PackedScene> scene = ResourceLoader::load(p_path);
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->commit_action();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_source_delete_pressed() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
ERR_FAIL_COND(selected_indices.is_empty());
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id);
undo_redo->commit_action();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_update_source_inspector() {
// Update the proxy object.
scenes_collection_source_proxy_object->edit(tile_set, tile_set_scenes_collection_source, tile_set_source_id);
}
void TileSetScenesCollectionSourceEditor::_update_tile_inspector() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
bool has_atlas_tile_selected = (selected_indices.size() > 0);
// Update the proxy object.
if (has_atlas_tile_selected) {
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
tile_proxy_object->edit(tile_set_scenes_collection_source, scene_id);
}
// Update visibility.
tile_inspector_label->set_visible(has_atlas_tile_selected);
tile_inspector->set_visible(has_atlas_tile_selected);
}
void TileSetScenesCollectionSourceEditor::_update_action_buttons() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
scene_tile_delete_button->set_disabled(selected_indices.size() <= 0 || read_only);
}
void TileSetScenesCollectionSourceEditor::_update_scenes_list() {
if (!tile_set_scenes_collection_source) {
return;
}
// Get the previously selected id.
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
int old_selected_scene_id = (selected_indices.size() > 0) ? int(scene_tiles_list->get_item_metadata(selected_indices[0])) : -1;
// Clear the list.
scene_tiles_list->clear();
// Rebuild the list.
int to_reselect = -1;
for (int i = 0; i < tile_set_scenes_collection_source->get_scene_tiles_count(); i++) {
int scene_id = tile_set_scenes_collection_source->get_scene_tile_id(i);
Ref<PackedScene> scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
int item_index = 0;
if (scene.is_valid()) {
item_index = scene_tiles_list->add_item(vformat("%s (path:%s id:%d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id));
Variant udata = i;
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
} else {
item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_editor_theme_icon(SNAME("PackedScene")));
}
scene_tiles_list->set_item_metadata(item_index, scene_id);
if (old_selected_scene_id >= 0 && scene_id == old_selected_scene_id) {
to_reselect = i;
}
}
if (scene_tiles_list->get_item_count() == 0) {
scene_tiles_list->add_item(TTR("Drag and drop scenes here or use the Add button."));
scene_tiles_list->set_item_disabled(-1, true);
}
// Reselect if needed.
if (to_reselect >= 0) {
scene_tiles_list->select(to_reselect);
}
// Icon size update.
int int_size = int(EDITOR_GET("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size));
}
void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "id", TTR("The tile's unique identifier within this TileSet. Each tile stores its source ID, so changing one may make tiles invalid."));
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "name", TTR("The human-readable name for the scene collection. Use a descriptive name here for organizational purposes (such as \"obstacles\", \"decoration\", etc.)."));
tile_inspector->edit(tile_proxy_object);
tile_inspector->add_custom_property_description("SceneTileProxyObject", "id", TTR("ID of the scene tile in the collection. Each painted tile has associated ID, so changing this property may cause your TileMaps to not display properly."));
tile_inspector->add_custom_property_description("SceneTileProxyObject", "scene", TTR("Absolute path to the scene associated with this tile."));
tile_inspector->add_custom_property_description("SceneTileProxyObject", "display_placeholder", TTR("If [code]true[/code], a placeholder marker will be displayed on top of the scene's preview. The marker is displayed anyway if the scene has no valid preview."));
} break;
case NOTIFICATION_THEME_CHANGED: {
scene_tile_add_button->set_icon(get_editor_theme_icon(SNAME("Add")));
scene_tile_delete_button->set_icon(get_editor_theme_icon(SNAME("Remove")));
_update_scenes_list();
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (tile_set_scenes_collection_source_changed_needs_update) {
read_only = false;
// Add the listener again and check for read-only status.
if (tile_set.is_valid()) {
read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set);
}
// Update everything.
_update_source_inspector();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
tile_set_scenes_collection_source_changed_needs_update = false;
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
// Update things just in case.
_update_scenes_list();
_update_action_buttons();
} break;
}
}
void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
bool new_read_only_state = false;
if (p_tile_set.is_valid()) {
new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set);
}
if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) {
return;
}
// Remove listener for old objects.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Change the edited object.
tile_set = p_tile_set;
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
tile_set_source_id = p_source_id;
// Read-only status is false by default
read_only = new_read_only_state;
if (tile_set.is_valid()) {
scenes_collection_source_inspector->set_read_only(read_only);
tile_inspector->set_read_only(read_only);
scene_tile_add_button->set_disabled(read_only);
}
// Add the listener again.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Update everything.
_update_source_inspector();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
if (p_from == scene_tiles_list) {
// Handle dropping a texture in the list of atlas resources.
Dictionary d = p_data;
Vector<String> files = d["files"];
for (int i = 0; i < files.size(); i++) {
Ref<PackedScene> resource = ResourceLoader::load(files[i]);
if (resource.is_valid()) {
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->commit_action();
}
}
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
}
bool TileSetScenesCollectionSourceEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
if (p_from == scene_tiles_list) {
Dictionary d = p_data;
if (!d.has("type")) {
return false;
}
// Check if we have a Texture2D.
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
if (files.size() == 0) {
return false;
}
for (int i = 0; i < files.size(); i++) {
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
if (!ClassDB::is_parent_class(ftype, "PackedScene")) {
return false;
}
}
return true;
}
}
return false;
}
void TileSetScenesCollectionSourceEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done);
}
TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
// -- Right side --
HSplitContainer *split_container_right_side = memnew(HSplitContainer);
split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(split_container_right_side);
// Middle panel.
ScrollContainer *middle_panel = memnew(ScrollContainer);
middle_panel->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
middle_panel->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
split_container_right_side->add_child(middle_panel);
VBoxContainer *middle_vbox_container = memnew(VBoxContainer);
middle_vbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
middle_panel->add_child(middle_vbox_container);
// Scenes collection source inspector.
scenes_collection_source_inspector_label = memnew(Label);
scenes_collection_source_inspector_label->set_text(TTR("Scenes collection properties:"));
middle_vbox_container->add_child(scenes_collection_source_inspector_label);
scenes_collection_source_proxy_object = memnew(TileSetScenesCollectionProxyObject());
scenes_collection_source_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed));
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
scenes_collection_source_inspector->set_use_doc_hints(true);
scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
middle_vbox_container->add_child(scenes_collection_source_inspector);
// Tile inspector.
tile_inspector_label = memnew(Label);
tile_inspector_label->set_text(TTR("Tile properties:"));
tile_inspector_label->hide();
middle_vbox_container->add_child(tile_inspector_label);
tile_proxy_object = memnew(SceneTileProxyObject(this));
tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_scenes_list).unbind(1));
tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
tile_inspector = memnew(EditorInspector);
tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tile_inspector->set_use_doc_hints(true);
tile_inspector->set_use_folding(true);
middle_vbox_container->add_child(tile_inspector);
// Scenes list.
VBoxContainer *right_vbox_container = memnew(VBoxContainer);
split_container_right_side->add_child(right_vbox_container);
scene_tiles_list = memnew(ItemList);
scene_tiles_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
SET_DRAG_FORWARDING_CDU(scene_tiles_list, TileSetScenesCollectionSourceEditor);
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1));
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated));
scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
right_vbox_container->add_child(scene_tiles_list);
HBoxContainer *scenes_bottom_actions = memnew(HBoxContainer);
right_vbox_container->add_child(scenes_bottom_actions);
scene_tile_add_button = memnew(Button);
scene_tile_add_button->set_theme_type_variation("FlatButton");
scene_tile_add_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_add_pressed));
scenes_bottom_actions->add_child(scene_tile_add_button);
scene_tile_delete_button = memnew(Button);
scene_tile_delete_button->set_theme_type_variation("FlatButton");
scene_tile_delete_button->set_disabled(true);
scene_tile_delete_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed));
scenes_bottom_actions->add_child(scene_tile_delete_button);
}
TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() {
memdelete(scenes_collection_source_proxy_object);
memdelete(tile_proxy_object);
}

View File

@ -0,0 +1,148 @@
/**************************************************************************/
/* tile_set_scenes_collection_source_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H
#define TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H
#include "editor/editor_inspector.h"
#include "scene/gui/box_container.h"
#include "scene/resources/2d/tile_set.h"
class Button;
class ItemList;
class Label;
class EditorFileDialog;
class TileSetScenesCollectionSourceEditor : public HBoxContainer {
GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer);
private:
// -- Proxy object for an atlas source, needed by the inspector --
class TileSetScenesCollectionProxyObject : public Object {
GDCLASS(TileSetScenesCollectionProxyObject, Object);
private:
Ref<TileSet> tile_set;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int source_id = -1;
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_id(int p_id);
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
};
// -- Proxy object for a tile, needed by the inspector --
class SceneTileProxyObject : public Object {
GDCLASS(SceneTileProxyObject, Object);
private:
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int source_id;
int scene_id;
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:
// Update the proxyed object.
void edit(TileSetScenesCollectionSource *p_tile_set_atlas_source, int p_scene_id);
SceneTileProxyObject(TileSetScenesCollectionSourceEditor *p_tiles_set_scenes_collection_source_editor) {
tile_set_scenes_collection_source_editor = p_tiles_set_scenes_collection_source_editor;
}
};
private:
bool read_only = false;
Ref<TileSet> tile_set;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int tile_set_source_id = -1;
bool tile_set_scenes_collection_source_changed_needs_update = false;
// Source inspector.
TileSetScenesCollectionProxyObject *scenes_collection_source_proxy_object = nullptr;
Label *scenes_collection_source_inspector_label = nullptr;
EditorInspector *scenes_collection_source_inspector = nullptr;
// Tile inspector.
SceneTileProxyObject *tile_proxy_object = nullptr;
Label *tile_inspector_label = nullptr;
EditorInspector *tile_inspector = nullptr;
ItemList *scene_tiles_list = nullptr;
Button *scene_tile_add_button = nullptr;
Button *scene_tile_delete_button = nullptr;
EditorFileDialog *scene_select_dialog = nullptr;
void _tile_set_scenes_collection_source_changed();
void _scenes_collection_source_proxy_object_changed(const String &p_what);
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
void _scenes_list_item_activated(int p_index);
void _source_add_pressed();
void _scene_file_selected(const String &p_path);
void _source_delete_pressed();
// Update methods.
void _update_source_inspector();
void _update_tile_inspector();
void _update_scenes_list();
void _update_action_buttons();
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
TileSetScenesCollectionSourceEditor();
~TileSetScenesCollectionSourceEditor();
};
#endif // TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H

View File

@ -0,0 +1,571 @@
/**************************************************************************/
/* tiles_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tiles_editor_plugin.h"
#include "tile_set_editor.h"
#include "core/os/mutex.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/separator.h"
#include "scene/resources/2d/tile_set.h"
#include "scene/resources/image_texture.h"
TilesEditorUtils *TilesEditorUtils::singleton = nullptr;
TileMapEditorPlugin *tile_map_plugin_singleton = nullptr;
TileSetEditorPlugin *tile_set_plugin_singleton = nullptr;
void TilesEditorUtils::_preview_frame_started() {
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_pattern_preview_done));
}
void TilesEditorUtils::_pattern_preview_done() {
pattern_preview_done.post();
}
void TilesEditorUtils::_thread_func(void *ud) {
TilesEditorUtils *te = static_cast<TilesEditorUtils *>(ud);
set_current_thread_safe_for_nodes(true);
te->_thread();
}
void TilesEditorUtils::_thread() {
pattern_thread_exited.clear();
while (!pattern_thread_exit.is_set()) {
pattern_preview_sem.wait();
pattern_preview_mutex.lock();
if (pattern_preview_queue.size() == 0) {
pattern_preview_mutex.unlock();
} else {
QueueItem item = pattern_preview_queue.front()->get();
pattern_preview_queue.pop_front();
pattern_preview_mutex.unlock();
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
thumbnail_size *= EDSCALE;
Vector2 thumbnail_size2 = Vector2(thumbnail_size, thumbnail_size);
if (item.pattern.is_valid() && !item.pattern->is_empty()) {
// Generate the pattern preview
SubViewport *viewport = memnew(SubViewport);
viewport->set_size(thumbnail_size2);
viewport->set_disable_input(true);
viewport->set_transparent_background(true);
viewport->set_update_mode(SubViewport::UPDATE_ONCE);
TileMap *tile_map = memnew(TileMap);
tile_map->set_tileset(item.tile_set);
tile_map->set_pattern(0, Vector2(), item.pattern);
viewport->add_child(tile_map);
TypedArray<Vector2i> used_cells = tile_map->get_used_cells(0);
Rect2 encompassing_rect;
encompassing_rect.set_position(tile_map->map_to_local(used_cells[0]));
for (int i = 0; i < used_cells.size(); i++) {
Vector2i cell = used_cells[i];
Vector2 world_pos = tile_map->map_to_local(cell);
encompassing_rect.expand_to(world_pos);
// Texture.
Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map->get_cell_source_id(0, cell));
if (atlas_source.is_valid()) {
Vector2i coords = tile_map->get_cell_atlas_coords(0, cell);
int alternative = tile_map->get_cell_alternative_tile(0, cell);
if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) {
Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin();
encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
}
}
}
Vector2 scale = thumbnail_size2 / MAX(encompassing_rect.size.x, encompassing_rect.size.y);
tile_map->set_scale(scale);
tile_map->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
// Add the viewport at the last moment to avoid rendering too early.
callable_mp((Node *)EditorNode::get_singleton(), &Node::add_child).call_deferred(viewport, false, Node::INTERNAL_MODE_DISABLED);
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_preview_frame_started), Object::CONNECT_ONE_SHOT);
pattern_preview_done.wait();
Ref<Image> image = viewport->get_texture()->get_image();
// Find the index for the given pattern. TODO: optimize.
item.callback.call(item.pattern, ImageTexture::create_from_image(image));
viewport->queue_free();
}
}
}
pattern_thread_exited.set();
}
void TilesEditorUtils::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_pattern.is_valid());
{
MutexLock lock(pattern_preview_mutex);
pattern_preview_queue.push_back({ p_tile_set, p_pattern, p_callback });
}
pattern_preview_sem.post();
}
void TilesEditorUtils::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
void TilesEditorUtils::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
ERR_FAIL_NULL(item_list);
ERR_FAIL_NULL(sorting_button);
if (sorting_button->is_visible_in_tree()) {
for (int i = 0; i != SOURCE_SORT_MAX; i++) {
sorting_button->get_popup()->set_item_checked(i, (i == (int)source_sort));
}
}
if (item_list->is_visible_in_tree()) {
// Make sure the selection is not overwritten after sorting.
int atlas_sources_lists_current_mem = atlas_sources_lists_current;
item_list->emit_signal(SNAME("sort_request"));
atlas_sources_lists_current = atlas_sources_lists_current_mem;
if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
item_list->deselect_all();
} else {
item_list->set_current(atlas_sources_lists_current);
item_list->ensure_current_is_visible();
item_list->emit_signal(SNAME("item_selected"), atlas_sources_lists_current);
}
}
}
void TilesEditorUtils::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
void TilesEditorUtils::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_NULL(tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
}
}
void TilesEditorUtils::set_sorting_option(int p_option) {
source_sort = p_option;
}
List<int> TilesEditorUtils::get_sorted_sources(const Ref<TileSet> p_tile_set) const {
SourceNameComparator::tile_set = p_tile_set;
List<int> source_ids;
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
source_ids.push_back(p_tile_set->get_source_id(i));
}
switch (source_sort) {
case SOURCE_SORT_ID_REVERSE:
// Already sorted.
source_ids.reverse();
break;
case SOURCE_SORT_NAME:
source_ids.sort_custom<SourceNameComparator>();
break;
case SOURCE_SORT_NAME_REVERSE:
source_ids.sort_custom<SourceNameComparator>();
source_ids.reverse();
break;
default: // SOURCE_SORT_ID
break;
}
SourceNameComparator::tile_set.unref();
return source_ids;
}
Ref<TileSet> TilesEditorUtils::SourceNameComparator::tile_set;
bool TilesEditorUtils::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
String name_a;
String name_b;
{
TileSetSource *source = *tile_set->get_source(p_a);
if (!source->get_name().is_empty()) {
name_a = source->get_name();
}
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (name_a.is_empty() && texture.is_valid()) {
name_a = texture->get_path().get_file();
}
}
if (name_a.is_empty()) {
name_a = itos(p_a);
}
}
{
TileSetSource *source = *tile_set->get_source(p_b);
if (!source->get_name().is_empty()) {
name_b = source->get_name();
}
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (name_b.is_empty() && texture.is_valid()) {
name_b = texture->get_path().get_file();
}
}
if (name_b.is_empty()) {
name_b = itos(p_b);
}
}
return NaturalNoCaseComparator()(name_a, name_b);
}
void TilesEditorUtils::display_tile_set_editor_panel() {
tile_map_plugin_singleton->hide_editor();
tile_set_plugin_singleton->make_visible(true);
}
void TilesEditorUtils::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
Ref<Texture2D> selection_texture = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("TileSelection"), EditorStringName(EditorIcons));
real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
RS::get_singleton()->canvas_item_add_nine_patch(
p_ci->get_canvas_item(), Rect2(Vector2(), p_rect.size * scale), Rect2(), selection_texture->get_rid(),
Vector2(2, 2), Vector2(2, 2), RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, false, p_color);
p_ci->draw_set_transform_matrix(Transform2D());
}
TilesEditorUtils::TilesEditorUtils() {
singleton = this;
// Pattern preview generation thread.
pattern_preview_thread.start(_thread_func, this);
}
TilesEditorUtils::~TilesEditorUtils() {
if (pattern_preview_thread.is_started()) {
pattern_thread_exit.set();
pattern_preview_sem.post();
while (!pattern_thread_exited.is_set()) {
OS::get_singleton()->delay_usec(10000);
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
}
pattern_preview_thread.wait_to_finish();
}
singleton = nullptr;
}
void TileMapEditorPlugin::_tile_map_layer_changed() {
if (tile_map_changed_needs_update) {
return;
}
tile_map_changed_needs_update = true;
callable_mp(this, &TileMapEditorPlugin::_update_tile_map).call_deferred();
}
void TileMapEditorPlugin::_tile_map_layer_removed() {
// Workaround for TileMap, making sure the editor stays open when you delete the currently edited layer.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_group_id));
if (tile_map) {
edit(tile_map);
}
}
void TileMapEditorPlugin::_update_tile_map() {
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
if (edited_layer) {
Ref<TileSet> tile_set = edited_layer->get_effective_tile_set();
if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_id = tile_set->get_instance_id();
} else if (tile_set.is_null()) {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
tile_set_id = ObjectID();
}
}
tile_map_changed_needs_update = false;
}
void TileMapEditorPlugin::_select_layer(const StringName &p_name) {
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
ERR_FAIL_NULL(edited_layer);
Node *parent = edited_layer->get_parent();
ERR_FAIL_NULL(parent);
TileMapLayer *new_layer = Object::cast_to<TileMapLayer>(parent->get_node_or_null(String(p_name)));
edit(new_layer);
}
void TileMapEditorPlugin::_edit_tile_map_layer(TileMapLayer *p_tile_map_layer) {
ERR_FAIL_NULL(p_tile_map_layer);
editor->edit(p_tile_map_layer);
// Update the selected layers in the TileMapLayerGroup parent node.
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(p_tile_map_layer->get_parent());
if (tile_map_layer_group) {
Vector<StringName> selected;
selected.push_back(p_tile_map_layer->get_name());
tile_map_layer_group->set_selected_layers(selected);
}
// Update the object IDs.
tile_map_layer_id = p_tile_map_layer->get_instance_id();
p_tile_map_layer->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
p_tile_map_layer->connect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
if (tile_map_layer_group) {
tile_map_group_id = tile_map_layer_group->get_instance_id();
tile_map_layer_group->connect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_layer_group->connect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_layer_group->connect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
}
// Update the edited tileset.
Ref<TileSet> tile_set = p_tile_map_layer->get_effective_tile_set();
if (tile_set.is_valid()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_id = tile_set->get_instance_id();
} else {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
}
}
void TileMapEditorPlugin::_edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group) {
ERR_FAIL_NULL(p_tile_map_layer_group);
Vector<StringName> selected_layers = p_tile_map_layer_group->get_selected_layers();
TileMapLayer *selected_layer = nullptr;
if (selected_layers.size() > 0) {
// Edit the selected layer.
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_node_or_null(String(selected_layers[0])));
}
if (!selected_layer) {
// Edit the first layer found.
for (int i = 0; i < p_tile_map_layer_group->get_child_count(); i++) {
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_child(i));
if (selected_layer) {
break;
}
}
}
if (selected_layer) {
_edit_tile_map_layer(selected_layer);
} else {
editor->edit(nullptr);
}
}
void TileMapEditorPlugin::_notification(int p_notification) {
if (p_notification == NOTIFICATION_EXIT_TREE) {
get_tree()->queue_delete(TilesEditorUtils::get_singleton());
}
}
void TileMapEditorPlugin::edit(Object *p_object) {
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
if (edited_layer) {
edited_layer->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
edited_layer->disconnect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
}
TileMapLayerGroup *tile_map_group = Object::cast_to<TileMapLayerGroup>(ObjectDB::get_instance(tile_map_group_id));
if (tile_map_group) {
tile_map_group->disconnect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_group->disconnect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
tile_map_group->disconnect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
}
tile_map_group_id = ObjectID();
tile_map_layer_id = ObjectID();
tile_set_id = ObjectID();
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMap>(p_object);
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_object);
if (tile_map_layer_group) {
_edit_tile_map_layer_group(tile_map_layer_group);
} else if (tile_map_layer) {
_edit_tile_map_layer(tile_map_layer);
} else {
// Deselect the layer in the group.
if (edited_layer) {
tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(edited_layer->get_parent());
if (tile_map_layer_group) {
tile_map_layer_group->set_selected_layers(Vector<StringName>());
}
}
}
}
bool TileMapEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<TileMapLayer>(p_object) != nullptr || Object::cast_to<TileMapLayerGroup>(p_object) != nullptr;
}
void TileMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
EditorNode::get_bottom_panel()->make_item_visible(editor);
} else {
button->hide();
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
bool TileMapEditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
return editor->forward_canvas_gui_input(p_event);
}
void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
editor->forward_canvas_draw_over_viewport(p_overlay);
}
void TileMapEditorPlugin::hide_editor() {
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
bool TileMapEditorPlugin::is_editor_visible() const {
return editor->is_visible_in_tree();
}
TileMapEditorPlugin::TileMapEditorPlugin() {
if (!TilesEditorUtils::get_singleton()) {
memnew(TilesEditorUtils);
}
tile_map_plugin_singleton = this;
editor = memnew(TileMapLayerEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->connect("change_selected_layer_request", callable_mp(this, &TileMapEditorPlugin::_select_layer));
editor->hide();
button = EditorNode::get_bottom_panel()->add_item(TTR("TileMap"), editor);
button->hide();
}
TileMapEditorPlugin::~TileMapEditorPlugin() {
tile_map_plugin_singleton = nullptr;
}
void TileSetEditorPlugin::edit(Object *p_object) {
editor->edit(Ref<TileSet>(p_object));
if (p_object) {
edited_tileset = p_object->get_instance_id();
} else {
edited_tileset = ObjectID();
}
}
bool TileSetEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<TileSet>(p_object) != nullptr;
}
void TileSetEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
if (!tile_map_plugin_singleton->is_editor_visible()) {
EditorNode::get_bottom_panel()->make_item_visible(editor);
}
} else {
button->hide();
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
ObjectID TileSetEditorPlugin::get_edited_tileset() const {
return edited_tileset;
}
TileSetEditorPlugin::TileSetEditorPlugin() {
if (!TilesEditorUtils::get_singleton()) {
memnew(TilesEditorUtils);
}
tile_set_plugin_singleton = this;
editor = memnew(TileSetEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->hide();
button = EditorNode::get_bottom_panel()->add_item(TTR("TileSet"), editor);
button->hide();
}
TileSetEditorPlugin::~TileSetEditorPlugin() {
tile_set_plugin_singleton = nullptr;
}

View File

@ -0,0 +1,169 @@
/**************************************************************************/
/* tiles_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILES_EDITOR_PLUGIN_H
#define TILES_EDITOR_PLUGIN_H
#include "editor/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "tile_atlas_view.h"
#include "tile_map_layer_editor.h"
#include "tile_set_editor.h"
class TilesEditorUtils : public Object {
GDCLASS(TilesEditorUtils, Object);
static TilesEditorUtils *singleton;
public:
enum SourceSortOption {
SOURCE_SORT_ID = 0,
SOURCE_SORT_ID_REVERSE,
SOURCE_SORT_NAME,
SOURCE_SORT_NAME_REVERSE,
SOURCE_SORT_MAX
};
private:
// For synchronization.
int atlas_sources_lists_current = 0;
float atlas_view_zoom = 1.0;
Vector2 atlas_view_scroll;
// Source sorting.
int source_sort = SOURCE_SORT_ID;
struct SourceNameComparator {
static Ref<TileSet> tile_set;
bool operator()(const int &p_a, const int &p_b) const;
};
// Patterns preview generation.
struct QueueItem {
Ref<TileSet> tile_set;
Ref<TileMapPattern> pattern;
Callable callback;
};
List<QueueItem> pattern_preview_queue;
Mutex pattern_preview_mutex;
Semaphore pattern_preview_sem;
Thread pattern_preview_thread;
SafeFlag pattern_thread_exit;
SafeFlag pattern_thread_exited;
Semaphore pattern_preview_done;
void _preview_frame_started();
void _pattern_preview_done();
static void _thread_func(void *ud);
void _thread();
public:
_FORCE_INLINE_ static TilesEditorUtils *get_singleton() { return singleton; }
// Pattern preview API.
void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
// To synchronize the atlas sources lists.
void set_sources_lists_current(int p_current);
void synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button);
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
// Sorting.
void set_sorting_option(int p_option);
List<int> get_sorted_sources(const Ref<TileSet> p_tile_set) const;
// Misc.
void display_tile_set_editor_panel();
static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
TilesEditorUtils();
~TilesEditorUtils();
};
class TileMapEditorPlugin : public EditorPlugin {
GDCLASS(TileMapEditorPlugin, EditorPlugin);
TileMapLayerEditor *editor = nullptr;
Button *button = nullptr;
ObjectID tile_map_layer_id;
ObjectID tile_map_group_id; // Allow keeping the layer selector up to date.
bool tile_map_changed_needs_update = false;
ObjectID tile_set_id; // The TileSet associated with the TileMap.
void _tile_map_layer_changed();
void _tile_map_layer_removed();
void _update_tile_map();
void _select_layer(const StringName &p_name);
void _edit_tile_map_layer(TileMapLayer *p_tile_map_layer);
void _edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group);
protected:
void _notification(int p_notification);
public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
void hide_editor();
bool is_editor_visible() const;
TileMapEditorPlugin();
~TileMapEditorPlugin();
};
class TileSetEditorPlugin : public EditorPlugin {
GDCLASS(TileSetEditorPlugin, EditorPlugin);
TileSetEditor *editor = nullptr;
Button *button = nullptr;
ObjectID edited_tileset;
public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
ObjectID get_edited_tileset() const;
TileSetEditorPlugin();
~TileSetEditorPlugin();
};
#endif // TILES_EDITOR_PLUGIN_H

View File

@ -0,0 +1,176 @@
/*************************************************************************/
/* tilemap_navigation_geometry_parser_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tilemap_navigation_geometry_parser_2d.h"
//#include "scene/2d/tile_map.h"
#include "modules/tile_map/tile_map.h"
#include "modules/tile_map/tile_set.h"
#include "scene/resources/navigation_2d/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_2d/navigation_polygon.h"
#include "modules/modules_enabled.gen.h"
bool TileMap2DNavigationGeometryParser2D::parses_node(Node *p_node) {
return (Object::cast_to<TileMap>(p_node) != nullptr);
}
void TileMap2DNavigationGeometryParser2D::parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry) {
TileMap *tilemap = Object::cast_to<TileMap>(p_node);
//NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_polygon->get_parsed_geometry_type();
//uint32_t navigation_polygon_collision_mask = p_navigation_polygon->get_collision_mask();
if (tilemap) {
Ref<TileSet> tile_set = tilemap->get_tileset();
if (!tile_set.is_valid()) {
return;
}
const Transform2D tilemap_xform = tilemap->get_transform();
Array used_cells = tilemap->get_used_cells();
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
Vector2 cell = used_cells[used_cell_index];
int cell_id = tilemap->get_cell(cell.x, cell.y);
Transform2D tile_transform;
tile_transform.set_origin(tilemap->to_local(cell));
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
Ref<NavigationPolygon> navigation_polygon = tile_set->tile_get_navigation_polygon(cell_id);
if (navigation_polygon.is_valid()) {
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
PoolVector<Vector2> traversable_outline = navigation_polygon->get_outline(outline_index);
Vector<Vector2> traversable_outline_new;
traversable_outline_new.resize(traversable_outline.size());
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
traversable_outline_new.write[traversable_outline_index] = tile_transform_offset.xform(traversable_outline[traversable_outline_index]);
}
p_source_geometry->_add_traversable_outline(traversable_outline_new);
}
}
/*
TODO
if (parsed_geometry_type != NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES && (tilemap->get_collision_layer() & navigation_polygon_collision_mask)) {
for (int collision_polygon_index = 0; collision_polygon_index < tile_set->tile_get_shape_count(cell_id); collision_polygon_index++) {
Vector<Vector2> obstruction_outline = tile_set->get_collision_polygon_points(collision_polygon_index);
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
obstruction_outline.write[obstruction_outline_index] = tile_transform_offset.xform(obstruction_outline[obstruction_outline_index]);
}
p_source_geometry->_add_obstruction_outline(obstruction_outline);
}
}
*/
}
}
}
bool TileMap2DNavigationGeometryParser2D::parses_node(Node *p_node) {
return (Object::cast_to<TileMap>(p_node) != nullptr);
}
void TileMap2DNavigationGeometryParser2D::parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry) {
#ifdef CLIPPER_ENABLED
TileMap *tilemap = Object::cast_to<TileMap>(p_node);
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_polygon->get_parsed_geometry_type();
uint32_t navigation_polygon_collision_mask = p_navigation_polygon->get_collision_mask();
if (tilemap) {
if (tilemap->get_layers_count() <= 0) {
return;
}
int tilemap_layer = 0; // only main tile map layer is supported
Ref<TileSet> tile_set = tilemap->get_tileset();
if (!tile_set.is_valid()) {
return;
}
int physics_layers_count = tile_set->get_physics_layers_count();
int navigation_layers_count = tile_set->get_navigation_layers_count();
if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
return;
}
const Transform2D tilemap_xform = tilemap->get_transform();
TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer);
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
const Vector2i &cell = used_cells[used_cell_index];
const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false);
Transform2D tile_transform;
tile_transform.set_origin(tilemap->map_to_local(cell));
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
if (navigation_layers_count > 0) {
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer);
if (navigation_polygon.is_valid()) {
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
Vector<Vector2> traversable_outline = navigation_polygon->get_outline(outline_index);
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
traversable_outline.write[traversable_outline_index] = tile_transform_offset.xform(traversable_outline[traversable_outline_index]);
}
p_source_geometry->_add_traversable_outline(traversable_outline);
}
}
}
if (physics_layers_count > 0 && parsed_geometry_type != NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & navigation_polygon_collision_mask)) {
for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) {
Vector<Vector2> obstruction_outline = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index);
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
obstruction_outline.write[obstruction_outline_index] = tile_transform_offset.xform(obstruction_outline[obstruction_outline_index]);
}
p_source_geometry->_add_obstruction_outline(obstruction_outline);
}
}
}
}
#endif // CLIPPER_ENABLED
}

View File

@ -0,0 +1,44 @@
#ifndef TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H
#define TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H
/*************************************************************************/
/* tilemap_navigation_geometry_parser_2d.h */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scene/2d/navigation_geometry_parser_2d.h"
class TileMap2DNavigationGeometryParser2D : public NavigationGeometryParser2D {
public:
virtual bool parses_node(Node *p_node);
virtual void parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry);
};
#endif // TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H

View File

@ -0,0 +1,84 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Copyright (c) 2021 Péter Magyar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "register_types.h"
#include "geometry_parser/tilemap_navigation_geometry_parser_2d.h"
#include "servers/navigation/navigation_mesh_generator.h"
#include "tile_map.h"
#include "tile_set.h"
#ifdef TOOLS_ENABLED
#include "tile_map_editor_plugin.h"
#include "tile_set_editor_plugin.h"
#endif
void register_tile_map_types(ModuleRegistrationLevel p_level) {
if (p_level == MODULE_REGISTRATION_LEVEL_SCENE) {
ClassDB::register_class<TileMap>();
ClassDB::register_class<TileSet>();
NavigationMeshGenerator::get_singleton()->register_geometry_parser_2d(memnew(TileMap2DNavigationGeometryParser2D));
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_REGISTRATION_LEVEL_EDITOR) {
EditorPlugins::add_by_type<TileMapEditorPlugin>();
EditorPlugins::add_by_type<TileSetEditorPlugin>();
}
#endif
}
void unregister_tile_map_types(ModuleRegistrationLevel p_level) {
}

View File

@ -0,0 +1,62 @@
#ifndef TILE_MAP_REGISTER_TYPES_H
#define TILE_MAP_REGISTER_TYPES_H
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Copyright (c) 2021 Péter Magyar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "modules/register_module_types.h"
void register_tile_map_types(ModuleRegistrationLevel p_level);
void unregister_tile_map_types(ModuleRegistrationLevel p_level);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
/**************************************************************************/
/* tile_map.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_H
#define TILE_MAP_H
#include "scene/2d/tile_map_layer_group.h"
#include "scene/resources/2d/tile_set.h"
class Control;
class TileMapLayer;
class TerrainConstraint;
enum TileMapDataFormat {
FORMAT_1 = 0,
FORMAT_2,
FORMAT_3,
FORMAT_MAX,
};
class TileMap : public TileMapLayerGroup {
GDCLASS(TileMap, TileMapLayerGroup)
public:
// Kept for compatibility, but should use TileMapLayer::VisibilityMode instead.
enum VisibilityMode {
VISIBILITY_MODE_DEFAULT,
VISIBILITY_MODE_FORCE_SHOW,
VISIBILITY_MODE_FORCE_HIDE,
};
private:
friend class TileSetPlugin;
// A compatibility enum to specify how is the data if formatted.
mutable TileMapDataFormat format = TileMapDataFormat::FORMAT_3;
static constexpr float FP_ADJUST = 0.00001;
// Properties.
int rendering_quadrant_size = 16;
bool collision_animatable = false;
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
// Layers.
LocalVector<TileMapLayer *> layers;
TileMapLayer *default_layer; // Dummy layer to fetch default values.
// Transforms for collision_animatable.
Transform2D last_valid_transform;
Transform2D new_transform;
void _emit_changed();
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;
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
void _notification(int p_what);
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
Rect2i _get_used_rect_bind_compat_78328();
void _set_quadrant_size_compat_81070(int p_quadrant_size);
int _get_quadrant_size_compat_81070() const;
VisibilityMode _get_collision_visibility_mode_bind_compat_87115();
VisibilityMode _get_navigation_visibility_mode_bind_compat_87115();
static void _bind_compatibility_methods();
#endif
public:
#ifdef TOOLS_ENABLED
virtual Rect2 _edit_get_rect() const override;
#endif
#ifndef DISABLE_DEPRECATED
void force_update(int p_layer);
#endif
void set_rendering_quadrant_size(int p_size);
int get_rendering_quadrant_size() const;
static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0);
// Layers management.
int get_layers_count() const;
void add_layer(int p_to_pos);
void move_layer(int p_layer, int p_to_pos);
void remove_layer(int p_layer);
void set_layer_name(int p_layer, String p_name);
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);
bool is_layer_enabled(int p_layer) const;
void set_layer_modulate(int p_layer, Color p_modulate);
Color get_layer_modulate(int p_layer) const;
void set_layer_y_sort_enabled(int p_layer, bool p_enabled);
bool is_layer_y_sort_enabled(int p_layer) const;
void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin);
int get_layer_y_sort_origin(int p_layer) const;
void set_layer_z_index(int p_layer, int p_z_index);
int get_layer_z_index(int p_layer) const;
void set_layer_navigation_enabled(int p_layer, bool p_enabled);
bool is_layer_navigation_enabled(int p_layer) const;
void set_layer_navigation_map(int p_layer, RID p_map);
RID get_layer_navigation_map(int p_layer) const;
void set_collision_animatable(bool p_collision_animatable);
bool is_collision_animatable() const;
// Debug visibility modes.
void set_collision_visibility_mode(VisibilityMode p_show_collision);
VisibilityMode get_collision_visibility_mode() const;
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
VisibilityMode get_navigation_visibility_mode() const;
// Cells accessors.
void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
void erase_cell(int p_layer, const Vector2i &p_coords);
int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
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;
// Helper method to make accessing the data easier.
TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
// Patterns.
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
void set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
// Terrains (Not exposed).
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints);
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true);
// Terrains (exposed).
void set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
void set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
// Not exposed to users.
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
int get_effective_quadrant_size(int p_layer) const;
virtual void set_y_sort_enabled(bool p_enable) override;
Vector2 map_to_local(const Vector2i &p_pos) const;
Vector2i local_to_map(const Vector2 &p_pos) const;
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
TypedArray<Vector2i> get_used_cells(int p_layer) const;
TypedArray<Vector2i> get_used_cells_by_id(int p_layer, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
Rect2i get_used_rect() const;
// Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems.
virtual void set_light_mask(int p_light_mask) override;
virtual void set_self_modulate(const Color &p_self_modulate) override;
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
// For finding tiles from collision.
Vector2i get_coords_for_body_rid(RID p_physics_body);
// For getting their layers as well.
int get_layer_for_body_rid(RID p_physics_body);
// Fixing and clearing methods.
void fix_invalid_tiles();
// Clears tiles from a given layer.
void clear_layer(int p_layer);
void clear();
// Force a TileMap update.
void update_internals();
void notify_runtime_tile_data_update(int p_layer = -1);
// Helpers?
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords);
// Virtual function to modify the TileData at runtime.
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *);
// Configuration warnings.
PackedStringArray get_configuration_warnings() const override;
TileMap();
~TileMap();
};
VARIANT_ENUM_CAST(TileMap::VisibilityMode);
#endif // TILE_MAP_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,456 @@
/**************************************************************************/
/* tile_map_layer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_LAYER_H
#define TILE_MAP_LAYER_H
#include "scene/2d/tile_map.h"
#include "scene/resources/2d/tile_set.h"
class TileSetAtlasSource;
class TerrainConstraint {
private:
Ref<TileSet> tile_set;
Vector2i base_cell_coords;
int bit = -1;
int terrain = -1;
int priority = 1;
public:
bool operator<(const TerrainConstraint &p_other) const {
if (base_cell_coords == p_other.base_cell_coords) {
return bit < p_other.bit;
}
return base_cell_coords < p_other.base_cell_coords;
}
String to_string() const {
return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority);
}
Vector2i get_base_cell_coords() const {
return base_cell_coords;
}
bool is_center_bit() const {
return bit == 0;
}
HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
void set_terrain(int p_terrain) {
terrain = p_terrain;
}
int get_terrain() const {
return terrain;
}
void set_priority(int p_priority) {
priority = p_priority;
}
int get_priority() const {
return priority;
}
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain); // For the center terrain bit
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
TerrainConstraint(){};
};
#ifdef DEBUG_ENABLED
class DebugQuadrant;
#endif // DEBUG_ENABLED
class RenderingQuadrant;
struct CellData {
Vector2i coords;
TileMapCell cell;
// Debug.
SelfList<CellData> debug_quadrant_list_element;
// Rendering.
Ref<RenderingQuadrant> rendering_quadrant;
SelfList<CellData> rendering_quadrant_list_element;
LocalVector<RID> occluders;
// Physics.
LocalVector<RID> bodies;
// Navigation.
LocalVector<RID> navigation_regions;
// Scenes.
String scene;
// Runtime TileData cache.
TileData *runtime_tile_data_cache = nullptr;
// List elements.
SelfList<CellData> dirty_list_element;
bool operator<(const CellData &p_other) const {
return coords < p_other.coords;
}
// For those, copy everything but SelfList elements.
void operator=(const CellData &p_other) {
coords = p_other.coords;
cell = p_other.cell;
occluders = p_other.occluders;
bodies = p_other.bodies;
navigation_regions = p_other.navigation_regions;
scene = p_other.scene;
runtime_tile_data_cache = p_other.runtime_tile_data_cache;
}
CellData(const CellData &p_other) :
debug_quadrant_list_element(this),
rendering_quadrant_list_element(this),
dirty_list_element(this) {
coords = p_other.coords;
cell = p_other.cell;
occluders = p_other.occluders;
bodies = p_other.bodies;
navigation_regions = p_other.navigation_regions;
scene = p_other.scene;
runtime_tile_data_cache = p_other.runtime_tile_data_cache;
}
CellData() :
debug_quadrant_list_element(this),
rendering_quadrant_list_element(this),
dirty_list_element(this) {
}
};
// For compatibility reasons, we use another comparator for Y-sorted layers.
struct CellDataYSortedComparator {
_FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const {
return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x);
}
};
#ifdef DEBUG_ENABLED
class DebugQuadrant : public RefCounted {
GDCLASS(DebugQuadrant, RefCounted);
public:
Vector2i quadrant_coords;
SelfList<CellData>::List cells;
RID canvas_item;
SelfList<DebugQuadrant> dirty_quadrant_list_element;
DebugQuadrant() :
dirty_quadrant_list_element(this) {
}
~DebugQuadrant() {
cells.clear();
}
};
#endif // DEBUG_ENABLED
class RenderingQuadrant : public RefCounted {
GDCLASS(RenderingQuadrant, RefCounted);
public:
struct CoordsWorldComparator {
_ALWAYS_INLINE_ bool operator()(const Vector2 &p_a, const Vector2 &p_b) const {
// We sort the cells by their local coords, as it is needed by rendering.
if (p_a.y == p_b.y) {
return p_a.x > p_b.x;
} else {
return p_a.y < p_b.y;
}
}
};
Vector2i quadrant_coords;
SelfList<CellData>::List cells;
List<RID> canvas_items;
Vector2 canvas_items_position;
SelfList<RenderingQuadrant> dirty_quadrant_list_element;
RenderingQuadrant() :
dirty_quadrant_list_element(this) {
}
~RenderingQuadrant() {
cells.clear();
}
};
class TileMapLayer : public Node2D {
GDCLASS(TileMapLayer, Node2D);
public:
enum VisibilityMode {
VISIBILITY_MODE_DEFAULT,
VISIBILITY_MODE_FORCE_SHOW,
VISIBILITY_MODE_FORCE_HIDE,
};
enum DirtyFlags {
DIRTY_FLAGS_LAYER_ENABLED = 0,
DIRTY_FLAGS_LAYER_IN_TREE,
DIRTY_FLAGS_LAYER_IN_CANVAS,
DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM,
DIRTY_FLAGS_LAYER_VISIBILITY,
DIRTY_FLAGS_LAYER_SELF_MODULATE,
DIRTY_FLAGS_LAYER_Y_SORT_ENABLED,
DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN,
DIRTY_FLAGS_LAYER_Z_INDEX,
DIRTY_FLAGS_LAYER_LIGHT_MASK,
DIRTY_FLAGS_LAYER_TEXTURE_FILTER,
DIRTY_FLAGS_LAYER_TEXTURE_REPEAT,
DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE,
DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES,
DIRTY_FLAGS_LAYER_COLLISION_VISIBILITY_MODE,
DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED,
DIRTY_FLAGS_LAYER_NAVIGATION_MAP,
DIRTY_FLAGS_LAYER_NAVIGATION_VISIBILITY_MODE,
DIRTY_FLAGS_LAYER_RUNTIME_UPDATE,
DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE, // For compatibility.
DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS,
DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED,
DIRTY_FLAGS_LAYER_GROUP_TILE_SET,
DIRTY_FLAGS_MAX,
};
private:
// Exposed properties.
bool enabled = true;
int y_sort_origin = 0;
int rendering_quadrant_size = 16;
bool use_kinematic_bodies = false;
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
bool navigation_enabled = true;
RID navigation_map_override;
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
// Internal.
HashMap<Vector2i, CellData> tile_map;
bool pending_update = false;
// For keeping compatibility with TileMap.
TileMap *tile_map_node = nullptr;
int layer_index_in_tile_map_node = -1;
// Dirty flag. Allows knowing what was modified since the last update.
struct {
bool flags[DIRTY_FLAGS_MAX] = { false };
SelfList<CellData>::List cell_list;
} dirty;
bool in_destructor = false;
// Rect cache.
mutable Rect2 rect_cache;
mutable bool rect_cache_dirty = true;
mutable Rect2i used_rect_cache;
mutable bool used_rect_cache_dirty = true;
// Runtime tile data.
bool _runtime_update_tile_data_was_cleaned_up = false;
void _build_runtime_update_tile_data();
void _build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_use_tilemap_for_runtime, bool p_auto_add_to_dirty_list = false);
bool _runtime_update_needs_all_cells_cleaned_up = false;
void _clear_runtime_update_tile_data();
void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data);
// Per-system methods.
#ifdef DEBUG_ENABLED
HashMap<Vector2i, Ref<DebugQuadrant>> debug_quadrant_map;
Vector2i _coords_to_debug_quadrant_coords(const Vector2i &p_coords) const;
bool _debug_was_cleaned_up = false;
void _debug_update();
void _debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list);
#endif // DEBUG_ENABLED
HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map;
bool _rendering_was_cleaned_up = false;
void _rendering_update();
void _rendering_notification(int p_what);
void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list);
void _rendering_occluders_clear_cell(CellData &r_cell_data);
void _rendering_occluders_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
void _rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
#endif // DEBUG_ENABLED
HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords.
bool _physics_was_cleaned_up = false;
void _physics_update();
void _physics_notification(int p_what);
void _physics_clear_cell(CellData &r_cell_data);
void _physics_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
void _physics_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
#endif // DEBUG_ENABLED
bool _navigation_was_cleaned_up = false;
void _navigation_update();
void _navigation_notification(int p_what);
void _navigation_clear_cell(CellData &r_cell_data);
void _navigation_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
void _navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
#endif // DEBUG_ENABLED
bool _scenes_was_cleaned_up = false;
void _scenes_update();
void _scenes_clear_cell(CellData &r_cell_data);
void _scenes_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
void _scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
#endif // DEBUG_ENABLED
// Terrains.
TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const;
RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
void _renamed();
void _update_notify_local_transform();
// Internal updates.
void _queue_internal_update();
void _deferred_internal_update();
void _internal_update();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
// TileMap node.
void set_as_tile_map_internal_node(int p_index);
// Rect caching.
Rect2 get_rect(bool &r_changed) const;
// Terrains.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true) const; // Not exposed.
// Not exposed to users.
TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
// For TileMap node's use.
void set_tile_data(TileMapDataFormat p_format, const Vector<int> &p_data);
Vector<int> get_tile_data() const;
void notify_tile_map_layer_group_change(DirtyFlags p_what);
void update_internals();
void notify_runtime_tile_data_update();
// --- Exposed in TileMap ---
// Cells manipulation.
void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
void erase_cell(const Vector2i &p_coords);
int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const;
Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const;
int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const;
TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier.
void clear();
// Patterns.
Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
// Terrains.
void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
// Cells usage.
TypedArray<Vector2i> get_used_cells() const;
TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
Rect2i get_used_rect() const;
// Layer properties.
void set_enabled(bool p_enabled);
bool is_enabled() const;
virtual void set_self_modulate(const Color &p_self_modulate) override;
virtual void set_y_sort_enabled(bool p_y_sort_enabled) override;
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
virtual void set_z_index(int p_z_index) override;
virtual void set_light_mask(int p_light_mask) override;
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
void set_rendering_quadrant_size(int p_size);
int get_rendering_quadrant_size() const;
void set_use_kinematic_bodies(bool p_use_kinematic_bodies);
bool is_using_kinematic_bodies() const;
void set_collision_visibility_mode(VisibilityMode p_show_collision);
VisibilityMode get_collision_visibility_mode() const;
void set_navigation_enabled(bool p_enabled);
bool is_navigation_enabled() const;
void set_navigation_map(RID p_map);
RID get_navigation_map() const;
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
VisibilityMode get_navigation_visibility_mode() const;
// Fixing and clearing methods.
void fix_invalid_tiles();
// Find coords for body.
bool has_body_rid(RID p_physics_body) const;
Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision.
// Helper.
Ref<TileSet> get_effective_tile_set() const;
// Virtual function to modify the TileData at runtime.
GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i);
GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *);
// ---
TileMapLayer();
~TileMapLayer();
};
#endif // TILE_MAP_LAYER_H

View File

@ -0,0 +1,148 @@
/**************************************************************************/
/* tile_map_layer_group.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_map_layer_group.h"
#include "core/core_string_names.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/resources/2d/tile_set.h"
#ifdef TOOLS_ENABLED
void TileMapLayerGroup::_cleanup_selected_layers() {
for (int i = 0; i < selected_layers.size(); i++) {
const String name = selected_layers[i];
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_node_or_null(name));
if (!layer) {
selected_layers.remove_at(i);
i--;
}
}
}
#endif // TOOLS_ENABLED
void TileMapLayerGroup::_tile_set_changed() {
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
}
}
update_configuration_warnings();
}
#ifdef TOOLS_ENABLED
void TileMapLayerGroup::set_selected_layers(Vector<StringName> p_layer_names) {
selected_layers = p_layer_names;
_cleanup_selected_layers();
// Update the layers modulation.
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS);
}
}
}
Vector<StringName> TileMapLayerGroup::get_selected_layers() const {
return selected_layers;
}
void TileMapLayerGroup::set_highlight_selected_layer(bool p_highlight_selected_layer) {
if (highlight_selected_layer == p_highlight_selected_layer) {
return;
}
highlight_selected_layer = p_highlight_selected_layer;
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED);
}
}
}
bool TileMapLayerGroup::is_highlighting_selected_layer() const {
return highlight_selected_layer;
}
#endif // TOOLS_ENABLED
void TileMapLayerGroup::remove_child_notify(Node *p_child) {
#ifdef TOOLS_ENABLED
_cleanup_selected_layers();
#endif // TOOLS_ENABLED
}
void TileMapLayerGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMapLayerGroup::set_tileset);
ClassDB::bind_method(D_METHOD("get_tileset"), &TileMapLayerGroup::get_tileset);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
}
void TileMapLayerGroup::set_tileset(const Ref<TileSet> &p_tileset) {
if (p_tileset == tile_set) {
return;
}
// Set the tileset, registering to its changes.
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
tile_set = p_tileset;
if (tile_set.is_valid()) {
tile_set->connect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
for (int i = 0; i < get_child_count(); i++) {
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
if (layer) {
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
}
}
}
Ref<TileSet> TileMapLayerGroup::get_tileset() const {
return tile_set;
}
TileMapLayerGroup::~TileMapLayerGroup() {
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
}
}

View File

@ -0,0 +1,73 @@
/**************************************************************************/
/* tile_map_layer_group.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_MAP_LAYER_GROUP_H
#define TILE_MAP_LAYER_GROUP_H
#include "scene/2d/node_2d.h"
class TileSet;
class TileMapLayerGroup : public Node2D {
GDCLASS(TileMapLayerGroup, Node2D);
private:
mutable Vector<StringName> selected_layers;
bool highlight_selected_layer = true;
#ifdef TOOLS_ENABLED
void _cleanup_selected_layers();
#endif
void _tile_set_changed();
protected:
Ref<TileSet> tile_set;
virtual void remove_child_notify(Node *p_child) override;
static void _bind_methods();
public:
#ifdef TOOLS_ENABLED
// For editor use.
void set_selected_layers(Vector<StringName> p_layer_names);
Vector<StringName> get_selected_layers() const;
void set_highlight_selected_layer(bool p_highlight_selected_layer);
bool is_highlighting_selected_layer() const;
#endif
// Accessors.
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
~TileMapLayerGroup();
};
#endif // TILE_MAP_LAYER_GROUP_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,987 @@
/**************************************************************************/
/* tile_set.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TILE_SET_H
#define TILE_SET_H
#include "core/io/resource.h"
#include "core/object/object.h"
#include "core/templates/local_vector.h"
#include "core/templates/rb_set.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/main/canvas_item.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/physics_material.h"
#ifndef DISABLE_DEPRECATED
#include "scene/resources/shader.h"
#endif
class TileMap;
class TileSetSource;
class TileSetAtlasSource;
class TileData;
// Forward-declare the plugins.
class TileSetPlugin;
class TileSetPluginAtlasRendering;
class TileSetPluginAtlasPhysics;
class TileSetPluginAtlasNavigation;
union TileMapCell {
struct {
int16_t source_id;
int16_t coord_x;
int16_t coord_y;
int16_t alternative_tile;
};
uint64_t _u64t;
static uint32_t hash(const TileMapCell &p_hash) {
return hash_one_uint64(p_hash._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);
}
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);
Size2i size;
HashMap<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;
const HashMap<Vector2i, TileMapCell> &get_pattern() const { return pattern; }
TypedArray<Vector2i> get_used_cells() const;
Size2i get_size() const;
void set_size(const Size2i &p_size);
bool is_empty() const;
void clear();
};
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
#ifndef DISABLE_DEPRECATED
private:
struct CompatibilityShapeData {
Vector2i autotile_coords;
bool one_way;
float one_way_margin;
Ref<Shape2D> shape;
Transform2D transform;
};
struct CompatibilityTileData {
String name;
Ref<Texture2D> texture;
Vector2 tex_offset;
Ref<Material> material;
Rect2 region;
int tile_mode = 0;
Color modulate = Color(1, 1, 1);
// Atlas or autotiles data
int autotile_bitmask_mode = 0;
Vector2 autotile_icon_coordinate;
Size2i autotile_tile_size = Size2i(16, 16);
int autotile_spacing = 0;
HashMap<Vector2i, int> autotile_bitmask_flags;
HashMap<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
HashMap<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
HashMap<Vector2i, int> autotile_priority_map;
HashMap<Vector2i, int> autotile_z_index_map;
Vector<CompatibilityShapeData> shapes;
Ref<OccluderPolygon2D> occluder;
Vector2 occluder_offset;
Ref<NavigationPolygon> navigation;
Vector2 navigation_offset;
int z_index = 0;
};
enum CompatibilityTileMode {
COMPATIBILITY_TILE_MODE_SINGLE_TILE = 0,
COMPATIBILITY_TILE_MODE_AUTO_TILE,
COMPATIBILITY_TILE_MODE_ATLAS_TILE,
};
HashMap<int, CompatibilityTileData *> compatibility_data;
HashMap<int, int> compatibility_tilemap_mapping_tile_modes;
HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping;
HashMap<Vector2i, int> compatibility_size_count;
void _compatibility_conversion();
public:
// Format of output array [source_id, atlas_coords, alternative]
Array compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose);
#endif // DISABLE_DEPRECATED
public:
static const int INVALID_SOURCE; // -1;
enum CellNeighbor {
CELL_NEIGHBOR_RIGHT_SIDE = 0,
CELL_NEIGHBOR_RIGHT_CORNER,
CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
CELL_NEIGHBOR_BOTTOM_SIDE,
CELL_NEIGHBOR_BOTTOM_CORNER,
CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
CELL_NEIGHBOR_LEFT_SIDE,
CELL_NEIGHBOR_LEFT_CORNER,
CELL_NEIGHBOR_TOP_LEFT_SIDE,
CELL_NEIGHBOR_TOP_LEFT_CORNER,
CELL_NEIGHBOR_TOP_SIDE,
CELL_NEIGHBOR_TOP_CORNER,
CELL_NEIGHBOR_TOP_RIGHT_SIDE,
CELL_NEIGHBOR_TOP_RIGHT_CORNER,
CELL_NEIGHBOR_MAX,
};
static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[];
enum TerrainMode {
TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0,
TERRAIN_MODE_MATCH_CORNERS,
TERRAIN_MODE_MATCH_SIDES,
};
enum TileShape {
TILE_SHAPE_SQUARE,
TILE_SHAPE_ISOMETRIC,
TILE_SHAPE_HALF_OFFSET_SQUARE,
TILE_SHAPE_HEXAGON,
};
enum TileLayout {
TILE_LAYOUT_STACKED,
TILE_LAYOUT_STACKED_OFFSET,
TILE_LAYOUT_STAIRS_RIGHT,
TILE_LAYOUT_STAIRS_DOWN,
TILE_LAYOUT_DIAMOND_RIGHT,
TILE_LAYOUT_DIAMOND_DOWN,
};
enum TileOffsetAxis {
TILE_OFFSET_AXIS_HORIZONTAL,
TILE_OFFSET_AXIS_VERTICAL,
};
struct PackedSceneSource {
Ref<PackedScene> scene;
Vector2 offset;
};
class TerrainsPattern {
bool valid = false;
int terrain = -1;
int bits[TileSet::CELL_NEIGHBOR_MAX];
bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
int not_empty_terrains_count = 0;
public:
bool is_valid() const;
bool is_erase_pattern() const;
bool operator<(const TerrainsPattern &p_terrains_pattern) const;
bool operator==(const TerrainsPattern &p_terrains_pattern) const;
bool operator!=(const TerrainsPattern &p_terrains_pattern) const {
return !operator==(p_terrains_pattern);
};
void set_terrain(int p_terrain);
int get_terrain() const;
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain);
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
void from_array(Array p_terrains);
Array as_array() const;
TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
TerrainsPattern() {}
};
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;
void _validate_property(PropertyInfo &p_property) const;
private:
// --- TileSet data ---
// Basic shape and layout.
TileShape tile_shape = TILE_SHAPE_SQUARE;
TileLayout tile_layout = TILE_LAYOUT_STACKED;
TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL;
Size2i tile_size = Size2i(16, 16); //Size2(64, 64);
// Rendering.
bool uv_clipping = false;
struct OcclusionLayer {
uint32_t light_mask = 1;
bool sdf_collision = false;
};
Vector<OcclusionLayer> occlusion_layers;
Ref<ArrayMesh> tile_lines_mesh;
Ref<ArrayMesh> tile_filled_mesh;
mutable bool tile_meshes_dirty = true;
// Physics
struct PhysicsLayer {
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
Ref<PhysicsMaterial> physics_material;
};
Vector<PhysicsLayer> physics_layers;
// Terrains
struct Terrain {
String name;
Color color;
};
struct TerrainSet {
TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES;
Vector<Terrain> terrains;
};
Vector<TerrainSet> terrain_sets;
HashMap<TerrainMode, Ref<ArrayMesh>> terrain_meshes;
HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_peering_bits_meshes;
bool terrain_bits_meshes_dirty = true;
LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
bool terrains_cache_dirty = true;
void _update_terrains_cache();
// Navigation
struct NavigationLayer {
uint32_t layers = 1;
};
Vector<NavigationLayer> navigation_layers;
// CustomData
struct CustomDataLayer {
String name;
Variant::Type type = Variant::NIL;
};
Vector<CustomDataLayer> custom_data_layers;
HashMap<String, int> custom_data_layers_by_name;
// Per Atlas source data.
HashMap<int, Ref<TileSetSource>> sources;
Vector<int> source_ids;
int next_source_id = 0;
// ---------------------
LocalVector<Ref<TileMapPattern>> patterns;
void _compute_next_source_id();
void _source_changed();
// Tile proxies
RBMap<int, int> source_level_proxies;
RBMap<Array, Array> coords_level_proxies;
RBMap<Array, Array> alternative_level_proxies;
// Helpers
Vector<Point2> _get_square_terrain_polygon(Vector2i p_size);
Vector<Point2> _get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_square_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_isometric_terrain_polygon(Vector2i p_size);
Vector<Point2> _get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_isometric_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
Vector<Point2> _get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
Vector<Point2> _get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
protected:
static void _bind_methods();
public:
// --- Plugins ---
Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const;
// --- Accessors for TileSet data ---
// -- Shape and layout --
void set_tile_shape(TileShape p_shape);
TileShape get_tile_shape() const;
void set_tile_layout(TileLayout p_layout);
TileLayout get_tile_layout() const;
void set_tile_offset_axis(TileOffsetAxis p_alignment);
TileOffsetAxis get_tile_offset_axis() const;
void set_tile_size(Size2i p_size);
Size2i get_tile_size() const;
// -- Sources management --
int get_next_source_id() const;
int get_source_count() const;
int get_source_id(int p_index) const;
int add_source(Ref<TileSetSource> p_tile_set_source, int p_source_id_override = -1);
void set_source_id(int p_source_id, int p_new_id);
void remove_source(int p_source_id);
void remove_source_ptr(TileSetSource *p_tile_set_source); // Not exposed
bool has_source(int p_source_id) const;
Ref<TileSetSource> get_source(int p_source_id) const;
// Rendering
void set_uv_clipping(bool p_uv_clipping);
bool is_uv_clipping() const;
int get_occlusion_layers_count() const;
void add_occlusion_layer(int p_index = -1);
void move_occlusion_layer(int p_from_index, int p_to_pos);
void remove_occlusion_layer(int p_index);
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
int get_occlusion_layer_light_mask(int p_layer_index) const;
void set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision);
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
// Physics
int get_physics_layers_count() const;
void add_physics_layer(int p_index = -1);
void move_physics_layer(int p_from_index, int p_to_pos);
void remove_physics_layer(int p_index);
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
// Terrain sets
int get_terrain_sets_count() const;
void add_terrain_set(int p_index = -1);
void move_terrain_set(int p_from_index, int p_to_pos);
void remove_terrain_set(int p_index);
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
// Terrains
int get_terrains_count(int p_terrain_set) const;
void add_terrain(int p_terrain_set, int p_index = -1);
void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
void remove_terrain(int p_terrain_set, int p_index);
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
bool is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const;
bool is_valid_terrain_peering_bit(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
// Navigation
int get_navigation_layers_count() const;
void add_navigation_layer(int p_index = -1);
void move_navigation_layer(int p_from_index, int p_to_pos);
void remove_navigation_layer(int p_index);
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
void set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value);
bool get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const;
// Custom data
int get_custom_data_layers_count() const;
void add_custom_data_layer(int p_index = -1);
void move_custom_data_layer(int p_from_index, int p_to_pos);
void remove_custom_data_layer(int p_index);
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_layer_name(int p_layer_id, String p_value);
String get_custom_data_layer_name(int p_layer_id) const;
void set_custom_data_layer_type(int p_layer_id, Variant::Type p_value);
Variant::Type get_custom_data_layer_type(int p_layer_id) const;
// Tiles proxies.
void set_source_level_tile_proxy(int p_source_from, int p_source_to);
int get_source_level_tile_proxy(int p_source_from);
bool has_source_level_tile_proxy(int p_source_from);
void remove_source_level_tile_proxy(int p_source_from);
void set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to);
Array get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
bool has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
void remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
void set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to);
Array get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
bool has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
void remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
Array get_source_level_tile_proxies() const;
Array get_coords_level_tile_proxies() const;
Array get_alternative_level_tile_proxies() const;
Array map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const;
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();
// Terrains.
RBSet<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
RBSet<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
// Helpers
Vector<Vector2> get_tile_shape_polygon() const;
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()) const;
// Used by TileMap/TileMapLayer
Vector2 map_to_local(const Vector2i &p_pos) const;
Vector2i local_to_map(const Vector2 &p_pos) const;
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords) const;
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) const;
void draw_cells_outline(CanvasItem *p_canvas_item, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D()) const;
Vector<Point2> get_terrain_polygon(int p_terrain_set);
Vector<Point2> get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size);
// Resource management
virtual void reset_state() override;
// Helpers.
static Vector2i transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
TileSet();
~TileSet();
};
class TileSetSource : public Resource {
GDCLASS(TileSetSource, Resource);
protected:
const TileSet *tile_set = nullptr;
static void _bind_methods();
public:
static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
static const int INVALID_TILE_ALTERNATIVE; // -1;
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
TileSet *get_tile_set() const;
virtual void notify_tile_data_properties_should_change(){};
virtual void add_occlusion_layer(int p_index){};
virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
virtual void remove_occlusion_layer(int p_index){};
virtual void add_physics_layer(int p_index){};
virtual void move_physics_layer(int p_from_index, int p_to_pos){};
virtual void remove_physics_layer(int p_index){};
virtual void add_terrain_set(int p_index){};
virtual void move_terrain_set(int p_from_index, int p_to_pos){};
virtual void remove_terrain_set(int p_index){};
virtual void add_terrain(int p_terrain_set, int p_index){};
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
virtual void remove_terrain(int p_terrain_set, int p_index){};
virtual void add_navigation_layer(int p_index){};
virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
virtual void remove_navigation_layer(int p_index){};
virtual void add_custom_data_layer(int p_index){};
virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
virtual void remove_custom_data_layer(int p_index){};
virtual void reset_state() override;
// Tiles.
virtual int get_tiles_count() const = 0;
virtual Vector2i get_tile_id(int tile_index) const = 0;
virtual bool has_tile(Vector2i p_atlas_coords) const = 0;
// Alternative tiles.
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0;
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0;
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0;
};
class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
public:
enum TileAnimationMode {
TILE_ANIMATION_MODE_DEFAULT,
TILE_ANIMATION_MODE_RANDOM_START_TIMES,
TILE_ANIMATION_MODE_MAX,
};
enum TransformBits {
TRANSFORM_FLIP_H = 1 << 12,
TRANSFORM_FLIP_V = 1 << 13,
TRANSFORM_TRANSPOSE = 1 << 14,
};
private:
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
Vector2i texture_offset;
// Animation
int animation_columns = 0;
Vector2i animation_separation;
real_t animation_speed = 1.0;
TileSetAtlasSource::TileAnimationMode animation_mode = TILE_ANIMATION_MODE_DEFAULT;
LocalVector<real_t> animation_frames_durations;
// Alternatives
HashMap<int, TileData *> alternatives;
Vector<int> alternatives_ids;
int next_alternative_id = 1;
};
Ref<Texture2D> texture;
Vector2i margins;
Vector2i separation;
Size2i texture_region_size = Size2i(16, 16);
HashMap<Vector2i, TileAlternativesData> tiles;
Vector<Vector2i> tiles_ids;
HashMap<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
bool use_texture_padding = true;
Ref<CanvasTexture> padded_texture;
bool padded_texture_needs_update = false;
void _queue_update_padded_texture();
Ref<ImageTexture> _create_padded_image_texture(const Ref<Texture2D> &p_source);
void _update_padded_texture();
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:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
const TileSet *get_tile_set() const;
virtual void notify_tile_data_properties_should_change() override;
virtual void add_occlusion_layer(int p_index) override;
virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
virtual void remove_occlusion_layer(int p_index) override;
virtual void add_physics_layer(int p_index) override;
virtual void move_physics_layer(int p_from_index, int p_to_pos) override;
virtual void remove_physics_layer(int p_index) override;
virtual void add_terrain_set(int p_index) override;
virtual void move_terrain_set(int p_from_index, int p_to_pos) override;
virtual void remove_terrain_set(int p_index) override;
virtual void add_terrain(int p_terrain_set, int p_index) override;
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override;
virtual void remove_terrain(int p_terrain_set, int p_index) override;
virtual void add_navigation_layer(int p_index) override;
virtual void move_navigation_layer(int p_from_index, int p_to_pos) override;
virtual void remove_navigation_layer(int p_index) override;
virtual void add_custom_data_layer(int p_index) override;
virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override;
virtual void remove_custom_data_layer(int p_index) override;
virtual void reset_state() override;
// Base properties.
void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
void set_margins(Vector2i p_margins);
Vector2i get_margins() const;
void set_separation(Vector2i p_separation);
Vector2i get_separation() const;
void set_texture_region_size(Vector2i p_tile_size);
Vector2i get_texture_region_size() const;
// Padding.
void set_use_texture_padding(bool p_use_padding);
bool get_use_texture_padding() const;
// Base tiles.
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
void remove_tile(Vector2i p_atlas_coords);
virtual bool has_tile(Vector2i p_atlas_coords) const override;
void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
virtual int get_tiles_count() const override;
virtual Vector2i get_tile_id(int p_index) const override;
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
bool has_tiles_outside_texture() const;
Vector<Vector2i> get_tiles_outside_texture() const;
void clear_tiles_outside_texture();
// Animation.
void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns);
int get_tile_animation_columns(const Vector2i p_atlas_coords) const;
void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation);
Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const;
void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed);
real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const;
void set_tile_animation_mode(const Vector2i p_atlas_coords, const TileSetAtlasSource::TileAnimationMode p_mode);
TileSetAtlasSource::TileAnimationMode get_tile_animation_mode(const Vector2i p_atlas_coords) const;
void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count);
int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const;
void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration);
real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const;
real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const;
// Alternative tiles.
int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id);
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const;
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
// Get data associated to a tile.
TileData *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
// Helpers.
Vector2i get_atlas_grid_size() const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
static int alternative_no_transform(int p_alternative_id);
// Getters for texture and tile region (padded or not)
Ref<Texture2D> get_runtime_texture() const;
Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
~TileSetAtlasSource();
};
class TileSetScenesCollectionSource : public TileSetSource {
GDCLASS(TileSetScenesCollectionSource, TileSetSource);
private:
struct SceneData {
Ref<PackedScene> scene;
bool display_placeholder = false;
};
Vector<int> scenes_ids;
HashMap<int, SceneData> scenes;
int next_scene_id = 1;
void _compute_next_alternative_id();
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:
// Tiles.
int get_tiles_count() const override;
Vector2i get_tile_id(int p_tile_index) const override;
bool has_tile(Vector2i p_atlas_coords) const override;
// Alternative tiles.
int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
// Scenes accessors. Lot are similar to "Alternative tiles".
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
int create_scene_tile(Ref<PackedScene> p_packed_scene = Ref<PackedScene>(), int p_id_override = -1);
void set_scene_tile_id(int p_id, int p_new_id);
void set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene);
Ref<PackedScene> get_scene_tile_scene(int p_id) const;
void set_scene_tile_display_placeholder(int p_id, bool p_packed_scene);
bool get_scene_tile_display_placeholder(int p_id) const;
void remove_scene_tile(int p_id);
int get_next_scene_tile_id() const;
};
class TileData : public Object {
GDCLASS(TileData, Object);
private:
const TileSet *tile_set = nullptr;
bool allow_transform = true;
// Rendering
bool flip_h = false;
bool flip_v = false;
bool transpose = false;
Vector2i texture_origin;
Ref<Material> material = Ref<Material>();
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
int y_sort_origin = 0;
struct OcclusionLayerTileData {
Ref<OccluderPolygon2D> occluder;
mutable HashMap<int, Ref<OccluderPolygon2D>> transformed_occluders;
};
Vector<OcclusionLayerTileData> occluders;
// Physics
struct PhysicsLayerTileData {
struct PolygonShapeTileData {
LocalVector<Vector2> polygon;
LocalVector<Ref<ConvexPolygonShape2D>> shapes;
mutable HashMap<int, LocalVector<Ref<ConvexPolygonShape2D>>> transformed_shapes;
bool one_way = false;
float one_way_margin = 1.0;
};
Vector2 linear_velocity;
double angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
// TODO add support for areas.
// Terrain
int terrain_set = -1;
int terrain = -1;
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
// Navigation
struct NavigationLayerTileData {
Ref<NavigationPolygon> navigation_polygon;
mutable HashMap<int, Ref<NavigationPolygon>> transformed_navigation_polygon;
};
Vector<NavigationLayerTileData> navigation;
// Misc
double probability = 1.0;
// Custom data
Vector<Variant> custom_data;
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();
#ifndef DISABLE_DEPRECATED
Ref<NavigationPolygon> _get_navigation_polygon_bind_compat_84660(int p_layer_id) const;
Ref<OccluderPolygon2D> _get_occluder_bind_compat_84660(int p_layer_id) const;
static void _bind_compatibility_methods();
#endif
public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
void notify_tile_data_properties_should_change();
void add_occlusion_layer(int p_index);
void move_occlusion_layer(int p_from_index, int p_to_pos);
void remove_occlusion_layer(int p_index);
void add_physics_layer(int p_index);
void move_physics_layer(int p_from_index, int p_to_pos);
void remove_physics_layer(int p_index);
void add_terrain_set(int p_index);
void move_terrain_set(int p_from_index, int p_to_pos);
void remove_terrain_set(int p_index);
void add_terrain(int p_terrain_set, int p_index);
void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
void remove_terrain(int p_terrain_set, int p_index);
void add_navigation_layer(int p_index);
void move_navigation_layer(int p_from_index, int p_to_pos);
void remove_navigation_layer(int p_index);
void add_custom_data_layer(int p_index);
void move_custom_data_layer(int p_from_index, int p_to_pos);
void remove_custom_data_layer(int p_index);
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
// To duplicate a TileData object, needed for runtiume update.
TileData *duplicate();
// Rendering
void set_flip_h(bool p_flip_h);
bool get_flip_h() const;
void set_flip_v(bool p_flip_v);
bool get_flip_v() const;
void set_transpose(bool p_transpose);
bool get_transpose() const;
void set_texture_origin(Vector2i p_texture_origin);
Vector2i get_texture_origin() const;
void set_material(Ref<Material> p_material);
Ref<Material> get_material() const;
void set_modulate(Color p_modulate);
Color get_modulate() const;
void set_z_index(int p_z_index);
int get_z_index() const;
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
Ref<OccluderPolygon2D> get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Physics
void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
Vector2 get_constant_linear_velocity(int p_layer_id) const;
void set_constant_angular_velocity(int p_layer_id, real_t p_velocity);
real_t get_constant_angular_velocity(int p_layer_id) const;
void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
int get_collision_polygons_count(int p_layer_id) const;
void add_collision_polygon(int p_layer_id);
void remove_collision_polygon(int p_layer_id, int p_polygon_index);
void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);
Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const;
void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way);
bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const;
void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin);
float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const;
int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const;
Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Terrain
void set_terrain_set(int p_terrain_id);
int get_terrain_set() const;
void set_terrain(int p_terrain_id);
int get_terrain() const;
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
bool is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
// Navigation
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Misc
void set_probability(float p_probability);
float get_probability() const;
// Custom data.
void set_custom_data(String p_layer_name, Variant p_value);
Variant get_custom_data(String p_layer_name) const;
void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
Variant get_custom_data_by_layer_id(int p_layer_id) const;
// Polygons.
static PackedVector2Array get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose);
};
VARIANT_ENUM_CAST(TileSet::CellNeighbor);
VARIANT_ENUM_CAST(TileSet::TerrainMode);
VARIANT_ENUM_CAST(TileSet::TileShape);
VARIANT_ENUM_CAST(TileSet::TileLayout);
VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
VARIANT_ENUM_CAST(TileSetAtlasSource::TileAnimationMode);
#endif // TILE_SET_H