initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
editor/scene/2d/tiles/SCsub
Normal file
6
editor/scene/2d/tiles/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
369
editor/scene/2d/tiles/atlas_merging_dialog.cpp
Normal file
369
editor/scene/2d/tiles/atlas_merging_dialog.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
/**************************************************************************/
|
||||
/* 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_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_null());
|
||||
|
||||
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();
|
||||
committed_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 < committed_actions_count; i++) {
|
||||
undo_redo->undo();
|
||||
}
|
||||
committed_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_null());
|
||||
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);
|
||||
|
||||
committed_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->set_theme_type_variation("ItemListSecondary");
|
||||
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_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
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);
|
||||
}
|
84
editor/scene/2d/tiles/atlas_merging_dialog.h
Normal file
84
editor/scene/2d/tiles/atlas_merging_dialog.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/inspector/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 committed_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();
|
||||
};
|
766
editor/scene/2d/tiles/tile_atlas_view.cpp
Normal file
766
editor/scene/2d/tiles/tile_atlas_view.cpp
Normal file
@@ -0,0 +1,766 @@
|
||||
/**************************************************************************/
|
||||
/* 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 "editor/settings/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/2d/tile_map_layer.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, get_global_rect())) {
|
||||
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, p_origin);
|
||||
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
|
||||
}
|
||||
|
||||
Size2i TileAtlasView::_compute_base_tiles_control_size() {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return Size2i();
|
||||
}
|
||||
// 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() {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return Size2i();
|
||||
}
|
||||
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, const Vector2 &p_mouse_pos) {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
Vector2 relative_mpos = p_mouse_pos - 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) {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
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() {
|
||||
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
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.
|
||||
TileMapLayer::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());
|
||||
RS::get_singleton()->canvas_item_set_default_texture_filter(ci_rid, RS::CanvasItemTextureFilter(p_base_item->get_texture_filter_in_tree()));
|
||||
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() {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
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() {
|
||||
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
// 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_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
|
||||
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() {
|
||||
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
TileMapLayer::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 = Ref<TileSet>(p_tile_set);
|
||||
tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);
|
||||
|
||||
_clear_material_canvas_items();
|
||||
|
||||
if (tile_set.is_null()) {
|
||||
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 {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return Vector2i();
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (texture.is_null()) {
|
||||
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 = ret.clamp(Vector2i(), size - Vector2i(1, 1));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TileAtlasView::_update_alternative_tiles_rect_cache() {
|
||||
if (tile_set_atlas_source.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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")));
|
||||
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
button_center_view->set_button_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(SceneStringName(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(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::gui_input));
|
||||
center_container->connect(SceneStringName(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_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_alternatives));
|
||||
alternative_tiles_drawing_root->add_child(alternatives_draw);
|
||||
}
|
||||
|
||||
TileAtlasView::~TileAtlasView() {
|
||||
_clear_material_canvas_items();
|
||||
}
|
171
editor/scene/2d/tiles/tile_atlas_view.h
Normal file
171
editor/scene/2d/tiles/tile_atlas_view.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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:
|
||||
Ref<TileSet> tile_set;
|
||||
Ref<TileSetAtlasSource> tile_set_atlas_source;
|
||||
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, const Vector2 &p_mouse_pos = Vector2());
|
||||
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();
|
||||
};
|
3039
editor/scene/2d/tiles/tile_data_editors.cpp
Normal file
3039
editor/scene/2d/tiles/tile_data_editors.cpp
Normal file
File diff suppressed because it is too large
Load Diff
415
editor/scene/2d/tiles/tile_data_editors.h
Normal file
415
editor/scene/2d/tiles/tile_data_editors.h
Normal file
@@ -0,0 +1,415 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
|
||||
#include "editor/inspector/editor_properties.h"
|
||||
#include "scene/gui/box_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;
|
||||
bool initializing = true;
|
||||
|
||||
Ref<TileSetAtlasSource> background_atlas_source;
|
||||
Vector2i background_atlas_coords;
|
||||
int background_alternative_id;
|
||||
|
||||
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_tile(const TileSetAtlasSource *p_atlas_source, const Vector2 &p_atlas_coords, int p_alternative_id);
|
||||
|
||||
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();
|
||||
};
|
4539
editor/scene/2d/tiles/tile_map_layer_editor.cpp
Normal file
4539
editor/scene/2d/tiles/tile_map_layer_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
427
editor/scene/2d/tiles/tile_map_layer_editor.h
Normal file
427
editor/scene/2d/tiles/tile_map_layer_editor.h
Normal file
@@ -0,0 +1,427 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
|
||||
#include "core/os/thread.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 TileMapLayer;
|
||||
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) {}
|
||||
virtual void draw_tile_coords_over_viewport(Control *p_overlay, const TileMapLayer *p_edited_layer, Ref<TileSet> p_tile_set, bool p_show_rectangle_size, const Vector2i &p_rectangle_origin);
|
||||
};
|
||||
|
||||
class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
|
||||
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
|
||||
|
||||
public:
|
||||
enum TileTransformType {
|
||||
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(TileTransformType p_type);
|
||||
int _get_transformed_alternative(int p_alternative_id, TileTransformType p_transform);
|
||||
|
||||
///// Selection system. /////
|
||||
RBSet<Vector2i> tile_map_selection;
|
||||
Ref<TileMapPattern> tile_map_clipboard;
|
||||
Ref<TileMapPattern> selection_pattern;
|
||||
Ref<TileMapPattern> erase_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();
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
class TileMapLayerEditor : public VBoxContainer {
|
||||
GDCLASS(TileMapLayerEditor, VBoxContainer);
|
||||
|
||||
private:
|
||||
bool tile_map_layer_changed_needs_update = false;
|
||||
|
||||
ObjectID edited_tile_map_layer_id;
|
||||
bool is_multi_node_edit = false;
|
||||
Vector<TileMapLayer *> tile_map_layers_in_scene_cache;
|
||||
bool layers_in_scene_list_cache_needs_update = false;
|
||||
TileMapLayer *_get_edited_layer() const;
|
||||
void _find_tile_map_layers_in_scene(Node *p_current, const Node *p_owner, Vector<TileMapLayer *> &r_list) const;
|
||||
void _update_tile_map_layers_in_scene_list_cache();
|
||||
void _node_change(Node *p_node);
|
||||
|
||||
Control *custom_overlay = nullptr;
|
||||
void _draw_overlay();
|
||||
|
||||
// Vector to keep plugins.
|
||||
Vector<TileMapLayerSubEditorPlugin *> tile_map_editor_plugins;
|
||||
|
||||
// Toolbar.
|
||||
HFlowContainer *tile_map_toolbar = nullptr;
|
||||
|
||||
bool show_layers_selector = false;
|
||||
|
||||
HBoxContainer *layer_selection_hbox = nullptr;
|
||||
Button *select_previous_layer = nullptr;
|
||||
void _select_previous_layer_pressed();
|
||||
Button *select_next_layer = nullptr;
|
||||
void _select_next_layer_pressed();
|
||||
Button *select_all_layers = nullptr;
|
||||
void _select_all_layers_pressed();
|
||||
OptionButton *layers_selection_button = nullptr;
|
||||
void _layers_selection_item_selected(int p_index);
|
||||
void _update_layers_selector();
|
||||
|
||||
Button *toggle_highlight_selected_layer_button = nullptr;
|
||||
void _clear_all_layers_highlighting();
|
||||
void _update_all_layers_highlighting();
|
||||
void _highlight_selected_layer_button_toggled(bool p_pressed);
|
||||
|
||||
Button *toggle_grid_button = nullptr;
|
||||
void _on_grid_toggled(bool p_pressed);
|
||||
|
||||
enum {
|
||||
ADVANCED_MENU_REPLACE_WITH_PROXIES,
|
||||
ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS,
|
||||
};
|
||||
MenuButton *advanced_menu_button = nullptr;
|
||||
void _advanced_menu_button_id_pressed(int p_id);
|
||||
|
||||
// Bottom panel.
|
||||
Label *cant_edit_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);
|
||||
|
||||
// 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);
|
||||
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(Object *p_tile_map_layer);
|
||||
void set_show_layer_selector(bool p_show_layer_selector);
|
||||
|
||||
TileMapLayerEditor();
|
||||
~TileMapLayerEditor();
|
||||
|
||||
// Static functions.
|
||||
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TileMapLayerEditorTilesPlugin::TileTransformType);
|
492
editor/scene/2d/tiles/tile_proxies_manager_dialog.cpp
Normal file
492
editor/scene/2d/tiles/tile_proxies_manager_dialog.cpp
Normal file
@@ -0,0 +1,492 @@
|
||||
/**************************************************************************/
|
||||
/* 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_undo_redo_manager.h"
|
||||
#include "editor/inspector/editor_properties_vector.h"
|
||||
#include "editor/settings/editor_settings.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();
|
||||
|
||||
committed_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();
|
||||
committed_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).maxi(-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).maxi(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 < committed_actions_count; i++) {
|
||||
undo_redo->undo();
|
||||
}
|
||||
committed_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_null());
|
||||
tile_set = p_tile_set;
|
||||
committed_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(SceneStringName(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(SceneStringName(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(SceneStringName(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(SceneStringName(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);
|
||||
}
|
86
editor/scene/2d/tiles/tile_proxies_manager_dialog.h
Normal file
86
editor/scene/2d/tiles/tile_proxies_manager_dialog.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/inspector/editor_properties.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class EditorPropertyVector2i;
|
||||
class EditorUndoRedoManager;
|
||||
|
||||
class TileProxiesManagerDialog : public ConfirmationDialog {
|
||||
GDCLASS(TileProxiesManagerDialog, ConfirmationDialog);
|
||||
|
||||
private:
|
||||
int committed_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();
|
||||
};
|
3040
editor/scene/2d/tiles/tile_set_atlas_source_editor.cpp
Normal file
3040
editor/scene/2d/tiles/tile_set_atlas_source_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
334
editor/scene/2d/tiles/tile_set_atlas_source_editor.h
Normal file
334
editor/scene/2d/tiles/tile_set_atlas_source_editor.h
Normal file
@@ -0,0 +1,334 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
|
||||
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 proxied 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;
|
||||
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;
|
||||
Label *help_label = 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_create_button_pressed(const Vector2i &p_atlas_coords);
|
||||
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();
|
||||
void _update_buttons();
|
||||
|
||||
// -- 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;
|
||||
};
|
1038
editor/scene/2d/tiles/tile_set_editor.cpp
Normal file
1038
editor/scene/2d/tiles/tile_set_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
142
editor/scene/2d/tiles/tile_set_editor.h
Normal file
142
editor/scene/2d/tiles/tile_set_editor.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 MarginContainer {
|
||||
GDCLASS(TileSetEditor, MarginContainer);
|
||||
|
||||
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;
|
||||
};
|
@@ -0,0 +1,593 @@
|
||||
/**************************************************************************/
|
||||
/* 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_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/file_system/editor_file_system.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/inspector/editor_resource_preview.h"
|
||||
#include "editor/scene/2d/tiles/tile_set_editor.h"
|
||||
#include "editor/settings/editor_settings.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"
|
||||
|
||||
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(CoreStringName(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(CoreStringName(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_null());
|
||||
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(CoreStringName(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(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed))) {
|
||||
tile_set_scenes_collection_source->connect(CoreStringName(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(CoreStringName(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(CoreStringName(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(CoreStringName(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()->load_scene(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.is_empty() || 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_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
scene_tile_delete_button->set_button_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_null());
|
||||
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.is_empty()) {
|
||||
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_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
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(CoreStringName(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);
|
||||
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(CoreStringName(changed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_scenes_list).unbind(1));
|
||||
tile_proxy_object->connect(CoreStringName(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(SceneStringName(item_selected), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1));
|
||||
scene_tiles_list->connect(SceneStringName(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(SceneStringName(FlatButton));
|
||||
scene_tile_add_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_add_pressed));
|
||||
scene_tile_add_button->set_accessibility_name(TTRC("Add"));
|
||||
scenes_bottom_actions->add_child(scene_tile_add_button);
|
||||
|
||||
scene_tile_delete_button = memnew(Button);
|
||||
scene_tile_delete_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
scene_tile_delete_button->set_disabled(true);
|
||||
scene_tile_delete_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed));
|
||||
scene_tile_delete_button->set_accessibility_name(TTRC("Delete"));
|
||||
scenes_bottom_actions->add_child(scene_tile_delete_button);
|
||||
|
||||
EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin));
|
||||
}
|
||||
|
||||
TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() {
|
||||
memdelete(scenes_collection_source_proxy_object);
|
||||
memdelete(tile_proxy_object);
|
||||
}
|
145
editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h
Normal file
145
editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/inspector/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 proxied 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();
|
||||
};
|
555
editor/scene/2d/tiles/tiles_editor_plugin.cpp
Normal file
555
editor/scene/2d/tiles/tiles_editor_plugin.cpp
Normal file
@@ -0,0 +1,555 @@
|
||||
/**************************************************************************/
|
||||
/* 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_string_names.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/inspector/multi_node_edit.h"
|
||||
#include "editor/scene/canvas_item_editor_plugin.h"
|
||||
#include "editor/settings/editor_command_palette.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/2d/tile_map_layer.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/control.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(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.is_empty()) {
|
||||
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);
|
||||
|
||||
TileMapLayer *tile_map_layer = memnew(TileMapLayer);
|
||||
tile_map_layer->set_tile_set(item.tile_set);
|
||||
tile_map_layer->set_pattern(Vector2(), item.pattern);
|
||||
viewport->add_child(tile_map_layer);
|
||||
|
||||
Rect2 encompassing_rect;
|
||||
encompassing_rect.set_position(tile_map_layer->map_to_local(tile_map_layer->get_tile_map_layer_data().begin()->key));
|
||||
for (KeyValue<Vector2i, CellData> kv : tile_map_layer->get_tile_map_layer_data()) {
|
||||
Vector2i cell = kv.key;
|
||||
Vector2 world_pos = tile_map_layer->map_to_local(cell);
|
||||
encompassing_rect.expand_to(world_pos);
|
||||
|
||||
// Texture.
|
||||
Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map_layer->get_cell_source_id(cell));
|
||||
if (atlas_source.is_valid()) {
|
||||
Vector2i coords = tile_map_layer->get_cell_atlas_coords(cell);
|
||||
int alternative = tile_map_layer->get_cell_alternative_tile(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_layer->set_scale(scale);
|
||||
tile_map_layer->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(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_null());
|
||||
ERR_FAIL_COND(p_pattern.is_null());
|
||||
{
|
||||
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(SceneStringName(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);
|
||||
|
||||
ED_SHORTCUT("tiles_editor/cut", TTRC("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
|
||||
ED_SHORTCUT("tiles_editor/copy", TTRC("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
|
||||
ED_SHORTCUT("tiles_editor/paste", TTRC("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
|
||||
ED_SHORTCUT("tiles_editor/cancel", TTRC("Cancel"), Key::ESCAPE);
|
||||
ED_SHORTCUT("tiles_editor/delete", TTRC("Delete"), Key::KEY_DELETE);
|
||||
|
||||
ED_SHORTCUT("tiles_editor/paint_tool", TTRC("Paint Tool"), Key::D);
|
||||
ED_SHORTCUT("tiles_editor/line_tool", TTRC("Line Tool"), Key::L);
|
||||
ED_SHORTCUT("tiles_editor/rect_tool", TTRC("Rect Tool"), Key::R);
|
||||
ED_SHORTCUT("tiles_editor/bucket_tool", TTRC("Bucket Tool"), Key::B);
|
||||
ED_SHORTCUT("tiles_editor/eraser", TTRC("Eraser Tool"), Key::E);
|
||||
ED_SHORTCUT("tiles_editor/picker", TTRC("Picker Tool"), Key::P);
|
||||
}
|
||||
|
||||
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 = ObjectDB::get_instance<TileMap>(tile_map_group_id);
|
||||
if (tile_map) {
|
||||
edit(tile_map);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_update_tile_map() {
|
||||
TileMapLayer *edited_layer = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
|
||||
if (edited_layer) {
|
||||
Ref<TileSet> tile_set = edited_layer->get_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 = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
|
||||
ERR_FAIL_NULL(edited_layer);
|
||||
|
||||
Node *parent = edited_layer->get_parent();
|
||||
if (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, bool p_show_layer_selector) {
|
||||
ERR_FAIL_NULL(p_tile_map_layer);
|
||||
|
||||
editor->edit(p_tile_map_layer);
|
||||
editor->set_show_layer_selector(p_show_layer_selector);
|
||||
|
||||
// Update the object IDs.
|
||||
tile_map_layer_id = p_tile_map_layer->get_instance_id();
|
||||
p_tile_map_layer->connect(CoreStringName(changed), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
|
||||
p_tile_map_layer->connect(SceneStringName(tree_exited), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
|
||||
|
||||
// Update the edited tileset.
|
||||
Ref<TileSet> tile_set = p_tile_map_layer->get_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(TileMap *p_tile_map) {
|
||||
ERR_FAIL_NULL(p_tile_map);
|
||||
|
||||
if (p_tile_map->get_layers_count() > 0) {
|
||||
TileMapLayer *selected_layer = Object::cast_to<TileMapLayer>(p_tile_map->get_child(0));
|
||||
_edit_tile_map_layer(selected_layer, true);
|
||||
} else {
|
||||
editor->edit(nullptr);
|
||||
editor->set_show_layer_selector(false);
|
||||
}
|
||||
}
|
||||
|
||||
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 = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
|
||||
if (edited_layer) {
|
||||
edited_layer->disconnect(CoreStringName(changed), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
|
||||
edited_layer->disconnect(SceneStringName(tree_exited), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
|
||||
}
|
||||
|
||||
tile_map_group_id = ObjectID();
|
||||
tile_map_layer_id = ObjectID();
|
||||
tile_set_id = ObjectID();
|
||||
|
||||
TileMap *tile_map = Object::cast_to<TileMap>(p_object);
|
||||
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_object);
|
||||
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(p_object);
|
||||
if (tile_map) {
|
||||
_edit_tile_map(tile_map);
|
||||
} else if (tile_map_layer) {
|
||||
_edit_tile_map_layer(tile_map_layer, false);
|
||||
} else if (multi_node_edit) {
|
||||
editor->edit(multi_node_edit);
|
||||
} else {
|
||||
editor->edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMapEditorPlugin::handles(Object *p_object) const {
|
||||
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(p_object);
|
||||
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
|
||||
if (multi_node_edit && edited_scene) {
|
||||
bool only_tile_map_layers = true;
|
||||
for (int i = 0; i < multi_node_edit->get_node_count(); i++) {
|
||||
if (!Object::cast_to<TileMapLayer>(edited_scene->get_node(multi_node_edit->get_node(i)))) {
|
||||
only_tile_map_layers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return only_tile_map_layers;
|
||||
}
|
||||
return Object::cast_to<TileMapLayer>(p_object) != nullptr || Object::cast_to<TileMap>(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->hide();
|
||||
|
||||
button = EditorNode::get_bottom_panel()->add_item(TTRC("TileMap"), editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_tile_map_bottom_panel", TTRC("Toggle TileMap Bottom Panel")));
|
||||
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(TTRC("TileSet"), editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_tile_set_bottom_panel", TTRC("Toggle TileSet Bottom Panel")));
|
||||
button->hide();
|
||||
}
|
||||
|
||||
TileSetEditorPlugin::~TileSetEditorPlugin() {
|
||||
tile_set_plugin_singleton = nullptr;
|
||||
}
|
164
editor/scene/2d/tiles/tiles_editor_plugin.h
Normal file
164
editor/scene/2d/tiles/tiles_editor_plugin.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.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, bool p_show_layer_selector);
|
||||
void _edit_tile_map(TileMap *p_tile_map);
|
||||
|
||||
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();
|
||||
};
|
Reference in New Issue
Block a user