mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-02-02 22:35:55 +01:00
Added godot 4's tilemap as a new layered tilemap module.
This commit is contained in:
parent
81717d4505
commit
eec9c78867
11
modules/layered_tile_map/SCsub
Normal file
11
modules/layered_tile_map/SCsub
Normal file
@ -0,0 +1,11 @@
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.modules_sources,"register_types.cpp")
|
||||
env.add_source_files(env.modules_sources,"tile_map.cpp")
|
||||
env.add_source_files(env.modules_sources,"tile_set.cpp")
|
||||
env.add_source_files(env.modules_sources, "geometry_parser/*.cpp")
|
||||
|
||||
if env["tools"]:
|
||||
env.add_source_files(env.modules_sources, "tile_map_editor_plugin.cpp")
|
||||
env.add_source_files(env.modules_sources, "tile_set_editor_plugin.cpp")
|
||||
|
15
modules/layered_tile_map/config.py
Normal file
15
modules/layered_tile_map/config.py
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
def can_build(env, platform):
|
||||
return False
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
|
||||
def get_doc_classes():
|
||||
return [
|
||||
"TileMap",
|
||||
"TileSet"
|
||||
]
|
||||
|
||||
def get_doc_path():
|
||||
return "doc_classes"
|
368
modules/layered_tile_map/editor/atlas_merging_dialog.cpp
Normal file
368
modules/layered_tile_map/editor/atlas_merging_dialog.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
/**************************************************************************/
|
||||
/* atlas_merging_dialog.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "atlas_merging_dialog.h"
|
||||
|
||||
#include "editor/editor_properties_vector.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void AtlasMergingDialog::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
|
||||
_set(p_property, p_value);
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::_generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns) {
|
||||
merged.instantiate();
|
||||
merged_mapping.clear();
|
||||
|
||||
if (p_atlas_sources.size() >= 2) {
|
||||
Ref<Image> output_image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8);
|
||||
|
||||
// Compute the new texture region size.
|
||||
Vector2i new_texture_region_size;
|
||||
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
|
||||
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
|
||||
new_texture_region_size = new_texture_region_size.max(atlas_source->get_texture_region_size());
|
||||
}
|
||||
|
||||
// Generate the new texture.
|
||||
Vector2i atlas_offset;
|
||||
int line_height = 0;
|
||||
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
|
||||
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
|
||||
Ref<Image> input_image = atlas_source->get_texture()->get_image();
|
||||
if (input_image->get_format() != Image::FORMAT_RGBA8) {
|
||||
input_image->convert(Image::FORMAT_RGBA8);
|
||||
}
|
||||
merged_mapping.push_back(HashMap<Vector2i, Vector2i>());
|
||||
|
||||
// Layout the tiles.
|
||||
Vector2i atlas_size;
|
||||
|
||||
for (int tile_index = 0; tile_index < atlas_source->get_tiles_count(); tile_index++) {
|
||||
Vector2i tile_id = atlas_source->get_tile_id(tile_index);
|
||||
atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id));
|
||||
|
||||
Rect2i new_tile_rect_in_atlas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
|
||||
|
||||
// Copy the texture.
|
||||
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
|
||||
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
|
||||
Vector2i new_position = new_tile_rect_in_atlas.position * new_texture_region_size;
|
||||
if (frame > 0) {
|
||||
new_position += src_rect.size * Vector2i(frame, 0);
|
||||
atlas_size.x = MAX(frame + 1, atlas_size.x);
|
||||
}
|
||||
Rect2 dst_rect_wide = Rect2i(new_position, new_tile_rect_in_atlas.size * new_texture_region_size);
|
||||
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
|
||||
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
|
||||
}
|
||||
output_image->blit_rect(input_image, src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
|
||||
}
|
||||
|
||||
// Add to the mapping.
|
||||
merged_mapping[source_index][tile_id] = new_tile_rect_in_atlas.position;
|
||||
}
|
||||
|
||||
// Compute the atlas offset.
|
||||
line_height = MAX(atlas_size.y, line_height);
|
||||
atlas_offset.x += atlas_size.x;
|
||||
if (atlas_offset.x >= p_max_columns) {
|
||||
atlas_offset.x = 0;
|
||||
atlas_offset.y += line_height;
|
||||
line_height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
merged->set_name(p_atlas_sources[0]->get_name());
|
||||
merged->set_texture(ImageTexture::create_from_image(output_image));
|
||||
merged->set_texture_region_size(new_texture_region_size);
|
||||
|
||||
// Copy the tiles to the merged TileSetAtlasSource.
|
||||
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
|
||||
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
|
||||
for (KeyValue<Vector2i, Vector2i> tile_mapping : merged_mapping[source_index]) {
|
||||
// Create tiles and alternatives, then copy their properties.
|
||||
for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) {
|
||||
int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index);
|
||||
int changed_id = -1;
|
||||
if (alternative_id == 0) {
|
||||
merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
|
||||
int count = atlas_source->get_tile_animation_frames_count(tile_mapping.key);
|
||||
merged->set_tile_animation_frames_count(tile_mapping.value, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
merged->set_tile_animation_frame_duration(tile_mapping.value, i, atlas_source->get_tile_animation_frame_duration(tile_mapping.key, i));
|
||||
}
|
||||
merged->set_tile_animation_speed(tile_mapping.value, atlas_source->get_tile_animation_speed(tile_mapping.key));
|
||||
merged->set_tile_animation_mode(tile_mapping.value, atlas_source->get_tile_animation_mode(tile_mapping.key));
|
||||
} else {
|
||||
changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index);
|
||||
}
|
||||
|
||||
// Copy the properties.
|
||||
TileData *src_tile_data = atlas_source->get_tile_data(tile_mapping.key, alternative_id);
|
||||
List<PropertyInfo> properties;
|
||||
src_tile_data->get_property_list(&properties);
|
||||
|
||||
TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, changed_id == -1 ? alternative_id : changed_id);
|
||||
for (PropertyInfo property : properties) {
|
||||
if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
}
|
||||
Variant value = src_tile_data->get(property.name);
|
||||
Variant default_value = ClassDB::class_get_default_property_value("TileData", property.name);
|
||||
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
|
||||
continue;
|
||||
}
|
||||
dst_tile_data->set(property.name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::_update_texture() {
|
||||
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
|
||||
if (selected.size() >= 2) {
|
||||
Vector<Ref<TileSetAtlasSource>> to_merge;
|
||||
for (int i = 0; i < selected.size(); i++) {
|
||||
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
|
||||
to_merge.push_back(tile_set->get_source(source_id));
|
||||
}
|
||||
_generate_merged(to_merge, next_line_after_column);
|
||||
preview->set_texture(merged->get_texture());
|
||||
preview->show();
|
||||
select_2_atlases_label->hide();
|
||||
get_ok_button()->set_disabled(false);
|
||||
merge_button->set_disabled(false);
|
||||
} else {
|
||||
_generate_merged(Vector<Ref<TileSetAtlasSource>>(), next_line_after_column);
|
||||
preview->set_texture(Ref<Texture2D>());
|
||||
preview->hide();
|
||||
select_2_atlases_label->show();
|
||||
get_ok_button()->set_disabled(true);
|
||||
merge_button->set_disabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::_merge_confirmed(const String &p_path) {
|
||||
ERR_FAIL_COND(!merged.is_valid());
|
||||
|
||||
Ref<ImageTexture> output_image_texture = merged->get_texture();
|
||||
output_image_texture->get_image()->save_png(p_path);
|
||||
|
||||
ResourceLoader::import(p_path);
|
||||
|
||||
Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D");
|
||||
merged->set_texture(new_texture_resource);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Merge TileSetAtlasSource"));
|
||||
int next_id = tile_set->get_next_source_id();
|
||||
undo_redo->add_do_method(*tile_set, "add_source", merged, next_id);
|
||||
undo_redo->add_undo_method(*tile_set, "remove_source", next_id);
|
||||
|
||||
if (delete_original_atlases) {
|
||||
// Delete originals if needed.
|
||||
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
|
||||
for (int i = 0; i < selected.size(); i++) {
|
||||
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
|
||||
Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
|
||||
undo_redo->add_do_method(*tile_set, "remove_source", source_id);
|
||||
undo_redo->add_undo_method(*tile_set, "add_source", tas, source_id);
|
||||
|
||||
// Add the tile proxies.
|
||||
for (int tile_index = 0; tile_index < tas->get_tiles_count(); tile_index++) {
|
||||
Vector2i tile_id = tas->get_tile_id(tile_index);
|
||||
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", source_id, tile_id, next_id, merged_mapping[i][tile_id]);
|
||||
if (tile_set->has_coords_level_tile_proxy(source_id, tile_id)) {
|
||||
Array a = tile_set->get_coords_level_tile_proxy(source_id, tile_id);
|
||||
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", a[0], a[1]);
|
||||
} else {
|
||||
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", source_id, tile_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
commited_actions_count++;
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::ok_pressed() {
|
||||
delete_original_atlases = false;
|
||||
editor_file_dialog->popup_file_dialog();
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::cancel_pressed() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
for (int i = 0; i < commited_actions_count; i++) {
|
||||
undo_redo->undo();
|
||||
}
|
||||
commited_actions_count = 0;
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::custom_action(const String &p_action) {
|
||||
if (p_action == "merge") {
|
||||
delete_original_atlases = true;
|
||||
editor_file_dialog->popup_file_dialog();
|
||||
}
|
||||
}
|
||||
|
||||
bool AtlasMergingDialog::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (p_name == "next_line_after_column" && p_value.get_type() == Variant::INT) {
|
||||
next_line_after_column = p_value;
|
||||
_update_texture();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AtlasMergingDialog::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (p_name == "next_line_after_column") {
|
||||
r_ret = next_line_after_column;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (is_visible()) {
|
||||
_update_texture();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
tile_set = p_tile_set;
|
||||
|
||||
atlas_merging_atlases_list->clear();
|
||||
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
|
||||
int source_id = p_tile_set->get_source_id(i);
|
||||
Ref<TileSetAtlasSource> atlas_source = p_tile_set->get_source(source_id);
|
||||
if (atlas_source.is_valid()) {
|
||||
Ref<Texture2D> texture = atlas_source->get_texture();
|
||||
if (texture.is_valid()) {
|
||||
String item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
|
||||
atlas_merging_atlases_list->add_item(item_text, texture);
|
||||
atlas_merging_atlases_list->set_item_metadata(-1, source_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_ok_button()->set_disabled(true);
|
||||
merge_button->set_disabled(true);
|
||||
|
||||
commited_actions_count = 0;
|
||||
}
|
||||
|
||||
AtlasMergingDialog::AtlasMergingDialog() {
|
||||
// Atlas merging window.
|
||||
set_title(TTR("Atlas Merging"));
|
||||
set_hide_on_ok(false);
|
||||
|
||||
// Ok buttons
|
||||
set_ok_button_text(TTR("Merge (Keep original Atlases)"));
|
||||
get_ok_button()->set_disabled(true);
|
||||
merge_button = add_button(TTR("Merge"), true, "merge");
|
||||
merge_button->set_disabled(true);
|
||||
|
||||
HSplitContainer *atlas_merging_h_split_container = memnew(HSplitContainer);
|
||||
atlas_merging_h_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
atlas_merging_h_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_child(atlas_merging_h_split_container);
|
||||
|
||||
// Atlas sources item list.
|
||||
atlas_merging_atlases_list = memnew(ItemList);
|
||||
atlas_merging_atlases_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
|
||||
atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
|
||||
atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200));
|
||||
atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI);
|
||||
atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2));
|
||||
atlas_merging_h_split_container->add_child(atlas_merging_atlases_list);
|
||||
|
||||
VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer);
|
||||
atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
atlas_merging_right_panel->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
|
||||
atlas_merging_h_split_container->add_child(atlas_merging_right_panel);
|
||||
|
||||
// Settings.
|
||||
Label *settings_label = memnew(Label);
|
||||
settings_label->set_text(TTR("Settings:"));
|
||||
atlas_merging_right_panel->add_child(settings_label);
|
||||
|
||||
columns_editor_property = memnew(EditorPropertyInteger);
|
||||
columns_editor_property->set_label(TTR("Next Line After Column"));
|
||||
columns_editor_property->set_object_and_property(this, "next_line_after_column");
|
||||
columns_editor_property->update_property();
|
||||
columns_editor_property->connect("property_changed", callable_mp(this, &AtlasMergingDialog::_property_changed));
|
||||
atlas_merging_right_panel->add_child(columns_editor_property);
|
||||
|
||||
// Preview.
|
||||
Label *preview_label = memnew(Label);
|
||||
preview_label->set_text(TTR("Preview:"));
|
||||
atlas_merging_right_panel->add_child(preview_label);
|
||||
|
||||
preview = memnew(TextureRect);
|
||||
preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||
preview->hide();
|
||||
preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
||||
atlas_merging_right_panel->add_child(preview);
|
||||
|
||||
select_2_atlases_label = memnew(Label);
|
||||
select_2_atlases_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
select_2_atlases_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
select_2_atlases_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
select_2_atlases_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
select_2_atlases_label->set_text(TTR("Please select two atlases or more."));
|
||||
atlas_merging_right_panel->add_child(select_2_atlases_label);
|
||||
|
||||
// The file dialog to choose the texture path.
|
||||
editor_file_dialog = memnew(EditorFileDialog);
|
||||
editor_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
|
||||
editor_file_dialog->add_filter("*.png");
|
||||
editor_file_dialog->connect("file_selected", callable_mp(this, &AtlasMergingDialog::_merge_confirmed));
|
||||
add_child(editor_file_dialog);
|
||||
}
|
87
modules/layered_tile_map/editor/atlas_merging_dialog.h
Normal file
87
modules/layered_tile_map/editor/atlas_merging_dialog.h
Normal file
@ -0,0 +1,87 @@
|
||||
/**************************************************************************/
|
||||
/* atlas_merging_dialog.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ATLAS_MERGING_DIALOG_H
|
||||
#define ATLAS_MERGING_DIALOG_H
|
||||
|
||||
#include "editor/editor_properties.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class EditorFileDialog;
|
||||
class EditorPropertyVector2i;
|
||||
|
||||
class AtlasMergingDialog : public ConfirmationDialog {
|
||||
GDCLASS(AtlasMergingDialog, ConfirmationDialog);
|
||||
|
||||
private:
|
||||
int commited_actions_count = 0;
|
||||
bool delete_original_atlases = true;
|
||||
Ref<TileSetAtlasSource> merged;
|
||||
LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping;
|
||||
Ref<TileSet> tile_set;
|
||||
|
||||
// Settings.
|
||||
int next_line_after_column = 30;
|
||||
|
||||
// GUI.
|
||||
ItemList *atlas_merging_atlases_list = nullptr;
|
||||
EditorPropertyVector2i *texture_region_size_editor_property = nullptr;
|
||||
EditorPropertyInteger *columns_editor_property = nullptr;
|
||||
TextureRect *preview = nullptr;
|
||||
Label *select_2_atlases_label = nullptr;
|
||||
EditorFileDialog *editor_file_dialog = nullptr;
|
||||
Button *merge_button = nullptr;
|
||||
|
||||
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
|
||||
|
||||
void _generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns);
|
||||
void _update_texture();
|
||||
void _merge_confirmed(const String &p_path);
|
||||
|
||||
protected:
|
||||
virtual void ok_pressed() override;
|
||||
virtual void cancel_pressed() override;
|
||||
virtual void custom_action(const String &) override;
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void update_tile_set(Ref<TileSet> p_tile_set);
|
||||
|
||||
AtlasMergingDialog();
|
||||
};
|
||||
|
||||
#endif // ATLAS_MERGING_DIALOG_H
|
735
modules/layered_tile_map/editor/tile_atlas_view.cpp
Normal file
735
modules/layered_tile_map/editor/tile_atlas_view.cpp
Normal file
@ -0,0 +1,735 @@
|
||||
/**************************************************************************/
|
||||
/* tile_atlas_view.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
|
||||
#include "core/input/input.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/panel.h"
|
||||
#include "scene/gui/view_panner.h"
|
||||
|
||||
void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (panner->gui_input(p_event)) {
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
|
||||
panning += p_scroll_vec;
|
||||
_update_zoom_and_panning(true);
|
||||
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
|
||||
}
|
||||
|
||||
void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
|
||||
zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
|
||||
_update_zoom_and_panning(true);
|
||||
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
|
||||
}
|
||||
|
||||
Size2i TileAtlasView::_compute_base_tiles_control_size() {
|
||||
// Update the texture.
|
||||
Vector2i size;
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (texture.is_valid()) {
|
||||
size = texture->get_size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
|
||||
Vector2i size;
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
||||
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
|
||||
Vector2i line_size;
|
||||
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
|
||||
for (int j = 1; j < alternatives_count; j++) {
|
||||
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
|
||||
bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();
|
||||
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
|
||||
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
|
||||
}
|
||||
size.x = MAX(size.x, line_size.x);
|
||||
size.y += line_size.y;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
|
||||
float zoom = zoom_widget->get_zoom();
|
||||
|
||||
// Compute the minimum sizes.
|
||||
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
|
||||
base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom);
|
||||
|
||||
Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
|
||||
alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom);
|
||||
|
||||
// Set the texture for the base tiles.
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
|
||||
// Set the scales.
|
||||
if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
|
||||
base_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
|
||||
} else {
|
||||
base_tiles_drawing_root->set_scale(Vector2(1, 1));
|
||||
}
|
||||
if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
|
||||
alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
|
||||
} else {
|
||||
alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
|
||||
}
|
||||
|
||||
// Update the margin container's margins.
|
||||
const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
|
||||
for (int i = 0; i < 4; i++) {
|
||||
margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom);
|
||||
}
|
||||
|
||||
// Update the backgrounds.
|
||||
background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
|
||||
background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
|
||||
|
||||
// Zoom on the position.
|
||||
if (p_zoom_on_mouse_pos) {
|
||||
// Offset the panning relative to the center of panel.
|
||||
Vector2 relative_mpos = get_local_mouse_position() - get_size() / 2;
|
||||
panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;
|
||||
} else {
|
||||
// Center of panel.
|
||||
panning = panning * zoom / previous_zoom;
|
||||
}
|
||||
button_center_view->set_disabled(panning.is_zero_approx());
|
||||
|
||||
previous_zoom = zoom;
|
||||
|
||||
center_container->set_begin(panning - center_container->get_minimum_size() / 2);
|
||||
center_container->set_size(center_container->get_minimum_size());
|
||||
}
|
||||
|
||||
void TileAtlasView::_zoom_widget_changed() {
|
||||
_update_zoom_and_panning();
|
||||
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
|
||||
}
|
||||
|
||||
void TileAtlasView::_center_view() {
|
||||
panning = Vector2();
|
||||
button_center_view->set_disabled(true);
|
||||
_update_zoom_and_panning();
|
||||
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
|
||||
}
|
||||
|
||||
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
|
||||
base_tiles_root_control->set_tooltip_text("");
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
|
||||
Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
|
||||
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
|
||||
coords = tile_set_atlas_source->get_tile_at_coords(coords);
|
||||
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
|
||||
base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_base_tiles() {
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (texture.is_valid()) {
|
||||
Vector2i margins = tile_set_atlas_source->get_margins();
|
||||
Vector2i separation = tile_set_atlas_source->get_separation();
|
||||
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
|
||||
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
|
||||
|
||||
// Draw the texture where there is no tile.
|
||||
for (int x = 0; x < grid_size.x; x++) {
|
||||
for (int y = 0; y < grid_size.y; y++) {
|
||||
Vector2i coords = Vector2i(x, y);
|
||||
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
|
||||
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
|
||||
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
|
||||
if (rect.size.x > 0 && rect.size.y > 0) {
|
||||
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw dark overlay after for performance reasons.
|
||||
for (int x = 0; x < grid_size.x; x++) {
|
||||
for (int y = 0; y < grid_size.y; y++) {
|
||||
Vector2i coords = Vector2i(x, y);
|
||||
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
|
||||
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
|
||||
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
|
||||
if (rect.size.x > 0 && rect.size.y > 0) {
|
||||
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the texture around the grid.
|
||||
Rect2i rect;
|
||||
|
||||
// Top.
|
||||
rect.position = Vector2i();
|
||||
rect.set_end(Vector2i(texture->get_size().x, margins.y));
|
||||
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
|
||||
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
|
||||
// Bottom
|
||||
int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
|
||||
if (bottom_border < texture->get_size().y) {
|
||||
rect.position = Vector2i(0, bottom_border);
|
||||
rect.set_end(texture->get_size());
|
||||
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
|
||||
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
}
|
||||
|
||||
// Left
|
||||
rect.position = Vector2i(0, margins.y);
|
||||
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
|
||||
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
|
||||
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
|
||||
// Right.
|
||||
int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
|
||||
if (right_border < texture->get_size().x) {
|
||||
rect.position = Vector2i(right_border, margins.y);
|
||||
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
|
||||
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
|
||||
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
}
|
||||
|
||||
// Draw actual tiles, using their properties (modulation, etc...)
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
|
||||
|
||||
// Different materials need to be drawn with different CanvasItems.
|
||||
RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);
|
||||
|
||||
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
|
||||
// Update the y to max value.
|
||||
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
|
||||
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
|
||||
|
||||
// Draw the tile.
|
||||
TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Dark overlay on separation in its own pass.
|
||||
if (separation.x > 0 || separation.y > 0) {
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
|
||||
|
||||
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
|
||||
// Update the y to max value.
|
||||
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
|
||||
|
||||
if (separation.x > 0) {
|
||||
Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
|
||||
right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
|
||||
if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
|
||||
//base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
|
||||
base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
if (separation.y > 0) {
|
||||
Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
|
||||
bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
|
||||
if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
|
||||
//base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
|
||||
base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {
|
||||
Ref<Material> mat = p_for_data->get_material();
|
||||
if (mat.is_null()) {
|
||||
return p_base_item->get_canvas_item();
|
||||
} else if (p_material_map.has(mat)) {
|
||||
return p_material_map[mat];
|
||||
} else {
|
||||
RID ci_rid = RS::get_singleton()->canvas_item_create();
|
||||
RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());
|
||||
RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());
|
||||
p_material_map[mat] = ci_rid;
|
||||
return ci_rid;
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_clear_material_canvas_items() {
|
||||
for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {
|
||||
RS::get_singleton()->free(kv.value);
|
||||
}
|
||||
material_tiles_draw.clear();
|
||||
|
||||
for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {
|
||||
RS::get_singleton()->free(kv.value);
|
||||
}
|
||||
material_alternatives_draw.clear();
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_base_tiles_texture_grid() {
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (texture.is_valid()) {
|
||||
Vector2i margins = tile_set_atlas_source->get_margins();
|
||||
Vector2i separation = tile_set_atlas_source->get_separation();
|
||||
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
|
||||
|
||||
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
|
||||
|
||||
// Draw each tile texture region.
|
||||
for (int x = 0; x < grid_size.x; x++) {
|
||||
for (int y = 0; y < grid_size.y; y++) {
|
||||
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
|
||||
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
|
||||
if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
|
||||
if (base_tile_coords == Vector2i(x, y)) {
|
||||
// Draw existing tile.
|
||||
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
|
||||
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
|
||||
base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
|
||||
}
|
||||
} else {
|
||||
// Draw the grid.
|
||||
base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_base_tiles_shape_grid() {
|
||||
// Draw the shapes.
|
||||
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
|
||||
Vector2i tile_shape_size = tile_set->get_tile_size();
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
||||
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
|
||||
if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) {
|
||||
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
|
||||
Color color = grid_color;
|
||||
if (frame > 0) {
|
||||
color.a *= 0.3;
|
||||
}
|
||||
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame);
|
||||
Transform2D tile_xform;
|
||||
tile_xform.set_origin(Rect2(texture_region).get_center() + in_tile_base_offset);
|
||||
tile_xform.set_scale(tile_shape_size);
|
||||
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
|
||||
alternative_tiles_root_control->set_tooltip_text("");
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
|
||||
Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
|
||||
Vector2i coords = Vector2i(coords3.x, coords3.y);
|
||||
int alternative_id = coords3.z;
|
||||
if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
|
||||
alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_alternatives() {
|
||||
// Draw the alternative tiles.
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (texture.is_valid()) {
|
||||
Vector2 current_pos;
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
|
||||
current_pos.x = 0;
|
||||
int y_increment = 0;
|
||||
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
|
||||
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
|
||||
for (int j = 1; j < alternatives_count; j++) {
|
||||
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
|
||||
TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
|
||||
bool transposed = tile_data->get_transpose();
|
||||
|
||||
// Different materials need to be drawn with different CanvasItems.
|
||||
RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);
|
||||
|
||||
// Update the y to max value.
|
||||
Vector2i offset_pos;
|
||||
if (transposed) {
|
||||
offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin());
|
||||
y_increment = MAX(y_increment, texture_region_size.x);
|
||||
} else {
|
||||
offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin());
|
||||
y_increment = MAX(y_increment, texture_region_size.y);
|
||||
}
|
||||
|
||||
// Draw the tile.
|
||||
TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
|
||||
|
||||
// Increment the x position.
|
||||
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
|
||||
}
|
||||
if (alternatives_count > 1) {
|
||||
current_pos.y += y_increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_background_left() {
|
||||
background_left->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_left->get_size()), true);
|
||||
}
|
||||
|
||||
void TileAtlasView::_draw_background_right() {
|
||||
background_right->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_right->get_size()), true);
|
||||
}
|
||||
|
||||
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
|
||||
tile_set = p_tile_set;
|
||||
tile_set_atlas_source = p_tile_set_atlas_source;
|
||||
|
||||
_clear_material_canvas_items();
|
||||
|
||||
if (!tile_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(p_source_id < 0);
|
||||
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
|
||||
|
||||
source_id = p_source_id;
|
||||
|
||||
// Show or hide the view.
|
||||
bool valid = tile_set_atlas_source->get_texture().is_valid();
|
||||
hbox->set_visible(valid);
|
||||
missing_source_label->set_visible(!valid);
|
||||
|
||||
// Update the rect cache.
|
||||
_update_alternative_tiles_rect_cache();
|
||||
|
||||
// Update everything.
|
||||
_update_zoom_and_panning();
|
||||
|
||||
base_tiles_drawing_root->set_size(_compute_base_tiles_control_size());
|
||||
alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size());
|
||||
|
||||
// Update.
|
||||
base_tiles_draw->queue_redraw();
|
||||
base_tiles_texture_grid->queue_redraw();
|
||||
base_tiles_shape_grid->queue_redraw();
|
||||
alternatives_draw->queue_redraw();
|
||||
background_left->queue_redraw();
|
||||
background_right->queue_redraw();
|
||||
}
|
||||
|
||||
float TileAtlasView::get_zoom() const {
|
||||
return zoom_widget->get_zoom();
|
||||
};
|
||||
|
||||
void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) {
|
||||
zoom_widget->set_zoom(p_zoom);
|
||||
panning = p_panning;
|
||||
_update_zoom_and_panning();
|
||||
};
|
||||
|
||||
void TileAtlasView::set_padding(Side p_side, int p_padding) {
|
||||
ERR_FAIL_COND(p_padding < 0);
|
||||
margin_container_paddings[p_side] = p_padding;
|
||||
}
|
||||
|
||||
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
|
||||
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
|
||||
if (!texture.is_valid()) {
|
||||
return TileSetSource::INVALID_ATLAS_COORDS;
|
||||
}
|
||||
|
||||
Vector2i margins = tile_set_atlas_source->get_margins();
|
||||
Vector2i separation = tile_set_atlas_source->get_separation();
|
||||
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
|
||||
|
||||
// Compute index in atlas.
|
||||
Vector2 pos = p_pos - margins;
|
||||
Vector2i ret = (pos / (texture_region_size + separation)).floor();
|
||||
|
||||
// Clamp.
|
||||
if (p_clamp) {
|
||||
Vector2i size = tile_set_atlas_source->get_atlas_grid_size();
|
||||
ret.x = CLAMP(ret.x, 0, size.x - 1);
|
||||
ret.y = CLAMP(ret.y, 0, size.y - 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TileAtlasView::_update_alternative_tiles_rect_cache() {
|
||||
alternative_tiles_rect_cache.clear();
|
||||
|
||||
Rect2i current;
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
||||
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
|
||||
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
|
||||
int line_height = 0;
|
||||
for (int j = 1; j < alternatives_count; j++) {
|
||||
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
|
||||
TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);
|
||||
bool transposed = tile_data->get_transpose();
|
||||
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
|
||||
|
||||
// Update the rect.
|
||||
if (!alternative_tiles_rect_cache.has(tile_id)) {
|
||||
alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>();
|
||||
}
|
||||
alternative_tiles_rect_cache[tile_id][alternative_id] = current;
|
||||
|
||||
current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
|
||||
line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
|
||||
}
|
||||
|
||||
current.position.x = 0;
|
||||
current.position.y += line_height;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
|
||||
for (const KeyValue<Vector2, HashMap<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
|
||||
for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
|
||||
if (E_alternative.value.has_point(p_pos)) {
|
||||
return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
|
||||
}
|
||||
|
||||
Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
|
||||
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
|
||||
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache[p_coords].has(p_alternative_tile), Rect2i(), vformat("No cached rect for tile coords:%s alternative_id:%d", p_coords, p_alternative_tile));
|
||||
|
||||
return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
|
||||
}
|
||||
|
||||
void TileAtlasView::queue_redraw() {
|
||||
base_tiles_draw->queue_redraw();
|
||||
base_tiles_texture_grid->queue_redraw();
|
||||
base_tiles_shape_grid->queue_redraw();
|
||||
alternatives_draw->queue_redraw();
|
||||
background_left->queue_redraw();
|
||||
background_right->queue_redraw();
|
||||
}
|
||||
|
||||
void TileAtlasView::_update_theme_item_cache() {
|
||||
Control::_update_theme_item_cache();
|
||||
|
||||
theme_cache.center_view_icon = get_editor_theme_icon(SNAME("CenterView"));
|
||||
theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
|
||||
}
|
||||
|
||||
void TileAtlasView::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
button_center_view->set_icon(theme_cache.center_view_icon);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void TileAtlasView::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
|
||||
}
|
||||
|
||||
TileAtlasView::TileAtlasView() {
|
||||
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
|
||||
|
||||
Panel *panel = memnew(Panel);
|
||||
panel->set_clip_contents(true);
|
||||
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
panel->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
panel->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(panel);
|
||||
|
||||
zoom_widget = memnew(EditorZoomWidget);
|
||||
add_child(zoom_widget);
|
||||
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
|
||||
zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
|
||||
zoom_widget->set_shortcut_context(this);
|
||||
|
||||
button_center_view = memnew(Button);
|
||||
button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);
|
||||
button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);
|
||||
button_center_view->connect("pressed", callable_mp(this, &TileAtlasView::_center_view));
|
||||
button_center_view->set_flat(true);
|
||||
button_center_view->set_disabled(true);
|
||||
button_center_view->set_tooltip_text(TTR("Center View"));
|
||||
add_child(button_center_view);
|
||||
|
||||
panner.instantiate();
|
||||
panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
|
||||
panner->set_enable_rmb(true);
|
||||
|
||||
center_container = memnew(CenterContainer);
|
||||
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
center_container->set_anchors_preset(Control::PRESET_CENTER);
|
||||
center_container->connect("gui_input", callable_mp(this, &TileAtlasView::gui_input));
|
||||
center_container->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
|
||||
center_container->set_focus_mode(FOCUS_CLICK);
|
||||
panel->add_child(center_container);
|
||||
|
||||
missing_source_label = memnew(Label);
|
||||
missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));
|
||||
center_container->add_child(missing_source_label);
|
||||
|
||||
margin_container = memnew(MarginContainer);
|
||||
margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
center_container->add_child(margin_container);
|
||||
|
||||
hbox = memnew(HBoxContainer);
|
||||
hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
hbox->add_theme_constant_override("separation", 10);
|
||||
hbox->hide();
|
||||
margin_container->add_child(hbox);
|
||||
|
||||
VBoxContainer *left_vbox = memnew(VBoxContainer);
|
||||
left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
hbox->add_child(left_vbox);
|
||||
|
||||
VBoxContainer *right_vbox = memnew(VBoxContainer);
|
||||
right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
hbox->add_child(right_vbox);
|
||||
|
||||
// Base tiles.
|
||||
Label *base_tile_label = memnew(Label);
|
||||
base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
base_tile_label->set_text(TTR("Base Tiles"));
|
||||
base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
left_vbox->add_child(base_tile_label);
|
||||
|
||||
base_tiles_root_control = memnew(Control);
|
||||
base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
|
||||
left_vbox->add_child(base_tiles_root_control);
|
||||
|
||||
background_left = memnew(Control);
|
||||
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
|
||||
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
|
||||
background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
|
||||
base_tiles_root_control->add_child(background_left);
|
||||
|
||||
base_tiles_drawing_root = memnew(Control);
|
||||
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
|
||||
base_tiles_root_control->add_child(base_tiles_drawing_root);
|
||||
|
||||
base_tiles_draw = memnew(Control);
|
||||
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles));
|
||||
base_tiles_drawing_root->add_child(base_tiles_draw);
|
||||
|
||||
base_tiles_texture_grid = memnew(Control);
|
||||
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
|
||||
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
|
||||
|
||||
base_tiles_shape_grid = memnew(Control);
|
||||
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
|
||||
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
|
||||
|
||||
// Alternative tiles.
|
||||
Label *alternative_tiles_label = memnew(Label);
|
||||
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
alternative_tiles_label->set_text(TTR("Alternative Tiles"));
|
||||
alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
right_vbox->add_child(alternative_tiles_label);
|
||||
|
||||
alternative_tiles_root_control = memnew(Control);
|
||||
alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
|
||||
right_vbox->add_child(alternative_tiles_root_control);
|
||||
|
||||
background_right = memnew(Control);
|
||||
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
|
||||
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
|
||||
background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right));
|
||||
alternative_tiles_root_control->add_child(background_right);
|
||||
|
||||
alternative_tiles_drawing_root = memnew(Control);
|
||||
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
|
||||
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
|
||||
|
||||
alternatives_draw = memnew(Control);
|
||||
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
|
||||
alternative_tiles_drawing_root->add_child(alternatives_draw);
|
||||
}
|
||||
|
||||
TileAtlasView::~TileAtlasView() {
|
||||
_clear_material_canvas_items();
|
||||
}
|
174
modules/layered_tile_map/editor/tile_atlas_view.h
Normal file
174
modules/layered_tile_map/editor/tile_atlas_view.h
Normal file
@ -0,0 +1,174 @@
|
||||
/**************************************************************************/
|
||||
/* tile_atlas_view.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_ATLAS_VIEW_H
|
||||
#define TILE_ATLAS_VIEW_H
|
||||
|
||||
#include "editor/gui/editor_zoom_widget.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/center_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class ViewPanner;
|
||||
|
||||
class TileAtlasView : public Control {
|
||||
GDCLASS(TileAtlasView, Control);
|
||||
|
||||
private:
|
||||
TileSet *tile_set = nullptr;
|
||||
TileSetAtlasSource *tile_set_atlas_source = nullptr;
|
||||
int source_id = TileSet::INVALID_SOURCE;
|
||||
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE,
|
||||
DRAG_TYPE_PAN,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
float previous_zoom = 1.0;
|
||||
EditorZoomWidget *zoom_widget = nullptr;
|
||||
Button *button_center_view = nullptr;
|
||||
CenterContainer *center_container = nullptr;
|
||||
Vector2 panning;
|
||||
void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false);
|
||||
void _zoom_widget_changed();
|
||||
void _center_view();
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
Ref<ViewPanner> panner;
|
||||
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
|
||||
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
|
||||
|
||||
HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache;
|
||||
void _update_alternative_tiles_rect_cache();
|
||||
|
||||
MarginContainer *margin_container = nullptr;
|
||||
int margin_container_paddings[4] = { 0, 0, 0, 0 };
|
||||
HBoxContainer *hbox = nullptr;
|
||||
Label *missing_source_label = nullptr;
|
||||
|
||||
// Background
|
||||
Control *background_left = nullptr;
|
||||
void _draw_background_left();
|
||||
Control *background_right = nullptr;
|
||||
void _draw_background_right();
|
||||
|
||||
// Left side.
|
||||
Control *base_tiles_root_control = nullptr;
|
||||
void _base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
Control *base_tiles_drawing_root = nullptr;
|
||||
|
||||
Control *base_tiles_draw = nullptr;
|
||||
HashMap<Ref<Material>, RID> material_tiles_draw;
|
||||
HashMap<Ref<Material>, RID> material_alternatives_draw;
|
||||
void _draw_base_tiles();
|
||||
RID _get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map);
|
||||
void _clear_material_canvas_items();
|
||||
|
||||
Control *base_tiles_texture_grid = nullptr;
|
||||
void _draw_base_tiles_texture_grid();
|
||||
|
||||
Control *base_tiles_shape_grid = nullptr;
|
||||
void _draw_base_tiles_shape_grid();
|
||||
|
||||
Size2i _compute_base_tiles_control_size();
|
||||
|
||||
// Right side.
|
||||
Control *alternative_tiles_root_control = nullptr;
|
||||
void _alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
Control *alternative_tiles_drawing_root = nullptr;
|
||||
|
||||
Control *alternatives_draw = nullptr;
|
||||
void _draw_alternatives();
|
||||
|
||||
Size2i _compute_alternative_tiles_control_size();
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<Texture2D> center_view_icon;
|
||||
Ref<Texture2D> checkerboard;
|
||||
} theme_cache;
|
||||
|
||||
protected:
|
||||
virtual void _update_theme_item_cache() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// Global.
|
||||
void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
|
||||
|
||||
float get_zoom() const;
|
||||
void set_transform(float p_zoom, Vector2i p_panning);
|
||||
|
||||
void set_padding(Side p_side, int p_padding);
|
||||
|
||||
// Left side.
|
||||
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
|
||||
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
|
||||
|
||||
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const;
|
||||
|
||||
void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
|
||||
if (scaled) {
|
||||
base_tiles_drawing_root->add_child(p_control);
|
||||
} else {
|
||||
base_tiles_root_control->add_child(p_control);
|
||||
}
|
||||
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
};
|
||||
|
||||
// Right side.
|
||||
Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const;
|
||||
Rect2i get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile);
|
||||
|
||||
void add_control_over_alternative_tiles(Control *p_control, bool scaled = true) {
|
||||
if (scaled) {
|
||||
alternative_tiles_drawing_root->add_child(p_control);
|
||||
} else {
|
||||
alternative_tiles_root_control->add_child(p_control);
|
||||
}
|
||||
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
};
|
||||
|
||||
// Redraw everything.
|
||||
void queue_redraw();
|
||||
|
||||
TileAtlasView();
|
||||
~TileAtlasView();
|
||||
};
|
||||
|
||||
#endif // TILE_ATLAS_VIEW_H
|
2958
modules/layered_tile_map/editor/tile_data_editors.cpp
Normal file
2958
modules/layered_tile_map/editor/tile_data_editors.cpp
Normal file
File diff suppressed because it is too large
Load Diff
423
modules/layered_tile_map/editor/tile_data_editors.h
Normal file
423
modules/layered_tile_map/editor/tile_data_editors.h
Normal file
@ -0,0 +1,423 @@
|
||||
/**************************************************************************/
|
||||
/* tile_data_editors.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_DATA_EDITORS_H
|
||||
#define TILE_DATA_EDITORS_H
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
|
||||
#include "editor/editor_properties.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
|
||||
class Label;
|
||||
class MenuButton;
|
||||
class SpinBox;
|
||||
class EditorUndoRedoManager;
|
||||
|
||||
class TileDataEditor : public VBoxContainer {
|
||||
GDCLASS(TileDataEditor, VBoxContainer);
|
||||
|
||||
private:
|
||||
bool _tile_set_changed_update_needed = false;
|
||||
void _tile_set_changed_plan_update();
|
||||
void _tile_set_changed_deferred_update();
|
||||
|
||||
protected:
|
||||
Ref<TileSet> tile_set;
|
||||
TileData *_get_tile_data(TileMapCell p_cell);
|
||||
virtual void _tile_set_changed(){};
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_tile_set(Ref<TileSet> p_tile_set);
|
||||
|
||||
// Input to handle painting.
|
||||
virtual Control *get_toolbar() { return nullptr; };
|
||||
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){};
|
||||
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){};
|
||||
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){};
|
||||
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){};
|
||||
|
||||
// Used to draw the tile data property value over a tile.
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false){};
|
||||
};
|
||||
|
||||
class DummyObject : public Object {
|
||||
GDCLASS(DummyObject, Object)
|
||||
private:
|
||||
HashMap<String, Variant> properties;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
||||
public:
|
||||
bool has_dummy_property(const StringName &p_name);
|
||||
void add_dummy_property(const StringName &p_name);
|
||||
void remove_dummy_property(const StringName &p_name);
|
||||
void clear_dummy_properties();
|
||||
};
|
||||
|
||||
class GenericTilePolygonEditor : public VBoxContainer {
|
||||
GDCLASS(GenericTilePolygonEditor, VBoxContainer);
|
||||
|
||||
private:
|
||||
Ref<TileSet> tile_set;
|
||||
LocalVector<Vector<Point2>> polygons;
|
||||
bool multiple_polygon_mode = false;
|
||||
|
||||
bool use_undo_redo = true;
|
||||
|
||||
// UI
|
||||
int hovered_polygon_index = -1;
|
||||
int hovered_point_index = -1;
|
||||
int hovered_segment_index = -1;
|
||||
Vector2 hovered_segment_point;
|
||||
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE,
|
||||
DRAG_TYPE_DRAG_POINT,
|
||||
DRAG_TYPE_CREATE_POINT,
|
||||
DRAG_TYPE_PAN,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
int drag_polygon_index = 0;
|
||||
int drag_point_index = 0;
|
||||
Vector2 drag_last_pos;
|
||||
PackedVector2Array drag_old_polygon;
|
||||
|
||||
HBoxContainer *toolbar = nullptr;
|
||||
Ref<ButtonGroup> tools_button_group;
|
||||
Button *button_expand = nullptr;
|
||||
Button *button_create = nullptr;
|
||||
Button *button_edit = nullptr;
|
||||
Button *button_delete = nullptr;
|
||||
MenuButton *button_advanced_menu = nullptr;
|
||||
|
||||
enum Snap {
|
||||
SNAP_NONE,
|
||||
SNAP_HALF_PIXEL,
|
||||
SNAP_GRID,
|
||||
};
|
||||
int current_snap_option = SNAP_HALF_PIXEL;
|
||||
MenuButton *button_pixel_snap = nullptr;
|
||||
SpinBox *snap_subdivision = nullptr;
|
||||
|
||||
Vector<Point2> in_creation_polygon;
|
||||
|
||||
Panel *panel = nullptr;
|
||||
Control *base_control = nullptr;
|
||||
EditorZoomWidget *editor_zoom_widget = nullptr;
|
||||
Button *button_center_view = nullptr;
|
||||
Vector2 panning;
|
||||
|
||||
Ref<Texture2D> background_texture;
|
||||
Rect2 background_region;
|
||||
Vector2 background_offset;
|
||||
bool background_h_flip = false;
|
||||
bool background_v_flip = false;
|
||||
bool background_transpose = false;
|
||||
Color background_modulate;
|
||||
|
||||
Color polygon_color = Color(1.0, 0.0, 0.0);
|
||||
|
||||
enum AdvancedMenuOption {
|
||||
RESET_TO_DEFAULT_TILE,
|
||||
CLEAR_TILE,
|
||||
ROTATE_RIGHT,
|
||||
ROTATE_LEFT,
|
||||
FLIP_HORIZONTALLY,
|
||||
FLIP_VERTICALLY,
|
||||
};
|
||||
|
||||
void _base_control_draw();
|
||||
void _zoom_changed();
|
||||
void _advanced_menu_item_pressed(int p_item_pressed);
|
||||
void _center_view();
|
||||
void _base_control_gui_input(Ref<InputEvent> p_event);
|
||||
void _set_snap_option(int p_index);
|
||||
void _store_snap_options();
|
||||
void _toggle_expand(bool p_expand);
|
||||
|
||||
void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist);
|
||||
void _snap_point(Point2 &r_point);
|
||||
void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index);
|
||||
void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_use_undo_redo(bool p_use_undo_redo);
|
||||
|
||||
void set_tile_set(Ref<TileSet> p_tile_set);
|
||||
void set_background(Ref<Texture2D> p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0));
|
||||
|
||||
int get_polygon_count();
|
||||
int add_polygon(const Vector<Point2> &p_polygon, int p_index = -1);
|
||||
void remove_polygon(int p_index);
|
||||
void clear_polygons();
|
||||
void set_polygon(int p_polygon_index, const Vector<Point2> &p_polygon);
|
||||
Vector<Point2> get_polygon(int p_polygon_index);
|
||||
|
||||
void set_polygons_color(Color p_color);
|
||||
void set_multiple_polygon_mode(bool p_multiple_polygon_mode);
|
||||
|
||||
GenericTilePolygonEditor();
|
||||
};
|
||||
|
||||
class TileDataDefaultEditor : public TileDataEditor {
|
||||
GDCLASS(TileDataDefaultEditor, TileDataEditor);
|
||||
|
||||
private:
|
||||
// Toolbar
|
||||
HBoxContainer *toolbar = memnew(HBoxContainer);
|
||||
Button *picker_button = nullptr;
|
||||
|
||||
// UI
|
||||
Ref<Texture2D> tile_bool_checked;
|
||||
Ref<Texture2D> tile_bool_unchecked;
|
||||
Label *label = nullptr;
|
||||
|
||||
EditorProperty *property_editor = nullptr;
|
||||
|
||||
// Painting state.
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE = 0,
|
||||
DRAG_TYPE_PAINT,
|
||||
DRAG_TYPE_PAINT_RECT,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
Vector2 drag_start_pos;
|
||||
Vector2 drag_last_pos;
|
||||
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
|
||||
Variant drag_painted_value;
|
||||
|
||||
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
|
||||
|
||||
protected:
|
||||
DummyObject *dummy_object = memnew(DummyObject);
|
||||
|
||||
StringName type;
|
||||
String property;
|
||||
Variant::Type property_type;
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual Variant _get_painted_value();
|
||||
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
|
||||
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value);
|
||||
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
|
||||
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value);
|
||||
|
||||
public:
|
||||
virtual Control *get_toolbar() override { return toolbar; };
|
||||
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
|
||||
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
|
||||
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
|
||||
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
|
||||
void setup_property_editor(Variant::Type p_type, const String &p_property, const String &p_label = "", const Variant &p_default_value = Variant());
|
||||
Variant::Type get_property_type();
|
||||
|
||||
TileDataDefaultEditor();
|
||||
~TileDataDefaultEditor();
|
||||
};
|
||||
|
||||
class TileDataTextureOriginEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataTextureOriginEditor, TileDataDefaultEditor);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
};
|
||||
|
||||
class TileDataPositionEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataPositionEditor, TileDataDefaultEditor);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
};
|
||||
|
||||
class TileDataYSortEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataYSortEditor, TileDataDefaultEditor);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
};
|
||||
|
||||
class TileDataOcclusionShapeEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataOcclusionShapeEditor, TileDataDefaultEditor);
|
||||
|
||||
private:
|
||||
int occlusion_layer = -1;
|
||||
|
||||
// UI
|
||||
GenericTilePolygonEditor *polygon_editor = nullptr;
|
||||
|
||||
void _polygon_changed(const PackedVector2Array &p_polygon);
|
||||
|
||||
virtual Variant _get_painted_value() override;
|
||||
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
|
||||
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
|
||||
|
||||
protected:
|
||||
virtual void _tile_set_changed() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
|
||||
void set_occlusion_layer(int p_occlusion_layer) { occlusion_layer = p_occlusion_layer; }
|
||||
|
||||
TileDataOcclusionShapeEditor();
|
||||
};
|
||||
|
||||
class TileDataCollisionEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataCollisionEditor, TileDataDefaultEditor);
|
||||
|
||||
int physics_layer = -1;
|
||||
|
||||
// UI
|
||||
GenericTilePolygonEditor *polygon_editor = nullptr;
|
||||
DummyObject *dummy_object = memnew(DummyObject);
|
||||
HashMap<StringName, EditorProperty *> property_editors;
|
||||
|
||||
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
|
||||
void _property_selected(const StringName &p_path, int p_focusable);
|
||||
void _polygons_changed();
|
||||
|
||||
virtual Variant _get_painted_value() override;
|
||||
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
|
||||
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
|
||||
|
||||
protected:
|
||||
virtual void _tile_set_changed() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
|
||||
void set_physics_layer(int p_physics_layer) { physics_layer = p_physics_layer; }
|
||||
|
||||
TileDataCollisionEditor();
|
||||
~TileDataCollisionEditor();
|
||||
};
|
||||
|
||||
class TileDataTerrainsEditor : public TileDataEditor {
|
||||
GDCLASS(TileDataTerrainsEditor, TileDataEditor);
|
||||
|
||||
private:
|
||||
// Toolbar
|
||||
HBoxContainer *toolbar = memnew(HBoxContainer);
|
||||
Button *picker_button = nullptr;
|
||||
|
||||
// Painting state.
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE = 0,
|
||||
DRAG_TYPE_PAINT_TERRAIN_SET,
|
||||
DRAG_TYPE_PAINT_TERRAIN_SET_RECT,
|
||||
DRAG_TYPE_PAINT_TERRAIN_BITS,
|
||||
DRAG_TYPE_PAINT_TERRAIN_BITS_RECT,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
Vector2 drag_start_pos;
|
||||
Vector2 drag_last_pos;
|
||||
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
|
||||
Variant drag_painted_value;
|
||||
|
||||
// UI
|
||||
Label *label = nullptr;
|
||||
DummyObject *dummy_object = memnew(DummyObject);
|
||||
EditorPropertyEnum *terrain_set_property_editor = nullptr;
|
||||
EditorPropertyEnum *terrain_property_editor = nullptr;
|
||||
|
||||
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
|
||||
|
||||
void _update_terrain_selector();
|
||||
|
||||
protected:
|
||||
virtual void _tile_set_changed() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual Control *get_toolbar() override { return toolbar; };
|
||||
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
|
||||
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
|
||||
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
|
||||
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
|
||||
TileDataTerrainsEditor();
|
||||
~TileDataTerrainsEditor();
|
||||
};
|
||||
|
||||
class TileDataNavigationEditor : public TileDataDefaultEditor {
|
||||
GDCLASS(TileDataNavigationEditor, TileDataDefaultEditor);
|
||||
|
||||
private:
|
||||
int navigation_layer = -1;
|
||||
PackedVector2Array navigation_polygon;
|
||||
|
||||
// UI
|
||||
GenericTilePolygonEditor *polygon_editor = nullptr;
|
||||
|
||||
void _polygon_changed(const PackedVector2Array &p_polygon);
|
||||
|
||||
virtual Variant _get_painted_value() override;
|
||||
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
|
||||
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
|
||||
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
|
||||
|
||||
protected:
|
||||
virtual void _tile_set_changed() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
|
||||
|
||||
void set_navigation_layer(int p_navigation_layer) { navigation_layer = p_navigation_layer; }
|
||||
|
||||
TileDataNavigationEditor();
|
||||
};
|
||||
|
||||
#endif // TILE_DATA_EDITORS_H
|
4272
modules/layered_tile_map/editor/tile_map_layer_editor.cpp
Normal file
4272
modules/layered_tile_map/editor/tile_map_layer_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
406
modules/layered_tile_map/editor/tile_map_layer_editor.h
Normal file
406
modules/layered_tile_map/editor/tile_map_layer_editor.h
Normal file
@ -0,0 +1,406 @@
|
||||
/**************************************************************************/
|
||||
/* tile_map_layer_editor.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_MAP_LAYER_EDITOR_H
|
||||
#define TILE_MAP_LAYER_EDITOR_H
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/flow_container.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_bar.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class TileMapLayerEditor;
|
||||
|
||||
class TileMapLayerSubEditorPlugin : public Object {
|
||||
protected:
|
||||
ObjectID edited_tile_map_layer_id;
|
||||
TileMapLayer *_get_edited_layer() const;
|
||||
|
||||
public:
|
||||
struct TabData {
|
||||
Control *toolbar = nullptr;
|
||||
Control *panel = nullptr;
|
||||
};
|
||||
|
||||
virtual Vector<TabData> get_tabs() const {
|
||||
return Vector<TabData>();
|
||||
};
|
||||
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
|
||||
virtual void tile_set_changed(){};
|
||||
virtual void edit(ObjectID p_tile_map_layer_id){};
|
||||
};
|
||||
|
||||
class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
|
||||
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
|
||||
|
||||
public:
|
||||
enum {
|
||||
TRANSFORM_ROTATE_LEFT,
|
||||
TRANSFORM_ROTATE_RIGHT,
|
||||
TRANSFORM_FLIP_H,
|
||||
TRANSFORM_FLIP_V,
|
||||
};
|
||||
|
||||
private:
|
||||
///// Toolbar /////
|
||||
HBoxContainer *toolbar = nullptr;
|
||||
|
||||
Ref<ButtonGroup> tool_buttons_group;
|
||||
Button *select_tool_button = nullptr;
|
||||
Button *paint_tool_button = nullptr;
|
||||
Button *line_tool_button = nullptr;
|
||||
Button *rect_tool_button = nullptr;
|
||||
Button *bucket_tool_button = nullptr;
|
||||
|
||||
HBoxContainer *tools_settings = nullptr;
|
||||
|
||||
VSeparator *tools_settings_vsep = nullptr;
|
||||
Button *picker_button = nullptr;
|
||||
Button *erase_button = nullptr;
|
||||
|
||||
HBoxContainer *transform_toolbar = nullptr;
|
||||
Button *transform_button_rotate_left = nullptr;
|
||||
Button *transform_button_rotate_right = nullptr;
|
||||
Button *transform_button_flip_h = nullptr;
|
||||
Button *transform_button_flip_v = nullptr;
|
||||
|
||||
VSeparator *tools_settings_vsep_2 = nullptr;
|
||||
CheckBox *bucket_contiguous_checkbox = nullptr;
|
||||
Button *random_tile_toggle = nullptr;
|
||||
|
||||
HBoxContainer *scatter_controls_container = nullptr;
|
||||
float scattering = 0.0;
|
||||
Label *scatter_label = nullptr;
|
||||
SpinBox *scatter_spinbox = nullptr;
|
||||
void _on_random_tile_checkbox_toggled(bool p_pressed);
|
||||
void _on_scattering_spinbox_changed(double p_value);
|
||||
|
||||
void _update_toolbar();
|
||||
void _update_transform_buttons();
|
||||
void _set_transform_buttons_state(const Vector<Button *> &p_enabled_buttons, const Vector<Button *> &p_disabled_buttons, const String &p_why_disabled);
|
||||
|
||||
///// Tilemap editing. /////
|
||||
bool has_mouse = false;
|
||||
void _mouse_exited_viewport();
|
||||
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE = 0,
|
||||
DRAG_TYPE_SELECT,
|
||||
DRAG_TYPE_MOVE,
|
||||
DRAG_TYPE_PAINT,
|
||||
DRAG_TYPE_LINE,
|
||||
DRAG_TYPE_RECT,
|
||||
DRAG_TYPE_BUCKET,
|
||||
DRAG_TYPE_PICK,
|
||||
DRAG_TYPE_CLIPBOARD_PASTE,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
bool drag_erasing = false;
|
||||
Vector2 drag_start_mouse_pos;
|
||||
Vector2 drag_last_mouse_pos;
|
||||
HashMap<Vector2i, TileMapCell> drag_modified;
|
||||
|
||||
TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
|
||||
HashMap<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
|
||||
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
||||
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
||||
void _stop_dragging();
|
||||
|
||||
void _apply_transform(int p_type);
|
||||
int _get_transformed_alternative(int p_alternative_id, int p_transform);
|
||||
|
||||
///// Selection system. /////
|
||||
RBSet<Vector2i> tile_map_selection;
|
||||
Ref<TileMapPattern> tile_map_clipboard;
|
||||
Ref<TileMapPattern> selection_pattern;
|
||||
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
|
||||
TypedArray<Vector2i> _get_tile_map_selection() const;
|
||||
|
||||
RBSet<TileMapCell> tile_set_selection;
|
||||
|
||||
void _update_selection_pattern_from_tilemap_selection();
|
||||
void _update_selection_pattern_from_tileset_tiles_selection();
|
||||
void _update_selection_pattern_from_tileset_pattern_selection();
|
||||
void _update_tileset_selection_from_selection_pattern();
|
||||
void _update_fix_selected_and_hovered();
|
||||
void _fix_invalid_tiles_in_tile_map_selection();
|
||||
|
||||
void patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
|
||||
///// Bottom panel common ////
|
||||
void _tab_changed();
|
||||
|
||||
///// Bottom panel tiles ////
|
||||
VBoxContainer *tiles_bottom_panel = nullptr;
|
||||
Label *missing_source_label = nullptr;
|
||||
Label *invalid_source_label = nullptr;
|
||||
|
||||
ItemList *sources_list = nullptr;
|
||||
MenuButton *source_sort_button = nullptr;
|
||||
|
||||
Ref<Texture2D> missing_atlas_texture_icon;
|
||||
void _update_tile_set_sources_list();
|
||||
|
||||
void _update_source_display();
|
||||
|
||||
// Atlas sources.
|
||||
TileMapCell hovered_tile;
|
||||
TileAtlasView *tile_atlas_view = nullptr;
|
||||
HSplitContainer *atlas_sources_split_container = nullptr;
|
||||
|
||||
bool tile_set_dragging_selection = false;
|
||||
Vector2i tile_set_drag_start_mouse_pos;
|
||||
|
||||
Control *tile_atlas_control = nullptr;
|
||||
void _tile_atlas_control_mouse_exited();
|
||||
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _tile_atlas_control_draw();
|
||||
|
||||
Control *alternative_tiles_control = nullptr;
|
||||
void _tile_alternatives_control_draw();
|
||||
void _tile_alternatives_control_mouse_exited();
|
||||
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _update_atlas_view();
|
||||
void _set_source_sort(int p_sort);
|
||||
|
||||
// Scenes collection sources.
|
||||
ItemList *scene_tiles_list = nullptr;
|
||||
|
||||
void _update_scenes_collection_view();
|
||||
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
|
||||
void _scenes_list_multi_selected(int p_index, bool p_selected);
|
||||
void _scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
|
||||
///// Bottom panel patterns ////
|
||||
VBoxContainer *patterns_bottom_panel = nullptr;
|
||||
ItemList *patterns_item_list = nullptr;
|
||||
Label *patterns_help_label = nullptr;
|
||||
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
|
||||
bool select_last_pattern = false;
|
||||
void _update_patterns_list();
|
||||
|
||||
// General
|
||||
void _update_theme();
|
||||
List<BaseButton *> viewport_shortcut_buttons;
|
||||
|
||||
// Update callback
|
||||
virtual void tile_set_changed() override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual Vector<TabData> get_tabs() const override;
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
|
||||
|
||||
virtual void edit(ObjectID p_tile_map_layer_id) override;
|
||||
|
||||
TileMapLayerEditorTilesPlugin();
|
||||
~TileMapLayerEditorTilesPlugin();
|
||||
};
|
||||
|
||||
class TileMapLayerEditorTerrainsPlugin : public TileMapLayerSubEditorPlugin {
|
||||
GDCLASS(TileMapLayerEditorTerrainsPlugin, TileMapLayerSubEditorPlugin);
|
||||
|
||||
private:
|
||||
// Toolbar.
|
||||
HBoxContainer *toolbar = nullptr;
|
||||
|
||||
Ref<ButtonGroup> tool_buttons_group;
|
||||
Button *paint_tool_button = nullptr;
|
||||
Button *line_tool_button = nullptr;
|
||||
Button *rect_tool_button = nullptr;
|
||||
Button *bucket_tool_button = nullptr;
|
||||
|
||||
HBoxContainer *tools_settings = nullptr;
|
||||
|
||||
VSeparator *tools_settings_vsep = nullptr;
|
||||
Button *picker_button = nullptr;
|
||||
Button *erase_button = nullptr;
|
||||
|
||||
VSeparator *tools_settings_vsep_2 = nullptr;
|
||||
CheckBox *bucket_contiguous_checkbox = nullptr;
|
||||
void _update_toolbar();
|
||||
|
||||
// Main vbox.
|
||||
VBoxContainer *main_vbox_container = nullptr;
|
||||
|
||||
// TileMap editing.
|
||||
bool has_mouse = false;
|
||||
void _mouse_exited_viewport();
|
||||
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE = 0,
|
||||
DRAG_TYPE_PAINT,
|
||||
DRAG_TYPE_LINE,
|
||||
DRAG_TYPE_RECT,
|
||||
DRAG_TYPE_BUCKET,
|
||||
DRAG_TYPE_PICK,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
bool drag_erasing = false;
|
||||
Vector2 drag_start_mouse_pos;
|
||||
Vector2 drag_last_mouse_pos;
|
||||
HashMap<Vector2i, TileMapCell> drag_modified;
|
||||
|
||||
// Painting
|
||||
HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
|
||||
HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
|
||||
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
||||
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
||||
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
|
||||
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
||||
void _stop_dragging();
|
||||
|
||||
enum SelectedType {
|
||||
SELECTED_TYPE_CONNECT = 0,
|
||||
SELECTED_TYPE_PATH,
|
||||
SELECTED_TYPE_PATTERN,
|
||||
};
|
||||
SelectedType selected_type;
|
||||
int selected_terrain_set = -1;
|
||||
int selected_terrain = -1;
|
||||
TileSet::TerrainsPattern selected_terrains_pattern;
|
||||
void _update_selection();
|
||||
|
||||
// Bottom panel.
|
||||
Tree *terrains_tree = nullptr;
|
||||
ItemList *terrains_tile_list = nullptr;
|
||||
|
||||
// Cache.
|
||||
LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
|
||||
List<BaseButton *> viewport_shortcut_buttons;
|
||||
|
||||
// Update functions.
|
||||
void _update_terrains_cache();
|
||||
void _update_terrains_tree();
|
||||
void _update_tiles_list();
|
||||
void _update_theme();
|
||||
|
||||
// Update callback
|
||||
virtual void tile_set_changed() override;
|
||||
|
||||
public:
|
||||
virtual Vector<TabData> get_tabs() const override;
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
|
||||
|
||||
virtual void edit(ObjectID p_tile_map_layer_id) override;
|
||||
|
||||
TileMapLayerEditorTerrainsPlugin();
|
||||
~TileMapLayerEditorTerrainsPlugin();
|
||||
};
|
||||
|
||||
class TileMapLayerEditor : public VBoxContainer {
|
||||
GDCLASS(TileMapLayerEditor, VBoxContainer);
|
||||
|
||||
private:
|
||||
bool tileset_changed_needs_update = false;
|
||||
|
||||
ObjectID edited_tile_map_layer_id;
|
||||
TileMapLayer *_get_edited_layer() const;
|
||||
|
||||
// Vector to keep plugins.
|
||||
Vector<TileMapLayerSubEditorPlugin *> tile_map_editor_plugins;
|
||||
|
||||
// Toolbar.
|
||||
HFlowContainer *tile_map_toolbar = nullptr;
|
||||
|
||||
OptionButton *layers_selection_button = nullptr;
|
||||
void _layers_selection_item_selected(int p_index);
|
||||
|
||||
Button *toggle_highlight_selected_layer_button = nullptr;
|
||||
void _highlight_selected_layer_button_toggled(bool p_pressed);
|
||||
|
||||
Button *toggle_grid_button = nullptr;
|
||||
void _on_grid_toggled(bool p_pressed);
|
||||
|
||||
MenuButton *advanced_menu_button = nullptr;
|
||||
void _advanced_menu_button_id_pressed(int p_id);
|
||||
|
||||
// Bottom panel.
|
||||
Label *missing_tileset_label = nullptr;
|
||||
TabBar *tabs_bar = nullptr;
|
||||
LocalVector<TileMapLayerSubEditorPlugin::TabData> tabs_data;
|
||||
LocalVector<TileMapLayerSubEditorPlugin *> tabs_plugins;
|
||||
void _update_bottom_panel();
|
||||
|
||||
// TileMap.
|
||||
Ref<Texture2D> missing_tile_texture;
|
||||
Ref<Texture2D> warning_pattern_texture;
|
||||
|
||||
// CallBack.
|
||||
void _tile_map_layer_changed();
|
||||
void _tab_changed(int p_tab_changed);
|
||||
|
||||
// Updates.
|
||||
void _layers_select_next_or_previous(bool p_next);
|
||||
void _update_highlighting_toggle();
|
||||
|
||||
// Inspector undo/redo callback.
|
||||
void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
|
||||
|
||||
public:
|
||||
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
|
||||
void edit(TileMapLayer *p_tile_map_layer);
|
||||
void update_layers_selector();
|
||||
|
||||
TileMapLayerEditor();
|
||||
~TileMapLayerEditor();
|
||||
|
||||
// Static functions.
|
||||
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
|
||||
};
|
||||
|
||||
#endif // TILE_MAP_LAYER_EDITOR_H
|
494
modules/layered_tile_map/editor/tile_proxies_manager_dialog.cpp
Normal file
494
modules/layered_tile_map/editor/tile_proxies_manager_dialog.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
/**************************************************************************/
|
||||
/* tile_proxies_manager_dialog.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tile_proxies_manager_dialog.h"
|
||||
|
||||
#include "editor/editor_properties_vector.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/separator.h"
|
||||
|
||||
void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list) {
|
||||
if (p_mouse_button_index != MouseButton::RIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemList *item_list = Object::cast_to<ItemList>(p_item_list);
|
||||
popup_menu->reset_size();
|
||||
popup_menu->set_position(get_position() + item_list->get_global_mouse_position());
|
||||
popup_menu->popup();
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_menu_id_pressed(int p_id) {
|
||||
if (p_id == 0) {
|
||||
// Delete.
|
||||
_delete_selected_bindings();
|
||||
}
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_delete_selected_bindings() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Remove Tile Proxies"));
|
||||
|
||||
Vector<int> source_level_selected = source_level_list->get_selected_items();
|
||||
for (int i = 0; i < source_level_selected.size(); i++) {
|
||||
int key = source_level_list->get_item_metadata(source_level_selected[i]);
|
||||
int val = tile_set->get_source_level_tile_proxy(key);
|
||||
undo_redo->add_do_method(*tile_set, "remove_source_level_tile_proxy", key);
|
||||
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", key, val);
|
||||
}
|
||||
|
||||
Vector<int> coords_level_selected = coords_level_list->get_selected_items();
|
||||
for (int i = 0; i < coords_level_selected.size(); i++) {
|
||||
Array key = coords_level_list->get_item_metadata(coords_level_selected[i]);
|
||||
Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]);
|
||||
undo_redo->add_do_method(*tile_set, "remove_coords_level_tile_proxy", key[0], key[1]);
|
||||
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", key[0], key[1], val[0], val[1]);
|
||||
}
|
||||
|
||||
Vector<int> alternative_level_selected = alternative_level_list->get_selected_items();
|
||||
for (int i = 0; i < alternative_level_selected.size(); i++) {
|
||||
Array key = alternative_level_list->get_item_metadata(alternative_level_selected[i]);
|
||||
Array val = tile_set->get_alternative_level_tile_proxy(key[0], key[1], key[2]);
|
||||
undo_redo->add_do_method(*tile_set, "remove_alternative_level_tile_proxy", key[0], key[1], key[2]);
|
||||
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", key[0], key[1], key[2], val[0], val[1], val[2]);
|
||||
}
|
||||
undo_redo->add_do_method(this, "_update_lists");
|
||||
undo_redo->add_undo_method(this, "_update_lists");
|
||||
undo_redo->commit_action();
|
||||
|
||||
commited_actions_count += 1;
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_update_lists() {
|
||||
source_level_list->clear();
|
||||
coords_level_list->clear();
|
||||
alternative_level_list->clear();
|
||||
|
||||
Array proxies = tile_set->get_source_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
String text = vformat("%s", proxy[0]).rpad(5) + "-> " + vformat("%s", proxy[1]);
|
||||
int id = source_level_list->add_item(text);
|
||||
source_level_list->set_item_metadata(id, proxy[0]);
|
||||
}
|
||||
|
||||
proxies = tile_set->get_coords_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
String text = vformat("%s, %s", proxy[0], proxy[1]).rpad(17) + "-> " + vformat("%s, %s", proxy[2], proxy[3]);
|
||||
int id = coords_level_list->add_item(text);
|
||||
coords_level_list->set_item_metadata(id, proxy.slice(0, 2));
|
||||
}
|
||||
|
||||
proxies = tile_set->get_alternative_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
String text = vformat("%s, %s, %s", proxy[0], proxy[1], proxy[2]).rpad(24) + "-> " + vformat("%s, %s, %s", proxy[3], proxy[4], proxy[5]);
|
||||
int id = alternative_level_list->add_item(text);
|
||||
alternative_level_list->set_item_metadata(id, proxy.slice(0, 3));
|
||||
}
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_update_enabled_property_editors() {
|
||||
if (from.source_id == TileSet::INVALID_SOURCE) {
|
||||
from.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
|
||||
to.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
|
||||
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
coords_from_property_editor->hide();
|
||||
coords_to_property_editor->hide();
|
||||
alternative_from_property_editor->hide();
|
||||
alternative_to_property_editor->hide();
|
||||
} else if (from.get_atlas_coords().x == -1 || from.get_atlas_coords().y == -1) {
|
||||
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
coords_from_property_editor->show();
|
||||
coords_to_property_editor->show();
|
||||
alternative_from_property_editor->hide();
|
||||
alternative_to_property_editor->hide();
|
||||
} else {
|
||||
coords_from_property_editor->show();
|
||||
coords_to_property_editor->show();
|
||||
alternative_from_property_editor->show();
|
||||
alternative_to_property_editor->show();
|
||||
}
|
||||
|
||||
source_from_property_editor->update_property();
|
||||
source_to_property_editor->update_property();
|
||||
coords_from_property_editor->update_property();
|
||||
coords_to_property_editor->update_property();
|
||||
alternative_from_property_editor->update_property();
|
||||
alternative_to_property_editor->update_property();
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing) {
|
||||
_set(p_path, p_value);
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_add_button_pressed() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
if (from.source_id != TileSet::INVALID_SOURCE && to.source_id != TileSet::INVALID_SOURCE) {
|
||||
Vector2i from_coords = from.get_atlas_coords();
|
||||
Vector2i to_coords = to.get_atlas_coords();
|
||||
if (from_coords.x >= 0 && from_coords.y >= 0 && to_coords.x >= 0 && to_coords.y >= 0) {
|
||||
if (from.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE && to.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE) {
|
||||
undo_redo->create_action(TTR("Create Alternative-level Tile Proxy"));
|
||||
undo_redo->add_do_method(*tile_set, "set_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile, to.source_id, to.get_atlas_coords(), to.alternative_tile);
|
||||
if (tile_set->has_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile)) {
|
||||
Array a = tile_set->get_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
|
||||
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", to.source_id, to.get_atlas_coords(), to.alternative_tile, a[0], a[1], a[2]);
|
||||
} else {
|
||||
undo_redo->add_undo_method(*tile_set, "remove_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile);
|
||||
}
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Create Coords-level Tile Proxy"));
|
||||
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", from.source_id, from.get_atlas_coords(), to.source_id, to.get_atlas_coords());
|
||||
if (tile_set->has_coords_level_tile_proxy(from.source_id, from.get_atlas_coords())) {
|
||||
Array a = tile_set->get_coords_level_tile_proxy(from.source_id, from.get_atlas_coords());
|
||||
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", to.source_id, to.get_atlas_coords(), a[0], a[1]);
|
||||
} else {
|
||||
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", from.source_id, from.get_atlas_coords());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Create source-level Tile Proxy"));
|
||||
undo_redo->add_do_method(*tile_set, "set_source_level_tile_proxy", from.source_id, to.source_id);
|
||||
if (tile_set->has_source_level_tile_proxy(from.source_id)) {
|
||||
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", to.source_id, tile_set->get_source_level_tile_proxy(from.source_id));
|
||||
} else {
|
||||
undo_redo->add_undo_method(*tile_set, "remove_source_level_tile_proxy", from.source_id);
|
||||
}
|
||||
}
|
||||
undo_redo->add_do_method(this, "_update_lists");
|
||||
undo_redo->add_undo_method(this, "_update_lists");
|
||||
undo_redo->commit_action();
|
||||
commited_actions_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Delete All Invalid Tile Proxies"));
|
||||
|
||||
undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies");
|
||||
|
||||
Array proxies = tile_set->get_source_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
|
||||
}
|
||||
|
||||
proxies = tile_set->get_coords_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
|
||||
}
|
||||
|
||||
proxies = tile_set->get_alternative_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
|
||||
}
|
||||
undo_redo->add_do_method(this, "_update_lists");
|
||||
undo_redo->add_undo_method(this, "_update_lists");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_clear_all_button_pressed() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Delete All Tile Proxies"));
|
||||
|
||||
undo_redo->add_do_method(*tile_set, "clear_tile_proxies");
|
||||
|
||||
Array proxies = tile_set->get_source_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
|
||||
}
|
||||
|
||||
proxies = tile_set->get_coords_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
|
||||
}
|
||||
|
||||
proxies = tile_set->get_alternative_level_tile_proxies();
|
||||
for (int i = 0; i < proxies.size(); i++) {
|
||||
Array proxy = proxies[i];
|
||||
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
|
||||
}
|
||||
undo_redo->add_do_method(this, "_update_lists");
|
||||
undo_redo->add_undo_method(this, "_update_lists");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
bool TileProxiesManagerDialog::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (p_name == "from_source") {
|
||||
from.source_id = MAX(int(p_value), -1);
|
||||
} else if (p_name == "from_coords") {
|
||||
from.set_atlas_coords(Vector2i(p_value).max(Vector2i(-1, -1)));
|
||||
} else if (p_name == "from_alternative") {
|
||||
from.alternative_tile = MAX(int(p_value), -1);
|
||||
} else if (p_name == "to_source") {
|
||||
to.source_id = MAX(int(p_value), 0);
|
||||
} else if (p_name == "to_coords") {
|
||||
to.set_atlas_coords(Vector2i(p_value).max(Vector2i(0, 0)));
|
||||
} else if (p_name == "to_alternative") {
|
||||
to.alternative_tile = MAX(int(p_value), 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_update_enabled_property_editors();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileProxiesManagerDialog::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (p_name == "from_source") {
|
||||
r_ret = from.source_id;
|
||||
} else if (p_name == "from_coords") {
|
||||
r_ret = from.get_atlas_coords();
|
||||
} else if (p_name == "from_alternative") {
|
||||
r_ret = from.alternative_tile;
|
||||
} else if (p_name == "to_source") {
|
||||
r_ret = to.source_id;
|
||||
} else if (p_name == "to_coords") {
|
||||
r_ret = to.get_atlas_coords();
|
||||
} else if (p_name == "to_alternative") {
|
||||
r_ret = to.alternative_tile;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_unhandled_key_input(Ref<InputEvent> p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) {
|
||||
if (!is_inside_tree() || !is_visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (popup_menu->activate_item_by_event(p_event, false)) {
|
||||
set_input_as_handled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::cancel_pressed() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
for (int i = 0; i < commited_actions_count; i++) {
|
||||
undo_redo->undo();
|
||||
}
|
||||
commited_actions_count = 0;
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_update_lists"), &TileProxiesManagerDialog::_update_lists);
|
||||
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileProxiesManagerDialog::_unhandled_key_input);
|
||||
}
|
||||
|
||||
void TileProxiesManagerDialog::update_tile_set(Ref<TileSet> p_tile_set) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
tile_set = p_tile_set;
|
||||
commited_actions_count = 0;
|
||||
_update_lists();
|
||||
}
|
||||
|
||||
TileProxiesManagerDialog::TileProxiesManagerDialog() {
|
||||
// Tile proxy management window.
|
||||
set_title(TTR("Tile Proxies Management"));
|
||||
set_process_unhandled_key_input(true);
|
||||
|
||||
to.source_id = 0;
|
||||
to.set_atlas_coords(Vector2i());
|
||||
to.alternative_tile = 0;
|
||||
|
||||
VBoxContainer *vbox_container = memnew(VBoxContainer);
|
||||
vbox_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
vbox_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_child(vbox_container);
|
||||
|
||||
Label *source_level_label = memnew(Label);
|
||||
source_level_label->set_text(TTR("Source-level proxies"));
|
||||
vbox_container->add_child(source_level_label);
|
||||
|
||||
source_level_list = memnew(ItemList);
|
||||
source_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
source_level_list->set_select_mode(ItemList::SELECT_MULTI);
|
||||
source_level_list->set_allow_rmb_select(true);
|
||||
source_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(source_level_list));
|
||||
vbox_container->add_child(source_level_list);
|
||||
|
||||
Label *coords_level_label = memnew(Label);
|
||||
coords_level_label->set_text(TTR("Coords-level proxies"));
|
||||
vbox_container->add_child(coords_level_label);
|
||||
|
||||
coords_level_list = memnew(ItemList);
|
||||
coords_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
coords_level_list->set_select_mode(ItemList::SELECT_MULTI);
|
||||
coords_level_list->set_allow_rmb_select(true);
|
||||
coords_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(coords_level_list));
|
||||
vbox_container->add_child(coords_level_list);
|
||||
|
||||
Label *alternative_level_label = memnew(Label);
|
||||
alternative_level_label->set_text(TTR("Alternative-level proxies"));
|
||||
vbox_container->add_child(alternative_level_label);
|
||||
|
||||
alternative_level_list = memnew(ItemList);
|
||||
alternative_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
alternative_level_list->set_select_mode(ItemList::SELECT_MULTI);
|
||||
alternative_level_list->set_allow_rmb_select(true);
|
||||
alternative_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(alternative_level_list));
|
||||
vbox_container->add_child(alternative_level_list);
|
||||
|
||||
popup_menu = memnew(PopupMenu);
|
||||
popup_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_delete"));
|
||||
popup_menu->connect("id_pressed", callable_mp(this, &TileProxiesManagerDialog::_menu_id_pressed));
|
||||
add_child(popup_menu);
|
||||
|
||||
// Add proxy panel.
|
||||
HSeparator *h_separator = memnew(HSeparator);
|
||||
vbox_container->add_child(h_separator);
|
||||
|
||||
Label *add_label = memnew(Label);
|
||||
add_label->set_text(TTR("Add a new tile proxy:"));
|
||||
vbox_container->add_child(add_label);
|
||||
|
||||
HBoxContainer *hboxcontainer = memnew(HBoxContainer);
|
||||
vbox_container->add_child(hboxcontainer);
|
||||
|
||||
// From
|
||||
VBoxContainer *vboxcontainer_from = memnew(VBoxContainer);
|
||||
vboxcontainer_from->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
hboxcontainer->add_child(vboxcontainer_from);
|
||||
|
||||
source_from_property_editor = memnew(EditorPropertyInteger);
|
||||
source_from_property_editor->set_label(TTR("From Source"));
|
||||
source_from_property_editor->set_object_and_property(this, "from_source");
|
||||
source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
source_from_property_editor->set_selectable(false);
|
||||
source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
source_from_property_editor->setup(-1, 99999, 1, false, true, false);
|
||||
vboxcontainer_from->add_child(source_from_property_editor);
|
||||
|
||||
coords_from_property_editor = memnew(EditorPropertyVector2i);
|
||||
coords_from_property_editor->set_label(TTR("From Coords"));
|
||||
coords_from_property_editor->set_object_and_property(this, "from_coords");
|
||||
coords_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
coords_from_property_editor->set_selectable(false);
|
||||
coords_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
coords_from_property_editor->setup(-1, 99999, true);
|
||||
coords_from_property_editor->hide();
|
||||
vboxcontainer_from->add_child(coords_from_property_editor);
|
||||
|
||||
alternative_from_property_editor = memnew(EditorPropertyInteger);
|
||||
alternative_from_property_editor->set_label(TTR("From Alternative"));
|
||||
alternative_from_property_editor->set_object_and_property(this, "from_alternative");
|
||||
alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
alternative_from_property_editor->set_selectable(false);
|
||||
alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
alternative_from_property_editor->setup(-1, 99999, 1, false, true, false);
|
||||
alternative_from_property_editor->hide();
|
||||
vboxcontainer_from->add_child(alternative_from_property_editor);
|
||||
|
||||
// To
|
||||
VBoxContainer *vboxcontainer_to = memnew(VBoxContainer);
|
||||
vboxcontainer_to->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
hboxcontainer->add_child(vboxcontainer_to);
|
||||
|
||||
source_to_property_editor = memnew(EditorPropertyInteger);
|
||||
source_to_property_editor->set_label(TTR("To Source"));
|
||||
source_to_property_editor->set_object_and_property(this, "to_source");
|
||||
source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
source_to_property_editor->set_selectable(false);
|
||||
source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
source_to_property_editor->setup(-1, 99999, 1, false, true, false);
|
||||
vboxcontainer_to->add_child(source_to_property_editor);
|
||||
|
||||
coords_to_property_editor = memnew(EditorPropertyVector2i);
|
||||
coords_to_property_editor->set_label(TTR("To Coords"));
|
||||
coords_to_property_editor->set_object_and_property(this, "to_coords");
|
||||
coords_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
coords_to_property_editor->set_selectable(false);
|
||||
coords_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
coords_to_property_editor->setup(-1, 99999, true);
|
||||
coords_to_property_editor->hide();
|
||||
vboxcontainer_to->add_child(coords_to_property_editor);
|
||||
|
||||
alternative_to_property_editor = memnew(EditorPropertyInteger);
|
||||
alternative_to_property_editor->set_label(TTR("To Alternative"));
|
||||
alternative_to_property_editor->set_object_and_property(this, "to_alternative");
|
||||
alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
|
||||
alternative_to_property_editor->set_selectable(false);
|
||||
alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
alternative_to_property_editor->setup(-1, 99999, 1, false, true, false);
|
||||
alternative_to_property_editor->hide();
|
||||
vboxcontainer_to->add_child(alternative_to_property_editor);
|
||||
|
||||
Button *add_button = memnew(Button);
|
||||
add_button->set_text(TTR("Add"));
|
||||
add_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
add_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_add_button_pressed));
|
||||
vbox_container->add_child(add_button);
|
||||
|
||||
h_separator = memnew(HSeparator);
|
||||
vbox_container->add_child(h_separator);
|
||||
|
||||
// Generic actions.
|
||||
Label *generic_actions_label = memnew(Label);
|
||||
generic_actions_label->set_text(TTR("Global actions:"));
|
||||
vbox_container->add_child(generic_actions_label);
|
||||
|
||||
hboxcontainer = memnew(HBoxContainer);
|
||||
vbox_container->add_child(hboxcontainer);
|
||||
|
||||
Button *clear_invalid_button = memnew(Button);
|
||||
clear_invalid_button->set_text(TTR("Clear Invalid"));
|
||||
clear_invalid_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
clear_invalid_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_invalid_button_pressed));
|
||||
hboxcontainer->add_child(clear_invalid_button);
|
||||
|
||||
Button *clear_all_button = memnew(Button);
|
||||
clear_all_button->set_text(TTR("Clear All"));
|
||||
clear_all_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
clear_all_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_all_button_pressed));
|
||||
hboxcontainer->add_child(clear_all_button);
|
||||
|
||||
h_separator = memnew(HSeparator);
|
||||
vbox_container->add_child(h_separator);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/**************************************************************************/
|
||||
/* tile_proxies_manager_dialog.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_PROXIES_MANAGER_DIALOG_H
|
||||
#define TILE_PROXIES_MANAGER_DIALOG_H
|
||||
|
||||
#include "editor/editor_properties.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
|
||||
class EditorPropertyVector2i;
|
||||
class EditorUndoRedoManager;
|
||||
|
||||
class TileProxiesManagerDialog : public ConfirmationDialog {
|
||||
GDCLASS(TileProxiesManagerDialog, ConfirmationDialog);
|
||||
|
||||
private:
|
||||
int commited_actions_count = 0;
|
||||
Ref<TileSet> tile_set;
|
||||
|
||||
TileMapCell from;
|
||||
TileMapCell to;
|
||||
|
||||
// GUI
|
||||
ItemList *source_level_list = nullptr;
|
||||
ItemList *coords_level_list = nullptr;
|
||||
ItemList *alternative_level_list = nullptr;
|
||||
|
||||
EditorPropertyInteger *source_from_property_editor = nullptr;
|
||||
EditorPropertyVector2i *coords_from_property_editor = nullptr;
|
||||
EditorPropertyInteger *alternative_from_property_editor = nullptr;
|
||||
EditorPropertyInteger *source_to_property_editor = nullptr;
|
||||
EditorPropertyVector2i *coords_to_property_editor = nullptr;
|
||||
EditorPropertyInteger *alternative_to_property_editor = nullptr;
|
||||
|
||||
PopupMenu *popup_menu = nullptr;
|
||||
void _right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list);
|
||||
void _menu_id_pressed(int p_id);
|
||||
void _delete_selected_bindings();
|
||||
void _update_lists();
|
||||
void _update_enabled_property_editors();
|
||||
void _property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing);
|
||||
void _add_button_pressed();
|
||||
|
||||
void _clear_invalid_button_pressed();
|
||||
void _clear_all_button_pressed();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _unhandled_key_input(Ref<InputEvent> p_event);
|
||||
virtual void cancel_pressed() override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void update_tile_set(Ref<TileSet> p_tile_set);
|
||||
|
||||
TileProxiesManagerDialog();
|
||||
};
|
||||
|
||||
#endif // TILE_PROXIES_MANAGER_DIALOG_H
|
3005
modules/layered_tile_map/editor/tile_set_atlas_source_editor.cpp
Normal file
3005
modules/layered_tile_map/editor/tile_set_atlas_source_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
336
modules/layered_tile_map/editor/tile_set_atlas_source_editor.h
Normal file
336
modules/layered_tile_map/editor/tile_set_atlas_source_editor.h
Normal file
@ -0,0 +1,336 @@
|
||||
/**************************************************************************/
|
||||
/* tile_set_atlas_source_editor.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_SET_ATLAS_SOURCE_EDITOR_H
|
||||
#define TILE_SET_ATLAS_SOURCE_EDITOR_H
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
#include "tile_data_editors.h"
|
||||
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class Popup;
|
||||
class TileSet;
|
||||
class Tree;
|
||||
class VSeparator;
|
||||
|
||||
class TileSetAtlasSourceEditor : public HSplitContainer {
|
||||
GDCLASS(TileSetAtlasSourceEditor, HSplitContainer);
|
||||
|
||||
public:
|
||||
// A class to store which tiles are selected.
|
||||
struct TileSelection {
|
||||
Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS;
|
||||
int alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
|
||||
bool operator<(const TileSelection &p_other) const {
|
||||
if (tile == p_other.tile) {
|
||||
return alternative < p_other.alternative;
|
||||
} else {
|
||||
return tile < p_other.tile;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// -- Proxy object for an atlas source, needed by the inspector --
|
||||
class TileSetAtlasSourceProxyObject : public Object {
|
||||
GDCLASS(TileSetAtlasSourceProxyObject, Object);
|
||||
|
||||
private:
|
||||
Ref<TileSet> tile_set;
|
||||
Ref<TileSetAtlasSource> tile_set_atlas_source;
|
||||
int source_id = TileSet::INVALID_SOURCE;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_id(int p_id);
|
||||
int get_id() const;
|
||||
|
||||
void edit(Ref<TileSet> p_tile_set, Ref<TileSetAtlasSource> p_tile_set_atlas_source, int p_source_id);
|
||||
Ref<TileSetAtlasSource> get_edited() { return tile_set_atlas_source; };
|
||||
};
|
||||
|
||||
// -- Proxy object for a tile, needed by the inspector --
|
||||
class AtlasTileProxyObject : public Object {
|
||||
GDCLASS(AtlasTileProxyObject, Object);
|
||||
|
||||
private:
|
||||
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr;
|
||||
|
||||
Ref<TileSetAtlasSource> tile_set_atlas_source;
|
||||
RBSet<TileSelection> tiles = RBSet<TileSelection>();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Ref<TileSetAtlasSource> get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; };
|
||||
RBSet<TileSelection> get_edited_tiles() const { return tiles; };
|
||||
|
||||
// Update the proxyed object.
|
||||
void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles = RBSet<TileSelection>());
|
||||
|
||||
AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) {
|
||||
tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor;
|
||||
}
|
||||
};
|
||||
|
||||
class TileAtlasControl : public Control {
|
||||
TileSetAtlasSourceEditor *editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
|
||||
TileAtlasControl(TileSetAtlasSourceEditor *p_editor) { editor = p_editor; }
|
||||
};
|
||||
friend class TileAtlasControl;
|
||||
|
||||
private:
|
||||
bool read_only = false;
|
||||
|
||||
Ref<TileSet> tile_set;
|
||||
TileSetAtlasSource *tile_set_atlas_source = nullptr;
|
||||
int tile_set_atlas_source_id = TileSet::INVALID_SOURCE;
|
||||
Ref<Texture2D> atlas_source_texture;
|
||||
|
||||
bool tile_set_changed_needs_update = false;
|
||||
|
||||
// -- Properties painting --
|
||||
ScrollContainer *tile_data_editors_scroll = nullptr;
|
||||
VBoxContainer *tile_data_painting_editor_container = nullptr;
|
||||
Label *tile_data_editors_label = nullptr;
|
||||
Button *tile_data_editor_dropdown_button = nullptr;
|
||||
Popup *tile_data_editors_popup = nullptr;
|
||||
Tree *tile_data_editors_tree = nullptr;
|
||||
void _tile_data_editor_dropdown_button_draw();
|
||||
void _tile_data_editor_dropdown_button_pressed();
|
||||
|
||||
// -- Tile data editors --
|
||||
String current_property;
|
||||
Control *current_tile_data_editor_toolbar = nullptr;
|
||||
HashMap<String, TileDataEditor *> tile_data_editors;
|
||||
TileDataEditor *current_tile_data_editor = nullptr;
|
||||
void _tile_data_editors_tree_selected();
|
||||
|
||||
// -- Inspector --
|
||||
AtlasTileProxyObject *tile_proxy_object = nullptr;
|
||||
EditorInspector *tile_inspector = nullptr;
|
||||
Label *tile_inspector_no_tile_selected_label = nullptr;
|
||||
String selected_property;
|
||||
void _inspector_property_selected(const String &p_property);
|
||||
|
||||
TileSetAtlasSourceProxyObject *atlas_source_proxy_object = nullptr;
|
||||
EditorInspector *atlas_source_inspector = nullptr;
|
||||
|
||||
// -- Atlas view --
|
||||
TileAtlasView *tile_atlas_view = nullptr;
|
||||
VBoxContainer *tile_create_help = nullptr;
|
||||
|
||||
// Dragging
|
||||
enum DragType {
|
||||
DRAG_TYPE_NONE = 0,
|
||||
DRAG_TYPE_CREATE_TILES,
|
||||
DRAG_TYPE_CREATE_TILES_USING_RECT,
|
||||
DRAG_TYPE_CREATE_BIG_TILE,
|
||||
DRAG_TYPE_REMOVE_TILES,
|
||||
DRAG_TYPE_REMOVE_TILES_USING_RECT,
|
||||
|
||||
DRAG_TYPE_MOVE_TILE,
|
||||
|
||||
DRAG_TYPE_RECT_SELECT,
|
||||
|
||||
DRAG_TYPE_MAY_POPUP_MENU,
|
||||
|
||||
// Warning: keep in this order.
|
||||
DRAG_TYPE_RESIZE_TOP_LEFT,
|
||||
DRAG_TYPE_RESIZE_TOP,
|
||||
DRAG_TYPE_RESIZE_TOP_RIGHT,
|
||||
DRAG_TYPE_RESIZE_RIGHT,
|
||||
DRAG_TYPE_RESIZE_BOTTOM_RIGHT,
|
||||
DRAG_TYPE_RESIZE_BOTTOM,
|
||||
DRAG_TYPE_RESIZE_BOTTOM_LEFT,
|
||||
DRAG_TYPE_RESIZE_LEFT,
|
||||
};
|
||||
DragType drag_type = DRAG_TYPE_NONE;
|
||||
Vector2i drag_start_mouse_pos;
|
||||
Vector2i drag_last_mouse_pos;
|
||||
Vector2i drag_current_tile;
|
||||
|
||||
Rect2i drag_start_tile_shape;
|
||||
RBSet<Vector2i> drag_modified_tiles;
|
||||
void _end_dragging();
|
||||
|
||||
HashMap<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas);
|
||||
|
||||
// Popup functions.
|
||||
enum MenuOptions {
|
||||
TILE_CREATE,
|
||||
TILE_CREATE_ALTERNATIVE,
|
||||
TILE_DELETE,
|
||||
|
||||
ADVANCED_AUTO_CREATE_TILES,
|
||||
ADVANCED_AUTO_REMOVE_TILES,
|
||||
ADVANCED_CLEANUP_TILES,
|
||||
};
|
||||
Vector2i menu_option_coords;
|
||||
int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
|
||||
void _menu_option(int p_option);
|
||||
|
||||
// Tool buttons.
|
||||
Ref<ButtonGroup> tools_button_group;
|
||||
Button *tool_setup_atlas_source_button = nullptr;
|
||||
Button *tool_select_button = nullptr;
|
||||
Button *tool_paint_button = nullptr;
|
||||
Label *tool_tile_id_label = nullptr;
|
||||
|
||||
// Tool settings.
|
||||
HBoxContainer *tool_settings = nullptr;
|
||||
HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr;
|
||||
Button *tools_settings_erase_button = nullptr;
|
||||
MenuButton *tool_advanced_menu_button = nullptr;
|
||||
TextureRect *outside_tiles_warning = nullptr;
|
||||
|
||||
// Selection.
|
||||
RBSet<TileSelection> selection;
|
||||
|
||||
void _set_selection_from_array(const Array &p_selection);
|
||||
Array _get_selection_as_array();
|
||||
|
||||
// A control on the tile atlas to draw and handle input events.
|
||||
Vector2i hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
|
||||
|
||||
PopupMenu *base_tile_popup_menu = nullptr;
|
||||
PopupMenu *empty_base_tile_popup_menu = nullptr;
|
||||
Ref<Texture2D> resize_handle;
|
||||
Ref<Texture2D> resize_handle_disabled;
|
||||
Control *tile_atlas_control = nullptr;
|
||||
Control *tile_atlas_control_unscaled = nullptr;
|
||||
void _tile_atlas_control_draw();
|
||||
void _tile_atlas_control_unscaled_draw();
|
||||
void _tile_atlas_control_mouse_exited();
|
||||
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _tile_atlas_view_transform_changed();
|
||||
|
||||
// A control over the alternative tiles.
|
||||
Vector3i hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
|
||||
|
||||
PopupMenu *alternative_tile_popup_menu = nullptr;
|
||||
Control *alternative_tiles_control = nullptr;
|
||||
Control *alternative_tiles_control_unscaled = nullptr;
|
||||
void _tile_alternatives_control_draw();
|
||||
void _tile_alternatives_control_unscaled_draw();
|
||||
void _tile_alternatives_control_mouse_exited();
|
||||
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
// -- Update functions --
|
||||
void _update_tile_id_label();
|
||||
void _update_source_inspector();
|
||||
void _update_fix_selected_and_hovered_tiles();
|
||||
void _update_atlas_source_inspector();
|
||||
void _update_tile_inspector();
|
||||
void _update_tile_data_editors();
|
||||
void _update_current_tile_data_editor();
|
||||
void _update_manage_tile_properties_button();
|
||||
void _update_atlas_view();
|
||||
void _update_toolbar();
|
||||
|
||||
// -- Misc --
|
||||
void _auto_create_tiles();
|
||||
void _auto_remove_tiles();
|
||||
void _cancel_auto_create_tiles();
|
||||
AcceptDialog *confirm_auto_create_tiles = nullptr;
|
||||
Vector<Ref<TileSetAtlasSource>> atlases_to_auto_create_tiles;
|
||||
Vector2i _get_drag_offset_tile_coords(const Vector2i &p_offset) const;
|
||||
|
||||
void _update_source_texture();
|
||||
void _check_outside_tiles();
|
||||
void _cleanup_outside_tiles();
|
||||
|
||||
void _tile_set_changed();
|
||||
void _tile_proxy_object_changed(const String &p_what);
|
||||
void _atlas_source_proxy_object_changed(const String &p_what);
|
||||
|
||||
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
// -- input events --
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
|
||||
void init_new_atlases(const Vector<Ref<TileSetAtlasSource>> &p_atlases);
|
||||
|
||||
TileSetAtlasSourceEditor();
|
||||
~TileSetAtlasSourceEditor();
|
||||
};
|
||||
|
||||
class EditorPropertyTilePolygon : public EditorProperty {
|
||||
GDCLASS(EditorPropertyTilePolygon, EditorProperty);
|
||||
|
||||
StringName count_property;
|
||||
String element_pattern;
|
||||
String base_type;
|
||||
|
||||
void _add_focusable_children(Node *p_node);
|
||||
|
||||
GenericTilePolygonEditor *generic_tile_polygon_editor = nullptr;
|
||||
void _polygons_changed();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
void setup_single_mode(const StringName &p_property, const String &p_base_type);
|
||||
void setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type);
|
||||
EditorPropertyTilePolygon();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginTileData : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginTileData, EditorInspectorPlugin);
|
||||
|
||||
void _occlusion_polygon_set_callback();
|
||||
void _polygons_changed(Object *p_generic_tile_polygon_editor, Object *p_object, const String &p_path);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
#endif // TILE_SET_ATLAS_SOURCE_EDITOR_H
|
1024
modules/layered_tile_map/editor/tile_set_editor.cpp
Normal file
1024
modules/layered_tile_map/editor/tile_set_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
145
modules/layered_tile_map/editor/tile_set_editor.h
Normal file
145
modules/layered_tile_map/editor/tile_set_editor.h
Normal file
@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* tile_set_editor.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_SET_EDITOR_H
|
||||
#define TILE_SET_EDITOR_H
|
||||
|
||||
#include "atlas_merging_dialog.h"
|
||||
#include "scene/gui/tab_bar.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
#include "tile_proxies_manager_dialog.h"
|
||||
#include "tile_set_atlas_source_editor.h"
|
||||
#include "tile_set_scenes_collection_source_editor.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class SpinBox;
|
||||
class HBoxContainer;
|
||||
class SplitContainer;
|
||||
class EditorFileDialog;
|
||||
class EditorInspectorPlugin;
|
||||
|
||||
class TileSetEditor : public Control {
|
||||
GDCLASS(TileSetEditor, Control);
|
||||
|
||||
static TileSetEditor *singleton;
|
||||
|
||||
private:
|
||||
bool read_only = false;
|
||||
|
||||
Ref<TileSet> tile_set;
|
||||
bool tile_set_changed_needs_update = false;
|
||||
HSplitContainer *split_container = nullptr;
|
||||
|
||||
// TabBar.
|
||||
HBoxContainer *tile_set_toolbar = nullptr;
|
||||
TabBar *tabs_bar = nullptr;
|
||||
|
||||
// Tiles.
|
||||
Label *no_source_selected_label = nullptr;
|
||||
TileSetAtlasSourceEditor *tile_set_atlas_source_editor = nullptr;
|
||||
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
|
||||
|
||||
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void _load_texture_files(const Vector<String> &p_paths);
|
||||
|
||||
void _update_sources_list(int force_selected_id = -1);
|
||||
|
||||
// Sources management.
|
||||
Button *sources_delete_button = nullptr;
|
||||
MenuButton *sources_add_button = nullptr;
|
||||
MenuButton *source_sort_button = nullptr;
|
||||
MenuButton *sources_advanced_menu_button = nullptr;
|
||||
ItemList *sources_list = nullptr;
|
||||
Ref<Texture2D> missing_texture_texture;
|
||||
void _source_selected(int p_source_index);
|
||||
void _source_delete_pressed();
|
||||
void _source_add_id_pressed(int p_id_pressed);
|
||||
void _sources_advanced_menu_id_pressed(int p_id_pressed);
|
||||
void _set_source_sort(int p_sort);
|
||||
|
||||
EditorFileDialog *texture_file_dialog = nullptr;
|
||||
AtlasMergingDialog *atlas_merging_dialog = nullptr;
|
||||
TileProxiesManagerDialog *tile_proxies_manager_dialog = nullptr;
|
||||
|
||||
bool first_edit = true;
|
||||
|
||||
// Patterns.
|
||||
ItemList *patterns_item_list = nullptr;
|
||||
Label *patterns_help_label = nullptr;
|
||||
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
|
||||
bool select_last_pattern = false;
|
||||
void _update_patterns_list();
|
||||
|
||||
// Expanded editor.
|
||||
PanelContainer *expanded_area = nullptr;
|
||||
Control *expanded_editor = nullptr;
|
||||
ObjectID expanded_editor_parent;
|
||||
LocalVector<SplitContainer *> disable_on_expand;
|
||||
|
||||
void _tile_set_changed();
|
||||
void _tab_changed(int p_tab_changed);
|
||||
|
||||
void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
|
||||
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
|
||||
|
||||
void edit(Ref<TileSet> p_tile_set);
|
||||
|
||||
void add_expanded_editor(Control *p_editor);
|
||||
void remove_expanded_editor();
|
||||
void register_split(SplitContainer *p_split);
|
||||
|
||||
TileSetEditor();
|
||||
};
|
||||
|
||||
class TileSourceInspectorPlugin : public EditorInspectorPlugin {
|
||||
GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
|
||||
|
||||
AcceptDialog *id_edit_dialog = nullptr;
|
||||
Label *id_label = nullptr;
|
||||
SpinBox *id_input = nullptr;
|
||||
Object *edited_source = nullptr;
|
||||
|
||||
void _show_id_edit_dialog(Object *p_for_source);
|
||||
void _confirm_change_id();
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
#endif // TILE_SET_EDITOR_H
|
@ -0,0 +1,591 @@
|
||||
/**************************************************************************/
|
||||
/* tile_set_scenes_collection_source_editor.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tile_set_scenes_collection_source_editor.h"
|
||||
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/plugins/tiles/tile_set_editor.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
|
||||
#include "core/core_string_names.h"
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id(int p_id) {
|
||||
ERR_FAIL_COND(p_id < 0);
|
||||
if (source_id == p_id) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Scenes Collection source ID. Another TileSet source exists with id %d.", p_id));
|
||||
|
||||
int previous_source = source_id;
|
||||
source_id = p_id; // source_id must be updated before, because it's used by the source list update.
|
||||
tile_set->set_source_id(previous_source, p_id);
|
||||
emit_signal(SNAME("changed"), "id");
|
||||
}
|
||||
|
||||
int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id() {
|
||||
return source_id;
|
||||
}
|
||||
|
||||
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
|
||||
String name = p_name;
|
||||
if (name == "name") {
|
||||
// Use the resource_name property to store the source's name.
|
||||
name = "resource_name";
|
||||
}
|
||||
bool valid = false;
|
||||
tile_set_scenes_collection_source->set(name, p_value, &valid);
|
||||
if (valid) {
|
||||
emit_signal(SNAME("changed"), String(name).utf8().get_data());
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (!tile_set_scenes_collection_source) {
|
||||
return false;
|
||||
}
|
||||
String name = p_name;
|
||||
if (name == "name") {
|
||||
// Use the resource_name property to store the source's name.
|
||||
name = "resource_name";
|
||||
}
|
||||
bool valid = false;
|
||||
r_ret = tile_set_scenes_collection_source->get(name, &valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() {
|
||||
// -- Shape and layout --
|
||||
ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id);
|
||||
ClassDB::bind_method(D_METHOD("get_id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
|
||||
ERR_FAIL_COND(p_source_id < 0);
|
||||
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
|
||||
|
||||
if (tile_set == p_tile_set && tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && source_id == p_source_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect to changes.
|
||||
if (tile_set_scenes_collection_source) {
|
||||
tile_set_scenes_collection_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
|
||||
}
|
||||
|
||||
tile_set = p_tile_set;
|
||||
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
|
||||
source_id = p_source_id;
|
||||
|
||||
// Connect to changes.
|
||||
if (tile_set_scenes_collection_source) {
|
||||
if (!tile_set_scenes_collection_source->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
|
||||
tile_set_scenes_collection_source->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
|
||||
}
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
// -- Proxy object used by the tile inspector --
|
||||
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (!tile_set_scenes_collection_source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_name == "id") {
|
||||
int as_int = int(p_value);
|
||||
ERR_FAIL_COND_V(as_int < 0, false);
|
||||
ERR_FAIL_COND_V(tile_set_scenes_collection_source->has_scene_tile_id(as_int), false);
|
||||
tile_set_scenes_collection_source->set_scene_tile_id(scene_id, as_int);
|
||||
scene_id = as_int;
|
||||
emit_signal(SNAME("changed"), "id");
|
||||
for (int i = 0; i < tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_count(); i++) {
|
||||
if (int(tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_metadata(i)) == scene_id) {
|
||||
tile_set_scenes_collection_source_editor->scene_tiles_list->select(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (p_name == "scene") {
|
||||
tile_set_scenes_collection_source->set_scene_tile_scene(scene_id, p_value);
|
||||
emit_signal(SNAME("changed"), "scene");
|
||||
return true;
|
||||
} else if (p_name == "display_placeholder") {
|
||||
tile_set_scenes_collection_source->set_scene_tile_display_placeholder(scene_id, p_value);
|
||||
emit_signal(SNAME("changed"), "display_placeholder");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (!tile_set_scenes_collection_source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_name == "id") {
|
||||
r_ret = scene_id;
|
||||
return true;
|
||||
} else if (p_name == "scene") {
|
||||
r_ret = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
|
||||
return true;
|
||||
} else if (p_name == "display_placeholder") {
|
||||
r_ret = tile_set_scenes_collection_source->get_scene_tile_display_placeholder(scene_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
if (!tile_set_scenes_collection_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_NONE, ""));
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "display_placeholder", PROPERTY_HINT_NONE, ""));
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_scene_id) {
|
||||
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
|
||||
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
|
||||
|
||||
if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
|
||||
scene_id = p_scene_id;
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed(const String &p_what) {
|
||||
if (p_what == "id") {
|
||||
emit_signal(SNAME("source_id_changed"), scenes_collection_source_proxy_object->get_id());
|
||||
}
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed() {
|
||||
tile_set_scenes_collection_source_changed_needs_update = true;
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud) {
|
||||
int index = p_ud;
|
||||
|
||||
if (index >= 0 && index < scene_tiles_list->get_item_count()) {
|
||||
scene_tiles_list->set_item_icon(index, p_preview);
|
||||
}
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_index) {
|
||||
Ref<PackedScene> packed_scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_tiles_list->get_item_metadata(p_index));
|
||||
if (packed_scene.is_valid()) {
|
||||
EditorNode::get_singleton()->open_request(packed_scene->get_path());
|
||||
}
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_source_add_pressed() {
|
||||
if (!scene_select_dialog) {
|
||||
scene_select_dialog = memnew(EditorFileDialog);
|
||||
add_child(scene_select_dialog);
|
||||
scene_select_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
scene_select_dialog->connect("file_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scene_file_selected));
|
||||
|
||||
for (const String &E : Vector<String>{ "tscn", "scn" }) {
|
||||
scene_select_dialog->add_filter("*." + E, E.to_upper());
|
||||
}
|
||||
}
|
||||
scene_select_dialog->popup_file_dialog();
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_scene_file_selected(const String &p_path) {
|
||||
Ref<PackedScene> scene = ResourceLoader::load(p_path);
|
||||
|
||||
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add a Scene Tile"));
|
||||
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id);
|
||||
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
|
||||
undo_redo->commit_action();
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
_update_tile_inspector();
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_source_delete_pressed() {
|
||||
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
|
||||
ERR_FAIL_COND(selected_indices.is_empty());
|
||||
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Remove a Scene Tile"));
|
||||
undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
|
||||
undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id);
|
||||
undo_redo->commit_action();
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
_update_tile_inspector();
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_update_source_inspector() {
|
||||
// Update the proxy object.
|
||||
scenes_collection_source_proxy_object->edit(tile_set, tile_set_scenes_collection_source, tile_set_source_id);
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_update_tile_inspector() {
|
||||
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
|
||||
bool has_atlas_tile_selected = (selected_indices.size() > 0);
|
||||
|
||||
// Update the proxy object.
|
||||
if (has_atlas_tile_selected) {
|
||||
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
|
||||
tile_proxy_object->edit(tile_set_scenes_collection_source, scene_id);
|
||||
}
|
||||
|
||||
// Update visibility.
|
||||
tile_inspector_label->set_visible(has_atlas_tile_selected);
|
||||
tile_inspector->set_visible(has_atlas_tile_selected);
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_update_action_buttons() {
|
||||
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
|
||||
scene_tile_delete_button->set_disabled(selected_indices.size() <= 0 || read_only);
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_update_scenes_list() {
|
||||
if (!tile_set_scenes_collection_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the previously selected id.
|
||||
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
|
||||
int old_selected_scene_id = (selected_indices.size() > 0) ? int(scene_tiles_list->get_item_metadata(selected_indices[0])) : -1;
|
||||
|
||||
// Clear the list.
|
||||
scene_tiles_list->clear();
|
||||
|
||||
// Rebuild the list.
|
||||
int to_reselect = -1;
|
||||
for (int i = 0; i < tile_set_scenes_collection_source->get_scene_tiles_count(); i++) {
|
||||
int scene_id = tile_set_scenes_collection_source->get_scene_tile_id(i);
|
||||
|
||||
Ref<PackedScene> scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
|
||||
|
||||
int item_index = 0;
|
||||
if (scene.is_valid()) {
|
||||
item_index = scene_tiles_list->add_item(vformat("%s (path:%s id:%d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id));
|
||||
Variant udata = i;
|
||||
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
|
||||
} else {
|
||||
item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_editor_theme_icon(SNAME("PackedScene")));
|
||||
}
|
||||
scene_tiles_list->set_item_metadata(item_index, scene_id);
|
||||
|
||||
if (old_selected_scene_id >= 0 && scene_id == old_selected_scene_id) {
|
||||
to_reselect = i;
|
||||
}
|
||||
}
|
||||
if (scene_tiles_list->get_item_count() == 0) {
|
||||
scene_tiles_list->add_item(TTR("Drag and drop scenes here or use the Add button."));
|
||||
scene_tiles_list->set_item_disabled(-1, true);
|
||||
}
|
||||
|
||||
// Reselect if needed.
|
||||
if (to_reselect >= 0) {
|
||||
scene_tiles_list->select(to_reselect);
|
||||
}
|
||||
|
||||
// Icon size update.
|
||||
int int_size = int(EDITOR_GET("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
|
||||
scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size));
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
|
||||
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "id", TTR("The tile's unique identifier within this TileSet. Each tile stores its source ID, so changing one may make tiles invalid."));
|
||||
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "name", TTR("The human-readable name for the scene collection. Use a descriptive name here for organizational purposes (such as \"obstacles\", \"decoration\", etc.)."));
|
||||
|
||||
tile_inspector->edit(tile_proxy_object);
|
||||
tile_inspector->add_custom_property_description("SceneTileProxyObject", "id", TTR("ID of the scene tile in the collection. Each painted tile has associated ID, so changing this property may cause your TileMaps to not display properly."));
|
||||
tile_inspector->add_custom_property_description("SceneTileProxyObject", "scene", TTR("Absolute path to the scene associated with this tile."));
|
||||
tile_inspector->add_custom_property_description("SceneTileProxyObject", "display_placeholder", TTR("If [code]true[/code], a placeholder marker will be displayed on top of the scene's preview. The marker is displayed anyway if the scene has no valid preview."));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
scene_tile_add_button->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
scene_tile_delete_button->set_icon(get_editor_theme_icon(SNAME("Remove")));
|
||||
_update_scenes_list();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
if (tile_set_scenes_collection_source_changed_needs_update) {
|
||||
read_only = false;
|
||||
// Add the listener again and check for read-only status.
|
||||
if (tile_set.is_valid()) {
|
||||
read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set);
|
||||
}
|
||||
|
||||
// Update everything.
|
||||
_update_source_inspector();
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
_update_tile_inspector();
|
||||
tile_set_scenes_collection_source_changed_needs_update = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
// Update things just in case.
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
|
||||
ERR_FAIL_COND(p_source_id < 0);
|
||||
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
|
||||
|
||||
bool new_read_only_state = false;
|
||||
if (p_tile_set.is_valid()) {
|
||||
new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set);
|
||||
}
|
||||
|
||||
if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove listener for old objects.
|
||||
if (tile_set_scenes_collection_source) {
|
||||
tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
|
||||
}
|
||||
|
||||
// Change the edited object.
|
||||
tile_set = p_tile_set;
|
||||
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
|
||||
tile_set_source_id = p_source_id;
|
||||
|
||||
// Read-only status is false by default
|
||||
read_only = new_read_only_state;
|
||||
|
||||
if (tile_set.is_valid()) {
|
||||
scenes_collection_source_inspector->set_read_only(read_only);
|
||||
tile_inspector->set_read_only(read_only);
|
||||
|
||||
scene_tile_add_button->set_disabled(read_only);
|
||||
}
|
||||
|
||||
// Add the listener again.
|
||||
if (tile_set_scenes_collection_source) {
|
||||
tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
|
||||
}
|
||||
|
||||
// Update everything.
|
||||
_update_source_inspector();
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
_update_tile_inspector();
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
|
||||
if (!_can_drop_data_fw(p_point, p_data, p_from)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_from == scene_tiles_list) {
|
||||
// Handle dropping a texture in the list of atlas resources.
|
||||
Dictionary d = p_data;
|
||||
Vector<String> files = d["files"];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
Ref<PackedScene> resource = ResourceLoader::load(files[i]);
|
||||
if (resource.is_valid()) {
|
||||
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add a Scene Tile"));
|
||||
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id);
|
||||
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
_update_scenes_list();
|
||||
_update_action_buttons();
|
||||
_update_tile_inspector();
|
||||
}
|
||||
}
|
||||
|
||||
bool TileSetScenesCollectionSourceEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
|
||||
if (p_from == scene_tiles_list) {
|
||||
Dictionary d = p_data;
|
||||
|
||||
if (!d.has("type")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have a Texture2D.
|
||||
if (String(d["type"]) == "files") {
|
||||
Vector<String> files = d["files"];
|
||||
|
||||
if (files.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
|
||||
|
||||
if (!ClassDB::is_parent_class(ftype, "PackedScene")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TileSetScenesCollectionSourceEditor::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done);
|
||||
}
|
||||
|
||||
TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
|
||||
// -- Right side --
|
||||
HSplitContainer *split_container_right_side = memnew(HSplitContainer);
|
||||
split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(split_container_right_side);
|
||||
|
||||
// Middle panel.
|
||||
ScrollContainer *middle_panel = memnew(ScrollContainer);
|
||||
middle_panel->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
middle_panel->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
|
||||
split_container_right_side->add_child(middle_panel);
|
||||
|
||||
VBoxContainer *middle_vbox_container = memnew(VBoxContainer);
|
||||
middle_vbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
middle_panel->add_child(middle_vbox_container);
|
||||
|
||||
// Scenes collection source inspector.
|
||||
scenes_collection_source_inspector_label = memnew(Label);
|
||||
scenes_collection_source_inspector_label->set_text(TTR("Scenes collection properties:"));
|
||||
middle_vbox_container->add_child(scenes_collection_source_inspector_label);
|
||||
|
||||
scenes_collection_source_proxy_object = memnew(TileSetScenesCollectionProxyObject());
|
||||
scenes_collection_source_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed));
|
||||
|
||||
scenes_collection_source_inspector = memnew(EditorInspector);
|
||||
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
scenes_collection_source_inspector->set_use_doc_hints(true);
|
||||
scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
|
||||
middle_vbox_container->add_child(scenes_collection_source_inspector);
|
||||
|
||||
// Tile inspector.
|
||||
tile_inspector_label = memnew(Label);
|
||||
tile_inspector_label->set_text(TTR("Tile properties:"));
|
||||
tile_inspector_label->hide();
|
||||
middle_vbox_container->add_child(tile_inspector_label);
|
||||
|
||||
tile_proxy_object = memnew(SceneTileProxyObject(this));
|
||||
tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_scenes_list).unbind(1));
|
||||
tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
|
||||
|
||||
tile_inspector = memnew(EditorInspector);
|
||||
tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
tile_inspector->set_use_doc_hints(true);
|
||||
tile_inspector->set_use_folding(true);
|
||||
middle_vbox_container->add_child(tile_inspector);
|
||||
|
||||
// Scenes list.
|
||||
VBoxContainer *right_vbox_container = memnew(VBoxContainer);
|
||||
split_container_right_side->add_child(right_vbox_container);
|
||||
|
||||
scene_tiles_list = memnew(ItemList);
|
||||
scene_tiles_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
SET_DRAG_FORWARDING_CDU(scene_tiles_list, TileSetScenesCollectionSourceEditor);
|
||||
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1));
|
||||
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
|
||||
scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated));
|
||||
scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
|
||||
right_vbox_container->add_child(scene_tiles_list);
|
||||
|
||||
HBoxContainer *scenes_bottom_actions = memnew(HBoxContainer);
|
||||
right_vbox_container->add_child(scenes_bottom_actions);
|
||||
|
||||
scene_tile_add_button = memnew(Button);
|
||||
scene_tile_add_button->set_theme_type_variation("FlatButton");
|
||||
scene_tile_add_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_add_pressed));
|
||||
scenes_bottom_actions->add_child(scene_tile_add_button);
|
||||
|
||||
scene_tile_delete_button = memnew(Button);
|
||||
scene_tile_delete_button->set_theme_type_variation("FlatButton");
|
||||
scene_tile_delete_button->set_disabled(true);
|
||||
scene_tile_delete_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed));
|
||||
scenes_bottom_actions->add_child(scene_tile_delete_button);
|
||||
}
|
||||
|
||||
TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() {
|
||||
memdelete(scenes_collection_source_proxy_object);
|
||||
memdelete(tile_proxy_object);
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/**************************************************************************/
|
||||
/* tile_set_scenes_collection_source_editor.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H
|
||||
#define TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class Button;
|
||||
class ItemList;
|
||||
class Label;
|
||||
class EditorFileDialog;
|
||||
|
||||
class TileSetScenesCollectionSourceEditor : public HBoxContainer {
|
||||
GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer);
|
||||
|
||||
private:
|
||||
// -- Proxy object for an atlas source, needed by the inspector --
|
||||
class TileSetScenesCollectionProxyObject : public Object {
|
||||
GDCLASS(TileSetScenesCollectionProxyObject, Object);
|
||||
|
||||
private:
|
||||
Ref<TileSet> tile_set;
|
||||
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
|
||||
int source_id = -1;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_id(int p_id);
|
||||
int get_id();
|
||||
|
||||
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
|
||||
};
|
||||
|
||||
// -- Proxy object for a tile, needed by the inspector --
|
||||
class SceneTileProxyObject : public Object {
|
||||
GDCLASS(SceneTileProxyObject, Object);
|
||||
|
||||
private:
|
||||
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
|
||||
|
||||
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
|
||||
int source_id;
|
||||
int scene_id;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// Update the proxyed object.
|
||||
void edit(TileSetScenesCollectionSource *p_tile_set_atlas_source, int p_scene_id);
|
||||
|
||||
SceneTileProxyObject(TileSetScenesCollectionSourceEditor *p_tiles_set_scenes_collection_source_editor) {
|
||||
tile_set_scenes_collection_source_editor = p_tiles_set_scenes_collection_source_editor;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool read_only = false;
|
||||
|
||||
Ref<TileSet> tile_set;
|
||||
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
|
||||
int tile_set_source_id = -1;
|
||||
|
||||
bool tile_set_scenes_collection_source_changed_needs_update = false;
|
||||
|
||||
// Source inspector.
|
||||
TileSetScenesCollectionProxyObject *scenes_collection_source_proxy_object = nullptr;
|
||||
Label *scenes_collection_source_inspector_label = nullptr;
|
||||
EditorInspector *scenes_collection_source_inspector = nullptr;
|
||||
|
||||
// Tile inspector.
|
||||
SceneTileProxyObject *tile_proxy_object = nullptr;
|
||||
Label *tile_inspector_label = nullptr;
|
||||
EditorInspector *tile_inspector = nullptr;
|
||||
|
||||
ItemList *scene_tiles_list = nullptr;
|
||||
Button *scene_tile_add_button = nullptr;
|
||||
Button *scene_tile_delete_button = nullptr;
|
||||
EditorFileDialog *scene_select_dialog = nullptr;
|
||||
|
||||
void _tile_set_scenes_collection_source_changed();
|
||||
void _scenes_collection_source_proxy_object_changed(const String &p_what);
|
||||
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
|
||||
void _scenes_list_item_activated(int p_index);
|
||||
|
||||
void _source_add_pressed();
|
||||
void _scene_file_selected(const String &p_path);
|
||||
void _source_delete_pressed();
|
||||
|
||||
// Update methods.
|
||||
void _update_source_inspector();
|
||||
void _update_tile_inspector();
|
||||
void _update_scenes_list();
|
||||
void _update_action_buttons();
|
||||
|
||||
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
|
||||
TileSetScenesCollectionSourceEditor();
|
||||
~TileSetScenesCollectionSourceEditor();
|
||||
};
|
||||
|
||||
#endif // TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H
|
571
modules/layered_tile_map/editor/tiles_editor_plugin.cpp
Normal file
571
modules/layered_tile_map/editor/tiles_editor_plugin.cpp
Normal file
@ -0,0 +1,571 @@
|
||||
/**************************************************************************/
|
||||
/* tiles_editor_plugin.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tiles_editor_plugin.h"
|
||||
|
||||
#include "tile_set_editor.h"
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/2d/tile_map_layer.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
TilesEditorUtils *TilesEditorUtils::singleton = nullptr;
|
||||
TileMapEditorPlugin *tile_map_plugin_singleton = nullptr;
|
||||
TileSetEditorPlugin *tile_set_plugin_singleton = nullptr;
|
||||
|
||||
void TilesEditorUtils::_preview_frame_started() {
|
||||
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_pattern_preview_done));
|
||||
}
|
||||
|
||||
void TilesEditorUtils::_pattern_preview_done() {
|
||||
pattern_preview_done.post();
|
||||
}
|
||||
|
||||
void TilesEditorUtils::_thread_func(void *ud) {
|
||||
TilesEditorUtils *te = static_cast<TilesEditorUtils *>(ud);
|
||||
set_current_thread_safe_for_nodes(true);
|
||||
te->_thread();
|
||||
}
|
||||
|
||||
void TilesEditorUtils::_thread() {
|
||||
pattern_thread_exited.clear();
|
||||
while (!pattern_thread_exit.is_set()) {
|
||||
pattern_preview_sem.wait();
|
||||
|
||||
pattern_preview_mutex.lock();
|
||||
if (pattern_preview_queue.size() == 0) {
|
||||
pattern_preview_mutex.unlock();
|
||||
} else {
|
||||
QueueItem item = pattern_preview_queue.front()->get();
|
||||
pattern_preview_queue.pop_front();
|
||||
pattern_preview_mutex.unlock();
|
||||
|
||||
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||
thumbnail_size *= EDSCALE;
|
||||
Vector2 thumbnail_size2 = Vector2(thumbnail_size, thumbnail_size);
|
||||
|
||||
if (item.pattern.is_valid() && !item.pattern->is_empty()) {
|
||||
// Generate the pattern preview
|
||||
SubViewport *viewport = memnew(SubViewport);
|
||||
viewport->set_size(thumbnail_size2);
|
||||
viewport->set_disable_input(true);
|
||||
viewport->set_transparent_background(true);
|
||||
viewport->set_update_mode(SubViewport::UPDATE_ONCE);
|
||||
|
||||
TileMap *tile_map = memnew(TileMap);
|
||||
tile_map->set_tileset(item.tile_set);
|
||||
tile_map->set_pattern(0, Vector2(), item.pattern);
|
||||
viewport->add_child(tile_map);
|
||||
|
||||
TypedArray<Vector2i> used_cells = tile_map->get_used_cells(0);
|
||||
|
||||
Rect2 encompassing_rect;
|
||||
encompassing_rect.set_position(tile_map->map_to_local(used_cells[0]));
|
||||
for (int i = 0; i < used_cells.size(); i++) {
|
||||
Vector2i cell = used_cells[i];
|
||||
Vector2 world_pos = tile_map->map_to_local(cell);
|
||||
encompassing_rect.expand_to(world_pos);
|
||||
|
||||
// Texture.
|
||||
Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map->get_cell_source_id(0, cell));
|
||||
if (atlas_source.is_valid()) {
|
||||
Vector2i coords = tile_map->get_cell_atlas_coords(0, cell);
|
||||
int alternative = tile_map->get_cell_alternative_tile(0, cell);
|
||||
|
||||
if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) {
|
||||
Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin();
|
||||
encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
|
||||
encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 scale = thumbnail_size2 / MAX(encompassing_rect.size.x, encompassing_rect.size.y);
|
||||
tile_map->set_scale(scale);
|
||||
tile_map->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
|
||||
|
||||
// Add the viewport at the last moment to avoid rendering too early.
|
||||
callable_mp((Node *)EditorNode::get_singleton(), &Node::add_child).call_deferred(viewport, false, Node::INTERNAL_MODE_DISABLED);
|
||||
|
||||
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_preview_frame_started), Object::CONNECT_ONE_SHOT);
|
||||
|
||||
pattern_preview_done.wait();
|
||||
|
||||
Ref<Image> image = viewport->get_texture()->get_image();
|
||||
|
||||
// Find the index for the given pattern. TODO: optimize.
|
||||
item.callback.call(item.pattern, ImageTexture::create_from_image(image));
|
||||
|
||||
viewport->queue_free();
|
||||
}
|
||||
}
|
||||
}
|
||||
pattern_thread_exited.set();
|
||||
}
|
||||
|
||||
void TilesEditorUtils::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
ERR_FAIL_COND(!p_pattern.is_valid());
|
||||
{
|
||||
MutexLock lock(pattern_preview_mutex);
|
||||
pattern_preview_queue.push_back({ p_tile_set, p_pattern, p_callback });
|
||||
}
|
||||
pattern_preview_sem.post();
|
||||
}
|
||||
|
||||
void TilesEditorUtils::set_sources_lists_current(int p_current) {
|
||||
atlas_sources_lists_current = p_current;
|
||||
}
|
||||
|
||||
void TilesEditorUtils::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
|
||||
ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
|
||||
MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
|
||||
ERR_FAIL_NULL(item_list);
|
||||
ERR_FAIL_NULL(sorting_button);
|
||||
|
||||
if (sorting_button->is_visible_in_tree()) {
|
||||
for (int i = 0; i != SOURCE_SORT_MAX; i++) {
|
||||
sorting_button->get_popup()->set_item_checked(i, (i == (int)source_sort));
|
||||
}
|
||||
}
|
||||
|
||||
if (item_list->is_visible_in_tree()) {
|
||||
// Make sure the selection is not overwritten after sorting.
|
||||
int atlas_sources_lists_current_mem = atlas_sources_lists_current;
|
||||
item_list->emit_signal(SNAME("sort_request"));
|
||||
atlas_sources_lists_current = atlas_sources_lists_current_mem;
|
||||
|
||||
if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
|
||||
item_list->deselect_all();
|
||||
} else {
|
||||
item_list->set_current(atlas_sources_lists_current);
|
||||
item_list->ensure_current_is_visible();
|
||||
item_list->emit_signal(SNAME("item_selected"), atlas_sources_lists_current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TilesEditorUtils::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
|
||||
atlas_view_zoom = p_zoom;
|
||||
atlas_view_scroll = p_scroll;
|
||||
}
|
||||
|
||||
void TilesEditorUtils::synchronize_atlas_view(Object *p_current) {
|
||||
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
|
||||
ERR_FAIL_NULL(tile_atlas_view);
|
||||
|
||||
if (tile_atlas_view->is_visible_in_tree()) {
|
||||
tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesEditorUtils::set_sorting_option(int p_option) {
|
||||
source_sort = p_option;
|
||||
}
|
||||
|
||||
List<int> TilesEditorUtils::get_sorted_sources(const Ref<TileSet> p_tile_set) const {
|
||||
SourceNameComparator::tile_set = p_tile_set;
|
||||
List<int> source_ids;
|
||||
|
||||
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
|
||||
source_ids.push_back(p_tile_set->get_source_id(i));
|
||||
}
|
||||
|
||||
switch (source_sort) {
|
||||
case SOURCE_SORT_ID_REVERSE:
|
||||
// Already sorted.
|
||||
source_ids.reverse();
|
||||
break;
|
||||
case SOURCE_SORT_NAME:
|
||||
source_ids.sort_custom<SourceNameComparator>();
|
||||
break;
|
||||
case SOURCE_SORT_NAME_REVERSE:
|
||||
source_ids.sort_custom<SourceNameComparator>();
|
||||
source_ids.reverse();
|
||||
break;
|
||||
default: // SOURCE_SORT_ID
|
||||
break;
|
||||
}
|
||||
|
||||
SourceNameComparator::tile_set.unref();
|
||||
return source_ids;
|
||||
}
|
||||
|
||||
Ref<TileSet> TilesEditorUtils::SourceNameComparator::tile_set;
|
||||
|
||||
bool TilesEditorUtils::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
|
||||
String name_a;
|
||||
String name_b;
|
||||
|
||||
{
|
||||
TileSetSource *source = *tile_set->get_source(p_a);
|
||||
|
||||
if (!source->get_name().is_empty()) {
|
||||
name_a = source->get_name();
|
||||
}
|
||||
|
||||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||
if (atlas_source) {
|
||||
Ref<Texture2D> texture = atlas_source->get_texture();
|
||||
if (name_a.is_empty() && texture.is_valid()) {
|
||||
name_a = texture->get_path().get_file();
|
||||
}
|
||||
}
|
||||
|
||||
if (name_a.is_empty()) {
|
||||
name_a = itos(p_a);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TileSetSource *source = *tile_set->get_source(p_b);
|
||||
|
||||
if (!source->get_name().is_empty()) {
|
||||
name_b = source->get_name();
|
||||
}
|
||||
|
||||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||
if (atlas_source) {
|
||||
Ref<Texture2D> texture = atlas_source->get_texture();
|
||||
if (name_b.is_empty() && texture.is_valid()) {
|
||||
name_b = texture->get_path().get_file();
|
||||
}
|
||||
}
|
||||
|
||||
if (name_b.is_empty()) {
|
||||
name_b = itos(p_b);
|
||||
}
|
||||
}
|
||||
|
||||
return NaturalNoCaseComparator()(name_a, name_b);
|
||||
}
|
||||
|
||||
void TilesEditorUtils::display_tile_set_editor_panel() {
|
||||
tile_map_plugin_singleton->hide_editor();
|
||||
tile_set_plugin_singleton->make_visible(true);
|
||||
}
|
||||
|
||||
void TilesEditorUtils::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
|
||||
Ref<Texture2D> selection_texture = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("TileSelection"), EditorStringName(EditorIcons));
|
||||
|
||||
real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
|
||||
p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
|
||||
RS::get_singleton()->canvas_item_add_nine_patch(
|
||||
p_ci->get_canvas_item(), Rect2(Vector2(), p_rect.size * scale), Rect2(), selection_texture->get_rid(),
|
||||
Vector2(2, 2), Vector2(2, 2), RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, false, p_color);
|
||||
p_ci->draw_set_transform_matrix(Transform2D());
|
||||
}
|
||||
|
||||
TilesEditorUtils::TilesEditorUtils() {
|
||||
singleton = this;
|
||||
// Pattern preview generation thread.
|
||||
pattern_preview_thread.start(_thread_func, this);
|
||||
}
|
||||
|
||||
TilesEditorUtils::~TilesEditorUtils() {
|
||||
if (pattern_preview_thread.is_started()) {
|
||||
pattern_thread_exit.set();
|
||||
pattern_preview_sem.post();
|
||||
while (!pattern_thread_exited.is_set()) {
|
||||
OS::get_singleton()->delay_usec(10000);
|
||||
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
|
||||
}
|
||||
pattern_preview_thread.wait_to_finish();
|
||||
}
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_tile_map_layer_changed() {
|
||||
if (tile_map_changed_needs_update) {
|
||||
return;
|
||||
}
|
||||
tile_map_changed_needs_update = true;
|
||||
callable_mp(this, &TileMapEditorPlugin::_update_tile_map).call_deferred();
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_tile_map_layer_removed() {
|
||||
// Workaround for TileMap, making sure the editor stays open when you delete the currently edited layer.
|
||||
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_group_id));
|
||||
if (tile_map) {
|
||||
edit(tile_map);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_update_tile_map() {
|
||||
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
|
||||
if (edited_layer) {
|
||||
Ref<TileSet> tile_set = edited_layer->get_effective_tile_set();
|
||||
if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) {
|
||||
tile_set_plugin_singleton->edit(tile_set.ptr());
|
||||
tile_set_plugin_singleton->make_visible(true);
|
||||
tile_set_id = tile_set->get_instance_id();
|
||||
} else if (tile_set.is_null()) {
|
||||
tile_set_plugin_singleton->edit(nullptr);
|
||||
tile_set_plugin_singleton->make_visible(false);
|
||||
tile_set_id = ObjectID();
|
||||
}
|
||||
}
|
||||
tile_map_changed_needs_update = false;
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_select_layer(const StringName &p_name) {
|
||||
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
|
||||
ERR_FAIL_NULL(edited_layer);
|
||||
|
||||
Node *parent = edited_layer->get_parent();
|
||||
ERR_FAIL_NULL(parent);
|
||||
|
||||
TileMapLayer *new_layer = Object::cast_to<TileMapLayer>(parent->get_node_or_null(String(p_name)));
|
||||
edit(new_layer);
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_edit_tile_map_layer(TileMapLayer *p_tile_map_layer) {
|
||||
ERR_FAIL_NULL(p_tile_map_layer);
|
||||
|
||||
editor->edit(p_tile_map_layer);
|
||||
|
||||
// Update the selected layers in the TileMapLayerGroup parent node.
|
||||
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(p_tile_map_layer->get_parent());
|
||||
if (tile_map_layer_group) {
|
||||
Vector<StringName> selected;
|
||||
selected.push_back(p_tile_map_layer->get_name());
|
||||
tile_map_layer_group->set_selected_layers(selected);
|
||||
}
|
||||
|
||||
// Update the object IDs.
|
||||
tile_map_layer_id = p_tile_map_layer->get_instance_id();
|
||||
p_tile_map_layer->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
|
||||
p_tile_map_layer->connect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
|
||||
if (tile_map_layer_group) {
|
||||
tile_map_group_id = tile_map_layer_group->get_instance_id();
|
||||
tile_map_layer_group->connect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
|
||||
tile_map_layer_group->connect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
|
||||
tile_map_layer_group->connect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
|
||||
}
|
||||
|
||||
// Update the edited tileset.
|
||||
Ref<TileSet> tile_set = p_tile_map_layer->get_effective_tile_set();
|
||||
if (tile_set.is_valid()) {
|
||||
tile_set_plugin_singleton->edit(tile_set.ptr());
|
||||
tile_set_plugin_singleton->make_visible(true);
|
||||
tile_set_id = tile_set->get_instance_id();
|
||||
} else {
|
||||
tile_set_plugin_singleton->edit(nullptr);
|
||||
tile_set_plugin_singleton->make_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group) {
|
||||
ERR_FAIL_NULL(p_tile_map_layer_group);
|
||||
|
||||
Vector<StringName> selected_layers = p_tile_map_layer_group->get_selected_layers();
|
||||
|
||||
TileMapLayer *selected_layer = nullptr;
|
||||
if (selected_layers.size() > 0) {
|
||||
// Edit the selected layer.
|
||||
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_node_or_null(String(selected_layers[0])));
|
||||
}
|
||||
if (!selected_layer) {
|
||||
// Edit the first layer found.
|
||||
for (int i = 0; i < p_tile_map_layer_group->get_child_count(); i++) {
|
||||
selected_layer = Object::cast_to<TileMapLayer>(p_tile_map_layer_group->get_child(i));
|
||||
if (selected_layer) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_layer) {
|
||||
_edit_tile_map_layer(selected_layer);
|
||||
} else {
|
||||
editor->edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::_notification(int p_notification) {
|
||||
if (p_notification == NOTIFICATION_EXIT_TREE) {
|
||||
get_tree()->queue_delete(TilesEditorUtils::get_singleton());
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::edit(Object *p_object) {
|
||||
TileMapLayer *edited_layer = Object::cast_to<TileMapLayer>(ObjectDB::get_instance(tile_map_layer_id));
|
||||
if (edited_layer) {
|
||||
edited_layer->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
|
||||
edited_layer->disconnect("tree_exited", callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
|
||||
}
|
||||
|
||||
TileMapLayerGroup *tile_map_group = Object::cast_to<TileMapLayerGroup>(ObjectDB::get_instance(tile_map_group_id));
|
||||
if (tile_map_group) {
|
||||
tile_map_group->disconnect("child_entered_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
|
||||
tile_map_group->disconnect("child_exiting_tree", callable_mp(editor, &TileMapLayerEditor::update_layers_selector).unbind(1));
|
||||
tile_map_group->disconnect("child_order_changed", callable_mp(editor, &TileMapLayerEditor::update_layers_selector));
|
||||
}
|
||||
|
||||
tile_map_group_id = ObjectID();
|
||||
tile_map_layer_id = ObjectID();
|
||||
tile_set_id = ObjectID();
|
||||
|
||||
TileMapLayerGroup *tile_map_layer_group = Object::cast_to<TileMap>(p_object);
|
||||
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_object);
|
||||
if (tile_map_layer_group) {
|
||||
_edit_tile_map_layer_group(tile_map_layer_group);
|
||||
} else if (tile_map_layer) {
|
||||
_edit_tile_map_layer(tile_map_layer);
|
||||
} else {
|
||||
// Deselect the layer in the group.
|
||||
if (edited_layer) {
|
||||
tile_map_layer_group = Object::cast_to<TileMapLayerGroup>(edited_layer->get_parent());
|
||||
if (tile_map_layer_group) {
|
||||
tile_map_layer_group->set_selected_layers(Vector<StringName>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMapEditorPlugin::handles(Object *p_object) const {
|
||||
return Object::cast_to<TileMapLayer>(p_object) != nullptr || Object::cast_to<TileMapLayerGroup>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
button->show();
|
||||
EditorNode::get_bottom_panel()->make_item_visible(editor);
|
||||
} else {
|
||||
button->hide();
|
||||
if (editor->is_visible_in_tree()) {
|
||||
EditorNode::get_bottom_panel()->hide_bottom_panel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMapEditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
|
||||
return editor->forward_canvas_gui_input(p_event);
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
editor->forward_canvas_draw_over_viewport(p_overlay);
|
||||
}
|
||||
|
||||
void TileMapEditorPlugin::hide_editor() {
|
||||
if (editor->is_visible_in_tree()) {
|
||||
EditorNode::get_bottom_panel()->hide_bottom_panel();
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMapEditorPlugin::is_editor_visible() const {
|
||||
return editor->is_visible_in_tree();
|
||||
}
|
||||
|
||||
TileMapEditorPlugin::TileMapEditorPlugin() {
|
||||
if (!TilesEditorUtils::get_singleton()) {
|
||||
memnew(TilesEditorUtils);
|
||||
}
|
||||
tile_map_plugin_singleton = this;
|
||||
|
||||
editor = memnew(TileMapLayerEditor);
|
||||
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
|
||||
editor->connect("change_selected_layer_request", callable_mp(this, &TileMapEditorPlugin::_select_layer));
|
||||
editor->hide();
|
||||
|
||||
button = EditorNode::get_bottom_panel()->add_item(TTR("TileMap"), editor);
|
||||
button->hide();
|
||||
}
|
||||
|
||||
TileMapEditorPlugin::~TileMapEditorPlugin() {
|
||||
tile_map_plugin_singleton = nullptr;
|
||||
}
|
||||
|
||||
void TileSetEditorPlugin::edit(Object *p_object) {
|
||||
editor->edit(Ref<TileSet>(p_object));
|
||||
if (p_object) {
|
||||
edited_tileset = p_object->get_instance_id();
|
||||
} else {
|
||||
edited_tileset = ObjectID();
|
||||
}
|
||||
}
|
||||
|
||||
bool TileSetEditorPlugin::handles(Object *p_object) const {
|
||||
return Object::cast_to<TileSet>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void TileSetEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
button->show();
|
||||
if (!tile_map_plugin_singleton->is_editor_visible()) {
|
||||
EditorNode::get_bottom_panel()->make_item_visible(editor);
|
||||
}
|
||||
} else {
|
||||
button->hide();
|
||||
if (editor->is_visible_in_tree()) {
|
||||
EditorNode::get_bottom_panel()->hide_bottom_panel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectID TileSetEditorPlugin::get_edited_tileset() const {
|
||||
return edited_tileset;
|
||||
}
|
||||
|
||||
TileSetEditorPlugin::TileSetEditorPlugin() {
|
||||
if (!TilesEditorUtils::get_singleton()) {
|
||||
memnew(TilesEditorUtils);
|
||||
}
|
||||
tile_set_plugin_singleton = this;
|
||||
|
||||
editor = memnew(TileSetEditor);
|
||||
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
|
||||
editor->hide();
|
||||
|
||||
button = EditorNode::get_bottom_panel()->add_item(TTR("TileSet"), editor);
|
||||
button->hide();
|
||||
}
|
||||
|
||||
TileSetEditorPlugin::~TileSetEditorPlugin() {
|
||||
tile_set_plugin_singleton = nullptr;
|
||||
}
|
169
modules/layered_tile_map/editor/tiles_editor_plugin.h
Normal file
169
modules/layered_tile_map/editor/tiles_editor_plugin.h
Normal file
@ -0,0 +1,169 @@
|
||||
/**************************************************************************/
|
||||
/* tiles_editor_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILES_EDITOR_PLUGIN_H
|
||||
#define TILES_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
#include "tile_atlas_view.h"
|
||||
#include "tile_map_layer_editor.h"
|
||||
#include "tile_set_editor.h"
|
||||
|
||||
class TilesEditorUtils : public Object {
|
||||
GDCLASS(TilesEditorUtils, Object);
|
||||
|
||||
static TilesEditorUtils *singleton;
|
||||
|
||||
public:
|
||||
enum SourceSortOption {
|
||||
SOURCE_SORT_ID = 0,
|
||||
SOURCE_SORT_ID_REVERSE,
|
||||
SOURCE_SORT_NAME,
|
||||
SOURCE_SORT_NAME_REVERSE,
|
||||
SOURCE_SORT_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
// For synchronization.
|
||||
int atlas_sources_lists_current = 0;
|
||||
float atlas_view_zoom = 1.0;
|
||||
Vector2 atlas_view_scroll;
|
||||
|
||||
// Source sorting.
|
||||
int source_sort = SOURCE_SORT_ID;
|
||||
|
||||
struct SourceNameComparator {
|
||||
static Ref<TileSet> tile_set;
|
||||
bool operator()(const int &p_a, const int &p_b) const;
|
||||
};
|
||||
|
||||
// Patterns preview generation.
|
||||
struct QueueItem {
|
||||
Ref<TileSet> tile_set;
|
||||
Ref<TileMapPattern> pattern;
|
||||
Callable callback;
|
||||
};
|
||||
List<QueueItem> pattern_preview_queue;
|
||||
Mutex pattern_preview_mutex;
|
||||
Semaphore pattern_preview_sem;
|
||||
Thread pattern_preview_thread;
|
||||
SafeFlag pattern_thread_exit;
|
||||
SafeFlag pattern_thread_exited;
|
||||
Semaphore pattern_preview_done;
|
||||
void _preview_frame_started();
|
||||
void _pattern_preview_done();
|
||||
static void _thread_func(void *ud);
|
||||
void _thread();
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ static TilesEditorUtils *get_singleton() { return singleton; }
|
||||
|
||||
// Pattern preview API.
|
||||
void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
|
||||
|
||||
// To synchronize the atlas sources lists.
|
||||
void set_sources_lists_current(int p_current);
|
||||
void synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button);
|
||||
|
||||
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
|
||||
void synchronize_atlas_view(Object *p_current);
|
||||
|
||||
// Sorting.
|
||||
void set_sorting_option(int p_option);
|
||||
List<int> get_sorted_sources(const Ref<TileSet> p_tile_set) const;
|
||||
|
||||
// Misc.
|
||||
void display_tile_set_editor_panel();
|
||||
|
||||
static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
|
||||
|
||||
TilesEditorUtils();
|
||||
~TilesEditorUtils();
|
||||
};
|
||||
|
||||
class TileMapEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(TileMapEditorPlugin, EditorPlugin);
|
||||
|
||||
TileMapLayerEditor *editor = nullptr;
|
||||
Button *button = nullptr;
|
||||
ObjectID tile_map_layer_id;
|
||||
ObjectID tile_map_group_id; // Allow keeping the layer selector up to date.
|
||||
|
||||
bool tile_map_changed_needs_update = false;
|
||||
ObjectID tile_set_id; // The TileSet associated with the TileMap.
|
||||
|
||||
void _tile_map_layer_changed();
|
||||
void _tile_map_layer_removed();
|
||||
void _update_tile_map();
|
||||
void _select_layer(const StringName &p_name);
|
||||
|
||||
void _edit_tile_map_layer(TileMapLayer *p_tile_map_layer);
|
||||
void _edit_tile_map_layer_group(TileMapLayerGroup *p_tile_map_layer_group);
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
|
||||
public:
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
|
||||
|
||||
void hide_editor();
|
||||
bool is_editor_visible() const;
|
||||
|
||||
TileMapEditorPlugin();
|
||||
~TileMapEditorPlugin();
|
||||
};
|
||||
|
||||
class TileSetEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(TileSetEditorPlugin, EditorPlugin);
|
||||
|
||||
TileSetEditor *editor = nullptr;
|
||||
Button *button = nullptr;
|
||||
|
||||
ObjectID edited_tileset;
|
||||
|
||||
public:
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
|
||||
ObjectID get_edited_tileset() const;
|
||||
|
||||
TileSetEditorPlugin();
|
||||
~TileSetEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // TILES_EDITOR_PLUGIN_H
|
@ -0,0 +1,176 @@
|
||||
/*************************************************************************/
|
||||
/* tilemap_navigation_geometry_parser_2d.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://github.com/Relintai/pandemonium_engine */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2022-present Péter Magyar. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "tilemap_navigation_geometry_parser_2d.h"
|
||||
|
||||
//#include "scene/2d/tile_map.h"
|
||||
|
||||
#include "modules/tile_map/tile_map.h"
|
||||
#include "modules/tile_map/tile_set.h"
|
||||
#include "scene/resources/navigation_2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/navigation_2d/navigation_polygon.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
bool TileMap2DNavigationGeometryParser2D::parses_node(Node *p_node) {
|
||||
return (Object::cast_to<TileMap>(p_node) != nullptr);
|
||||
}
|
||||
|
||||
void TileMap2DNavigationGeometryParser2D::parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry) {
|
||||
TileMap *tilemap = Object::cast_to<TileMap>(p_node);
|
||||
//NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_polygon->get_parsed_geometry_type();
|
||||
//uint32_t navigation_polygon_collision_mask = p_navigation_polygon->get_collision_mask();
|
||||
|
||||
if (tilemap) {
|
||||
Ref<TileSet> tile_set = tilemap->get_tileset();
|
||||
if (!tile_set.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Transform2D tilemap_xform = tilemap->get_transform();
|
||||
Array used_cells = tilemap->get_used_cells();
|
||||
|
||||
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
|
||||
Vector2 cell = used_cells[used_cell_index];
|
||||
|
||||
int cell_id = tilemap->get_cell(cell.x, cell.y);
|
||||
|
||||
Transform2D tile_transform;
|
||||
tile_transform.set_origin(tilemap->to_local(cell));
|
||||
|
||||
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
|
||||
|
||||
Ref<NavigationPolygon> navigation_polygon = tile_set->tile_get_navigation_polygon(cell_id);
|
||||
if (navigation_polygon.is_valid()) {
|
||||
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
|
||||
PoolVector<Vector2> traversable_outline = navigation_polygon->get_outline(outline_index);
|
||||
|
||||
Vector<Vector2> traversable_outline_new;
|
||||
traversable_outline_new.resize(traversable_outline.size());
|
||||
|
||||
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
|
||||
traversable_outline_new.write[traversable_outline_index] = tile_transform_offset.xform(traversable_outline[traversable_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry->_add_traversable_outline(traversable_outline_new);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO
|
||||
if (parsed_geometry_type != NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES && (tilemap->get_collision_layer() & navigation_polygon_collision_mask)) {
|
||||
for (int collision_polygon_index = 0; collision_polygon_index < tile_set->tile_get_shape_count(cell_id); collision_polygon_index++) {
|
||||
Vector<Vector2> obstruction_outline = tile_set->get_collision_polygon_points(collision_polygon_index);
|
||||
|
||||
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
|
||||
obstruction_outline.write[obstruction_outline_index] = tile_transform_offset.xform(obstruction_outline[obstruction_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry->_add_obstruction_outline(obstruction_outline);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMap2DNavigationGeometryParser2D::parses_node(Node *p_node) {
|
||||
return (Object::cast_to<TileMap>(p_node) != nullptr);
|
||||
}
|
||||
|
||||
void TileMap2DNavigationGeometryParser2D::parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry) {
|
||||
#ifdef CLIPPER_ENABLED
|
||||
TileMap *tilemap = Object::cast_to<TileMap>(p_node);
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_polygon->get_parsed_geometry_type();
|
||||
uint32_t navigation_polygon_collision_mask = p_navigation_polygon->get_collision_mask();
|
||||
|
||||
if (tilemap) {
|
||||
if (tilemap->get_layers_count() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tilemap_layer = 0; // only main tile map layer is supported
|
||||
|
||||
Ref<TileSet> tile_set = tilemap->get_tileset();
|
||||
if (!tile_set.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int physics_layers_count = tile_set->get_physics_layers_count();
|
||||
int navigation_layers_count = tile_set->get_navigation_layers_count();
|
||||
|
||||
if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Transform2D tilemap_xform = tilemap->get_transform();
|
||||
TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer);
|
||||
|
||||
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
|
||||
const Vector2i &cell = used_cells[used_cell_index];
|
||||
|
||||
const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false);
|
||||
|
||||
Transform2D tile_transform;
|
||||
tile_transform.set_origin(tilemap->map_to_local(cell));
|
||||
|
||||
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
|
||||
|
||||
if (navigation_layers_count > 0) {
|
||||
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer);
|
||||
if (navigation_polygon.is_valid()) {
|
||||
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
|
||||
Vector<Vector2> traversable_outline = navigation_polygon->get_outline(outline_index);
|
||||
|
||||
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
|
||||
traversable_outline.write[traversable_outline_index] = tile_transform_offset.xform(traversable_outline[traversable_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry->_add_traversable_outline(traversable_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (physics_layers_count > 0 && parsed_geometry_type != NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & navigation_polygon_collision_mask)) {
|
||||
for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) {
|
||||
Vector<Vector2> obstruction_outline = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index);
|
||||
|
||||
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
|
||||
obstruction_outline.write[obstruction_outline_index] = tile_transform_offset.xform(obstruction_outline[obstruction_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry->_add_obstruction_outline(obstruction_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // CLIPPER_ENABLED
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
#ifndef TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H
|
||||
#define TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H
|
||||
|
||||
/*************************************************************************/
|
||||
/* tilemap_navigation_geometry_parser_2d.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://github.com/Relintai/pandemonium_engine */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2022-present Péter Magyar. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "scene/2d/navigation_geometry_parser_2d.h"
|
||||
|
||||
class TileMap2DNavigationGeometryParser2D : public NavigationGeometryParser2D {
|
||||
public:
|
||||
virtual bool parses_node(Node *p_node);
|
||||
|
||||
virtual void parse_geometry(Node *p_node, Ref<NavigationPolygon> p_navigation_polygon, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry);
|
||||
};
|
||||
|
||||
#endif // TILEMAP_NAVIGATION_GEOMETRY_PARSER_2D_H
|
84
modules/layered_tile_map/register_types.cpp
Normal file
84
modules/layered_tile_map/register_types.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*************************************************************************/
|
||||
/* register_types.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://github.com/Relintai/pandemonium_engine */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2022-present Péter Magyar. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
/*
|
||||
Copyright (c) 2021 Péter Magyar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "register_types.h"
|
||||
|
||||
#include "geometry_parser/tilemap_navigation_geometry_parser_2d.h"
|
||||
#include "servers/navigation/navigation_mesh_generator.h"
|
||||
|
||||
#include "tile_map.h"
|
||||
#include "tile_set.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "tile_map_editor_plugin.h"
|
||||
#include "tile_set_editor_plugin.h"
|
||||
#endif
|
||||
|
||||
void register_tile_map_types(ModuleRegistrationLevel p_level) {
|
||||
if (p_level == MODULE_REGISTRATION_LEVEL_SCENE) {
|
||||
ClassDB::register_class<TileMap>();
|
||||
ClassDB::register_class<TileSet>();
|
||||
|
||||
NavigationMeshGenerator::get_singleton()->register_geometry_parser_2d(memnew(TileMap2DNavigationGeometryParser2D));
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_level == MODULE_REGISTRATION_LEVEL_EDITOR) {
|
||||
EditorPlugins::add_by_type<TileMapEditorPlugin>();
|
||||
EditorPlugins::add_by_type<TileSetEditorPlugin>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void unregister_tile_map_types(ModuleRegistrationLevel p_level) {
|
||||
}
|
62
modules/layered_tile_map/register_types.h
Normal file
62
modules/layered_tile_map/register_types.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef TILE_MAP_REGISTER_TYPES_H
|
||||
#define TILE_MAP_REGISTER_TYPES_H
|
||||
|
||||
/*************************************************************************/
|
||||
/* register_types.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://github.com/Relintai/pandemonium_engine */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2022-present Péter Magyar. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
/*
|
||||
Copyright (c) 2021 Péter Magyar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "modules/register_module_types.h"
|
||||
|
||||
void register_tile_map_types(ModuleRegistrationLevel p_level);
|
||||
void unregister_tile_map_types(ModuleRegistrationLevel p_level);
|
||||
|
||||
#endif
|
1003
modules/layered_tile_map/tile_map.cpp
Normal file
1003
modules/layered_tile_map/tile_map.cpp
Normal file
File diff suppressed because it is too large
Load Diff
227
modules/layered_tile_map/tile_map.h
Normal file
227
modules/layered_tile_map/tile_map.h
Normal file
@ -0,0 +1,227 @@
|
||||
/**************************************************************************/
|
||||
/* tile_map.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_MAP_H
|
||||
#define TILE_MAP_H
|
||||
|
||||
#include "scene/2d/tile_map_layer_group.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class Control;
|
||||
class TileMapLayer;
|
||||
class TerrainConstraint;
|
||||
|
||||
enum TileMapDataFormat {
|
||||
FORMAT_1 = 0,
|
||||
FORMAT_2,
|
||||
FORMAT_3,
|
||||
FORMAT_MAX,
|
||||
};
|
||||
|
||||
class TileMap : public TileMapLayerGroup {
|
||||
GDCLASS(TileMap, TileMapLayerGroup)
|
||||
|
||||
public:
|
||||
// Kept for compatibility, but should use TileMapLayer::VisibilityMode instead.
|
||||
enum VisibilityMode {
|
||||
VISIBILITY_MODE_DEFAULT,
|
||||
VISIBILITY_MODE_FORCE_SHOW,
|
||||
VISIBILITY_MODE_FORCE_HIDE,
|
||||
};
|
||||
|
||||
private:
|
||||
friend class TileSetPlugin;
|
||||
|
||||
// A compatibility enum to specify how is the data if formatted.
|
||||
mutable TileMapDataFormat format = TileMapDataFormat::FORMAT_3;
|
||||
|
||||
static constexpr float FP_ADJUST = 0.00001;
|
||||
|
||||
// Properties.
|
||||
int rendering_quadrant_size = 16;
|
||||
bool collision_animatable = false;
|
||||
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
|
||||
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
|
||||
|
||||
// Layers.
|
||||
LocalVector<TileMapLayer *> layers;
|
||||
TileMapLayer *default_layer; // Dummy layer to fetch default values.
|
||||
|
||||
// Transforms for collision_animatable.
|
||||
Transform2D last_valid_transform;
|
||||
Transform2D new_transform;
|
||||
|
||||
void _emit_changed();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
bool _property_can_revert(const StringName &p_name) const;
|
||||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
Rect2i _get_used_rect_bind_compat_78328();
|
||||
void _set_quadrant_size_compat_81070(int p_quadrant_size);
|
||||
int _get_quadrant_size_compat_81070() const;
|
||||
VisibilityMode _get_collision_visibility_mode_bind_compat_87115();
|
||||
VisibilityMode _get_navigation_visibility_mode_bind_compat_87115();
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void force_update(int p_layer);
|
||||
#endif
|
||||
|
||||
void set_rendering_quadrant_size(int p_size);
|
||||
int get_rendering_quadrant_size() const;
|
||||
|
||||
static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0);
|
||||
|
||||
// Layers management.
|
||||
int get_layers_count() const;
|
||||
void add_layer(int p_to_pos);
|
||||
void move_layer(int p_layer, int p_to_pos);
|
||||
void remove_layer(int p_layer);
|
||||
|
||||
void set_layer_name(int p_layer, String p_name);
|
||||
String get_layer_name(int p_layer) const;
|
||||
void set_layer_enabled(int p_layer, bool p_visible);
|
||||
bool is_layer_enabled(int p_layer) const;
|
||||
void set_layer_modulate(int p_layer, Color p_modulate);
|
||||
Color get_layer_modulate(int p_layer) const;
|
||||
void set_layer_y_sort_enabled(int p_layer, bool p_enabled);
|
||||
bool is_layer_y_sort_enabled(int p_layer) const;
|
||||
void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin);
|
||||
int get_layer_y_sort_origin(int p_layer) const;
|
||||
void set_layer_z_index(int p_layer, int p_z_index);
|
||||
int get_layer_z_index(int p_layer) const;
|
||||
void set_layer_navigation_enabled(int p_layer, bool p_enabled);
|
||||
bool is_layer_navigation_enabled(int p_layer) const;
|
||||
void set_layer_navigation_map(int p_layer, RID p_map);
|
||||
RID get_layer_navigation_map(int p_layer) const;
|
||||
|
||||
void set_collision_animatable(bool p_collision_animatable);
|
||||
bool is_collision_animatable() const;
|
||||
|
||||
// Debug visibility modes.
|
||||
void set_collision_visibility_mode(VisibilityMode p_show_collision);
|
||||
VisibilityMode get_collision_visibility_mode() const;
|
||||
|
||||
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
|
||||
VisibilityMode get_navigation_visibility_mode() const;
|
||||
|
||||
// Cells accessors.
|
||||
void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
|
||||
void erase_cell(int p_layer, const Vector2i &p_coords);
|
||||
int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
// Helper method to make accessing the data easier.
|
||||
TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
|
||||
// Patterns.
|
||||
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
|
||||
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
|
||||
void set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
|
||||
|
||||
// Terrains (Not exposed).
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints);
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true);
|
||||
|
||||
// Terrains (exposed).
|
||||
void set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
void set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
|
||||
// Not exposed to users.
|
||||
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
int get_effective_quadrant_size(int p_layer) const;
|
||||
|
||||
virtual void set_y_sort_enabled(bool p_enable) override;
|
||||
|
||||
Vector2 map_to_local(const Vector2i &p_pos) const;
|
||||
Vector2i local_to_map(const Vector2 &p_pos) const;
|
||||
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
|
||||
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
|
||||
|
||||
TypedArray<Vector2i> get_used_cells(int p_layer) const;
|
||||
TypedArray<Vector2i> get_used_cells_by_id(int p_layer, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
|
||||
Rect2i get_used_rect() const;
|
||||
|
||||
// Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems.
|
||||
virtual void set_light_mask(int p_light_mask) override;
|
||||
virtual void set_self_modulate(const Color &p_self_modulate) override;
|
||||
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
|
||||
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
|
||||
|
||||
// For finding tiles from collision.
|
||||
Vector2i get_coords_for_body_rid(RID p_physics_body);
|
||||
// For getting their layers as well.
|
||||
int get_layer_for_body_rid(RID p_physics_body);
|
||||
|
||||
// Fixing and clearing methods.
|
||||
void fix_invalid_tiles();
|
||||
|
||||
// Clears tiles from a given layer.
|
||||
void clear_layer(int p_layer);
|
||||
void clear();
|
||||
|
||||
// Force a TileMap update.
|
||||
void update_internals();
|
||||
void notify_runtime_tile_data_update(int p_layer = -1);
|
||||
|
||||
// Helpers?
|
||||
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords);
|
||||
|
||||
// Virtual function to modify the TileData at runtime.
|
||||
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
|
||||
GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *);
|
||||
|
||||
// Configuration warnings.
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
TileMap();
|
||||
~TileMap();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TileMap::VisibilityMode);
|
||||
|
||||
#endif // TILE_MAP_H
|
3090
modules/layered_tile_map/tile_map_layer.cpp
Normal file
3090
modules/layered_tile_map/tile_map_layer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
456
modules/layered_tile_map/tile_map_layer.h
Normal file
456
modules/layered_tile_map/tile_map_layer.h
Normal file
@ -0,0 +1,456 @@
|
||||
/**************************************************************************/
|
||||
/* tile_map_layer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_MAP_LAYER_H
|
||||
#define TILE_MAP_LAYER_H
|
||||
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class TileSetAtlasSource;
|
||||
|
||||
class TerrainConstraint {
|
||||
private:
|
||||
Ref<TileSet> tile_set;
|
||||
Vector2i base_cell_coords;
|
||||
int bit = -1;
|
||||
int terrain = -1;
|
||||
|
||||
int priority = 1;
|
||||
|
||||
public:
|
||||
bool operator<(const TerrainConstraint &p_other) const {
|
||||
if (base_cell_coords == p_other.base_cell_coords) {
|
||||
return bit < p_other.bit;
|
||||
}
|
||||
return base_cell_coords < p_other.base_cell_coords;
|
||||
}
|
||||
|
||||
String to_string() const {
|
||||
return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority);
|
||||
}
|
||||
|
||||
Vector2i get_base_cell_coords() const {
|
||||
return base_cell_coords;
|
||||
}
|
||||
|
||||
bool is_center_bit() const {
|
||||
return bit == 0;
|
||||
}
|
||||
|
||||
HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
|
||||
|
||||
void set_terrain(int p_terrain) {
|
||||
terrain = p_terrain;
|
||||
}
|
||||
|
||||
int get_terrain() const {
|
||||
return terrain;
|
||||
}
|
||||
|
||||
void set_priority(int p_priority) {
|
||||
priority = p_priority;
|
||||
}
|
||||
|
||||
int get_priority() const {
|
||||
return priority;
|
||||
}
|
||||
|
||||
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain); // For the center terrain bit
|
||||
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
|
||||
TerrainConstraint(){};
|
||||
};
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
class DebugQuadrant;
|
||||
#endif // DEBUG_ENABLED
|
||||
class RenderingQuadrant;
|
||||
|
||||
struct CellData {
|
||||
Vector2i coords;
|
||||
TileMapCell cell;
|
||||
|
||||
// Debug.
|
||||
SelfList<CellData> debug_quadrant_list_element;
|
||||
|
||||
// Rendering.
|
||||
Ref<RenderingQuadrant> rendering_quadrant;
|
||||
SelfList<CellData> rendering_quadrant_list_element;
|
||||
LocalVector<RID> occluders;
|
||||
|
||||
// Physics.
|
||||
LocalVector<RID> bodies;
|
||||
|
||||
// Navigation.
|
||||
LocalVector<RID> navigation_regions;
|
||||
|
||||
// Scenes.
|
||||
String scene;
|
||||
|
||||
// Runtime TileData cache.
|
||||
TileData *runtime_tile_data_cache = nullptr;
|
||||
|
||||
// List elements.
|
||||
SelfList<CellData> dirty_list_element;
|
||||
|
||||
bool operator<(const CellData &p_other) const {
|
||||
return coords < p_other.coords;
|
||||
}
|
||||
|
||||
// For those, copy everything but SelfList elements.
|
||||
void operator=(const CellData &p_other) {
|
||||
coords = p_other.coords;
|
||||
cell = p_other.cell;
|
||||
occluders = p_other.occluders;
|
||||
bodies = p_other.bodies;
|
||||
navigation_regions = p_other.navigation_regions;
|
||||
scene = p_other.scene;
|
||||
runtime_tile_data_cache = p_other.runtime_tile_data_cache;
|
||||
}
|
||||
|
||||
CellData(const CellData &p_other) :
|
||||
debug_quadrant_list_element(this),
|
||||
rendering_quadrant_list_element(this),
|
||||
dirty_list_element(this) {
|
||||
coords = p_other.coords;
|
||||
cell = p_other.cell;
|
||||
occluders = p_other.occluders;
|
||||
bodies = p_other.bodies;
|
||||
navigation_regions = p_other.navigation_regions;
|
||||
scene = p_other.scene;
|
||||
runtime_tile_data_cache = p_other.runtime_tile_data_cache;
|
||||
}
|
||||
|
||||
CellData() :
|
||||
debug_quadrant_list_element(this),
|
||||
rendering_quadrant_list_element(this),
|
||||
dirty_list_element(this) {
|
||||
}
|
||||
};
|
||||
|
||||
// For compatibility reasons, we use another comparator for Y-sorted layers.
|
||||
struct CellDataYSortedComparator {
|
||||
_FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const {
|
||||
return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
class DebugQuadrant : public RefCounted {
|
||||
GDCLASS(DebugQuadrant, RefCounted);
|
||||
|
||||
public:
|
||||
Vector2i quadrant_coords;
|
||||
SelfList<CellData>::List cells;
|
||||
RID canvas_item;
|
||||
|
||||
SelfList<DebugQuadrant> dirty_quadrant_list_element;
|
||||
|
||||
DebugQuadrant() :
|
||||
dirty_quadrant_list_element(this) {
|
||||
}
|
||||
|
||||
~DebugQuadrant() {
|
||||
cells.clear();
|
||||
}
|
||||
};
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
class RenderingQuadrant : public RefCounted {
|
||||
GDCLASS(RenderingQuadrant, RefCounted);
|
||||
|
||||
public:
|
||||
struct CoordsWorldComparator {
|
||||
_ALWAYS_INLINE_ bool operator()(const Vector2 &p_a, const Vector2 &p_b) const {
|
||||
// We sort the cells by their local coords, as it is needed by rendering.
|
||||
if (p_a.y == p_b.y) {
|
||||
return p_a.x > p_b.x;
|
||||
} else {
|
||||
return p_a.y < p_b.y;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Vector2i quadrant_coords;
|
||||
SelfList<CellData>::List cells;
|
||||
List<RID> canvas_items;
|
||||
Vector2 canvas_items_position;
|
||||
|
||||
SelfList<RenderingQuadrant> dirty_quadrant_list_element;
|
||||
|
||||
RenderingQuadrant() :
|
||||
dirty_quadrant_list_element(this) {
|
||||
}
|
||||
|
||||
~RenderingQuadrant() {
|
||||
cells.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class TileMapLayer : public Node2D {
|
||||
GDCLASS(TileMapLayer, Node2D);
|
||||
|
||||
public:
|
||||
enum VisibilityMode {
|
||||
VISIBILITY_MODE_DEFAULT,
|
||||
VISIBILITY_MODE_FORCE_SHOW,
|
||||
VISIBILITY_MODE_FORCE_HIDE,
|
||||
};
|
||||
|
||||
enum DirtyFlags {
|
||||
DIRTY_FLAGS_LAYER_ENABLED = 0,
|
||||
DIRTY_FLAGS_LAYER_IN_TREE,
|
||||
DIRTY_FLAGS_LAYER_IN_CANVAS,
|
||||
DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM,
|
||||
DIRTY_FLAGS_LAYER_VISIBILITY,
|
||||
DIRTY_FLAGS_LAYER_SELF_MODULATE,
|
||||
DIRTY_FLAGS_LAYER_Y_SORT_ENABLED,
|
||||
DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN,
|
||||
DIRTY_FLAGS_LAYER_Z_INDEX,
|
||||
DIRTY_FLAGS_LAYER_LIGHT_MASK,
|
||||
DIRTY_FLAGS_LAYER_TEXTURE_FILTER,
|
||||
DIRTY_FLAGS_LAYER_TEXTURE_REPEAT,
|
||||
DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE,
|
||||
DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES,
|
||||
DIRTY_FLAGS_LAYER_COLLISION_VISIBILITY_MODE,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_MAP,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_VISIBILITY_MODE,
|
||||
DIRTY_FLAGS_LAYER_RUNTIME_UPDATE,
|
||||
|
||||
DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE, // For compatibility.
|
||||
|
||||
DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS,
|
||||
DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED,
|
||||
DIRTY_FLAGS_LAYER_GROUP_TILE_SET,
|
||||
|
||||
DIRTY_FLAGS_MAX,
|
||||
};
|
||||
|
||||
private:
|
||||
// Exposed properties.
|
||||
bool enabled = true;
|
||||
int y_sort_origin = 0;
|
||||
int rendering_quadrant_size = 16;
|
||||
|
||||
bool use_kinematic_bodies = false;
|
||||
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
|
||||
|
||||
bool navigation_enabled = true;
|
||||
RID navigation_map_override;
|
||||
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
|
||||
|
||||
// Internal.
|
||||
HashMap<Vector2i, CellData> tile_map;
|
||||
bool pending_update = false;
|
||||
|
||||
// For keeping compatibility with TileMap.
|
||||
TileMap *tile_map_node = nullptr;
|
||||
int layer_index_in_tile_map_node = -1;
|
||||
|
||||
// Dirty flag. Allows knowing what was modified since the last update.
|
||||
struct {
|
||||
bool flags[DIRTY_FLAGS_MAX] = { false };
|
||||
SelfList<CellData>::List cell_list;
|
||||
} dirty;
|
||||
bool in_destructor = false;
|
||||
|
||||
// Rect cache.
|
||||
mutable Rect2 rect_cache;
|
||||
mutable bool rect_cache_dirty = true;
|
||||
mutable Rect2i used_rect_cache;
|
||||
mutable bool used_rect_cache_dirty = true;
|
||||
|
||||
// Runtime tile data.
|
||||
bool _runtime_update_tile_data_was_cleaned_up = false;
|
||||
void _build_runtime_update_tile_data();
|
||||
void _build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_use_tilemap_for_runtime, bool p_auto_add_to_dirty_list = false);
|
||||
bool _runtime_update_needs_all_cells_cleaned_up = false;
|
||||
void _clear_runtime_update_tile_data();
|
||||
void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data);
|
||||
|
||||
// Per-system methods.
|
||||
#ifdef DEBUG_ENABLED
|
||||
HashMap<Vector2i, Ref<DebugQuadrant>> debug_quadrant_map;
|
||||
Vector2i _coords_to_debug_quadrant_coords(const Vector2i &p_coords) const;
|
||||
bool _debug_was_cleaned_up = false;
|
||||
void _debug_update();
|
||||
void _debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list);
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map;
|
||||
bool _rendering_was_cleaned_up = false;
|
||||
void _rendering_update();
|
||||
void _rendering_notification(int p_what);
|
||||
void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list);
|
||||
void _rendering_occluders_clear_cell(CellData &r_cell_data);
|
||||
void _rendering_occluders_update_cell(CellData &r_cell_data);
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords.
|
||||
bool _physics_was_cleaned_up = false;
|
||||
void _physics_update();
|
||||
void _physics_notification(int p_what);
|
||||
void _physics_clear_cell(CellData &r_cell_data);
|
||||
void _physics_update_cell(CellData &r_cell_data);
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _physics_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
bool _navigation_was_cleaned_up = false;
|
||||
void _navigation_update();
|
||||
void _navigation_notification(int p_what);
|
||||
void _navigation_clear_cell(CellData &r_cell_data);
|
||||
void _navigation_update_cell(CellData &r_cell_data);
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
bool _scenes_was_cleaned_up = false;
|
||||
void _scenes_update();
|
||||
void _scenes_clear_cell(CellData &r_cell_data);
|
||||
void _scenes_update_cell(CellData &r_cell_data);
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data);
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
// Terrains.
|
||||
TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const;
|
||||
RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
|
||||
RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
|
||||
|
||||
void _renamed();
|
||||
void _update_notify_local_transform();
|
||||
|
||||
// Internal updates.
|
||||
void _queue_internal_update();
|
||||
void _deferred_internal_update();
|
||||
void _internal_update();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// TileMap node.
|
||||
void set_as_tile_map_internal_node(int p_index);
|
||||
|
||||
// Rect caching.
|
||||
Rect2 get_rect(bool &r_changed) const;
|
||||
|
||||
// Terrains.
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) const; // Not exposed.
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true) const; // Not exposed.
|
||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true) const; // Not exposed.
|
||||
|
||||
// Not exposed to users.
|
||||
TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
|
||||
// For TileMap node's use.
|
||||
void set_tile_data(TileMapDataFormat p_format, const Vector<int> &p_data);
|
||||
Vector<int> get_tile_data() const;
|
||||
void notify_tile_map_layer_group_change(DirtyFlags p_what);
|
||||
|
||||
void update_internals();
|
||||
void notify_runtime_tile_data_update();
|
||||
|
||||
// --- Exposed in TileMap ---
|
||||
// Cells manipulation.
|
||||
void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
|
||||
void erase_cell(const Vector2i &p_coords);
|
||||
|
||||
int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier.
|
||||
void clear();
|
||||
|
||||
// Patterns.
|
||||
Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
|
||||
void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
|
||||
|
||||
// Terrains.
|
||||
void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||
|
||||
// Cells usage.
|
||||
TypedArray<Vector2i> get_used_cells() const;
|
||||
TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
|
||||
Rect2i get_used_rect() const;
|
||||
|
||||
// Layer properties.
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const;
|
||||
virtual void set_self_modulate(const Color &p_self_modulate) override;
|
||||
virtual void set_y_sort_enabled(bool p_y_sort_enabled) override;
|
||||
void set_y_sort_origin(int p_y_sort_origin);
|
||||
int get_y_sort_origin() const;
|
||||
virtual void set_z_index(int p_z_index) override;
|
||||
virtual void set_light_mask(int p_light_mask) override;
|
||||
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
|
||||
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
|
||||
void set_rendering_quadrant_size(int p_size);
|
||||
int get_rendering_quadrant_size() const;
|
||||
|
||||
void set_use_kinematic_bodies(bool p_use_kinematic_bodies);
|
||||
bool is_using_kinematic_bodies() const;
|
||||
void set_collision_visibility_mode(VisibilityMode p_show_collision);
|
||||
VisibilityMode get_collision_visibility_mode() const;
|
||||
|
||||
void set_navigation_enabled(bool p_enabled);
|
||||
bool is_navigation_enabled() const;
|
||||
void set_navigation_map(RID p_map);
|
||||
RID get_navigation_map() const;
|
||||
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
|
||||
VisibilityMode get_navigation_visibility_mode() const;
|
||||
|
||||
// Fixing and clearing methods.
|
||||
void fix_invalid_tiles();
|
||||
|
||||
// Find coords for body.
|
||||
bool has_body_rid(RID p_physics_body) const;
|
||||
Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision.
|
||||
|
||||
// Helper.
|
||||
Ref<TileSet> get_effective_tile_set() const;
|
||||
|
||||
// Virtual function to modify the TileData at runtime.
|
||||
GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i);
|
||||
GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *);
|
||||
// ---
|
||||
|
||||
TileMapLayer();
|
||||
~TileMapLayer();
|
||||
};
|
||||
|
||||
#endif // TILE_MAP_LAYER_H
|
148
modules/layered_tile_map/tile_map_layer_group.cpp
Normal file
148
modules/layered_tile_map/tile_map_layer_group.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/**************************************************************************/
|
||||
/* tile_map_layer_group.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tile_map_layer_group.h"
|
||||
|
||||
#include "core/core_string_names.h"
|
||||
#include "scene/2d/tile_map_layer.h"
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
void TileMapLayerGroup::_cleanup_selected_layers() {
|
||||
for (int i = 0; i < selected_layers.size(); i++) {
|
||||
const String name = selected_layers[i];
|
||||
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_node_or_null(name));
|
||||
if (!layer) {
|
||||
selected_layers.remove_at(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void TileMapLayerGroup::_tile_set_changed() {
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
|
||||
if (layer) {
|
||||
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
|
||||
}
|
||||
}
|
||||
|
||||
update_configuration_warnings();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
void TileMapLayerGroup::set_selected_layers(Vector<StringName> p_layer_names) {
|
||||
selected_layers = p_layer_names;
|
||||
_cleanup_selected_layers();
|
||||
|
||||
// Update the layers modulation.
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
|
||||
if (layer) {
|
||||
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<StringName> TileMapLayerGroup::get_selected_layers() const {
|
||||
return selected_layers;
|
||||
}
|
||||
|
||||
void TileMapLayerGroup::set_highlight_selected_layer(bool p_highlight_selected_layer) {
|
||||
if (highlight_selected_layer == p_highlight_selected_layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
highlight_selected_layer = p_highlight_selected_layer;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
|
||||
if (layer) {
|
||||
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TileMapLayerGroup::is_highlighting_selected_layer() const {
|
||||
return highlight_selected_layer;
|
||||
}
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void TileMapLayerGroup::remove_child_notify(Node *p_child) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
_cleanup_selected_layers();
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
void TileMapLayerGroup::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMapLayerGroup::set_tileset);
|
||||
ClassDB::bind_method(D_METHOD("get_tileset"), &TileMapLayerGroup::get_tileset);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
|
||||
}
|
||||
|
||||
void TileMapLayerGroup::set_tileset(const Ref<TileSet> &p_tileset) {
|
||||
if (p_tileset == tile_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the tileset, registering to its changes.
|
||||
if (tile_set.is_valid()) {
|
||||
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
|
||||
}
|
||||
|
||||
tile_set = p_tileset;
|
||||
|
||||
if (tile_set.is_valid()) {
|
||||
tile_set->connect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
|
||||
}
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
TileMapLayer *layer = Object::cast_to<TileMapLayer>(get_child(i));
|
||||
if (layer) {
|
||||
layer->notify_tile_map_layer_group_change(TileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<TileSet> TileMapLayerGroup::get_tileset() const {
|
||||
return tile_set;
|
||||
}
|
||||
|
||||
TileMapLayerGroup::~TileMapLayerGroup() {
|
||||
if (tile_set.is_valid()) {
|
||||
tile_set->disconnect_changed(callable_mp(this, &TileMapLayerGroup::_tile_set_changed));
|
||||
}
|
||||
}
|
73
modules/layered_tile_map/tile_map_layer_group.h
Normal file
73
modules/layered_tile_map/tile_map_layer_group.h
Normal file
@ -0,0 +1,73 @@
|
||||
/**************************************************************************/
|
||||
/* tile_map_layer_group.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_MAP_LAYER_GROUP_H
|
||||
#define TILE_MAP_LAYER_GROUP_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class TileSet;
|
||||
|
||||
class TileMapLayerGroup : public Node2D {
|
||||
GDCLASS(TileMapLayerGroup, Node2D);
|
||||
|
||||
private:
|
||||
mutable Vector<StringName> selected_layers;
|
||||
bool highlight_selected_layer = true;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void _cleanup_selected_layers();
|
||||
#endif
|
||||
void _tile_set_changed();
|
||||
|
||||
protected:
|
||||
Ref<TileSet> tile_set;
|
||||
|
||||
virtual void remove_child_notify(Node *p_child) override;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
// For editor use.
|
||||
void set_selected_layers(Vector<StringName> p_layer_names);
|
||||
Vector<StringName> get_selected_layers() const;
|
||||
void set_highlight_selected_layer(bool p_highlight_selected_layer);
|
||||
bool is_highlighting_selected_layer() const;
|
||||
#endif
|
||||
|
||||
// Accessors.
|
||||
void set_tileset(const Ref<TileSet> &p_tileset);
|
||||
Ref<TileSet> get_tileset() const;
|
||||
|
||||
~TileMapLayerGroup();
|
||||
};
|
||||
|
||||
#endif // TILE_MAP_LAYER_GROUP_H
|
6927
modules/layered_tile_map/tile_set.cpp
Normal file
6927
modules/layered_tile_map/tile_set.cpp
Normal file
File diff suppressed because it is too large
Load Diff
987
modules/layered_tile_map/tile_set.h
Normal file
987
modules/layered_tile_map/tile_set.h
Normal file
@ -0,0 +1,987 @@
|
||||
/**************************************************************************/
|
||||
/* tile_set.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TILE_SET_H
|
||||
#define TILE_SET_H
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rb_set.h"
|
||||
#include "scene/2d/light_occluder_2d.h"
|
||||
#include "scene/main/canvas_item.h"
|
||||
#include "scene/resources/2d/convex_polygon_shape_2d.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "scene/resources/navigation_polygon.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/resources/physics_material.h"
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "scene/resources/shader.h"
|
||||
#endif
|
||||
|
||||
class TileMap;
|
||||
class TileSetSource;
|
||||
class TileSetAtlasSource;
|
||||
class TileData;
|
||||
|
||||
// Forward-declare the plugins.
|
||||
class TileSetPlugin;
|
||||
class TileSetPluginAtlasRendering;
|
||||
class TileSetPluginAtlasPhysics;
|
||||
class TileSetPluginAtlasNavigation;
|
||||
|
||||
union TileMapCell {
|
||||
struct {
|
||||
int16_t source_id;
|
||||
int16_t coord_x;
|
||||
int16_t coord_y;
|
||||
int16_t alternative_tile;
|
||||
};
|
||||
|
||||
uint64_t _u64t;
|
||||
|
||||
static uint32_t hash(const TileMapCell &p_hash) {
|
||||
return hash_one_uint64(p_hash._u64t);
|
||||
}
|
||||
|
||||
TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE
|
||||
source_id = p_source_id;
|
||||
set_atlas_coords(p_atlas_coords);
|
||||
alternative_tile = p_alternative_tile;
|
||||
}
|
||||
|
||||
Vector2i get_atlas_coords() const {
|
||||
return Vector2i(coord_x, coord_y);
|
||||
}
|
||||
|
||||
void set_atlas_coords(const Vector2i &r_coords) {
|
||||
coord_x = r_coords.x;
|
||||
coord_y = r_coords.y;
|
||||
}
|
||||
|
||||
bool operator<(const TileMapCell &p_other) const {
|
||||
if (source_id == p_other.source_id) {
|
||||
if (coord_x == p_other.coord_x) {
|
||||
if (coord_y == p_other.coord_y) {
|
||||
return alternative_tile < p_other.alternative_tile;
|
||||
} else {
|
||||
return coord_y < p_other.coord_y;
|
||||
}
|
||||
} else {
|
||||
return coord_x < p_other.coord_x;
|
||||
}
|
||||
} else {
|
||||
return source_id < p_other.source_id;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const TileMapCell &p_other) const {
|
||||
return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile);
|
||||
}
|
||||
bool operator==(const TileMapCell &p_other) const {
|
||||
return source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile;
|
||||
}
|
||||
};
|
||||
|
||||
class TileMapPattern : public Resource {
|
||||
GDCLASS(TileMapPattern, Resource);
|
||||
|
||||
Size2i size;
|
||||
HashMap<Vector2i, TileMapCell> pattern;
|
||||
|
||||
void _set_tile_data(const Vector<int> &p_data);
|
||||
Vector<int> _get_tile_data() const;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0);
|
||||
bool has_cell(const Vector2i &p_coords) const;
|
||||
void remove_cell(const Vector2i &p_coords, bool p_update_size = true);
|
||||
int get_cell_source_id(const Vector2i &p_coords) const;
|
||||
Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
|
||||
int get_cell_alternative_tile(const Vector2i &p_coords) const;
|
||||
|
||||
const HashMap<Vector2i, TileMapCell> &get_pattern() const { return pattern; }
|
||||
TypedArray<Vector2i> get_used_cells() const;
|
||||
|
||||
Size2i get_size() const;
|
||||
void set_size(const Size2i &p_size);
|
||||
bool is_empty() const;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
class TileSet : public Resource {
|
||||
GDCLASS(TileSet, Resource);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
private:
|
||||
struct CompatibilityShapeData {
|
||||
Vector2i autotile_coords;
|
||||
bool one_way;
|
||||
float one_way_margin;
|
||||
Ref<Shape2D> shape;
|
||||
Transform2D transform;
|
||||
};
|
||||
|
||||
struct CompatibilityTileData {
|
||||
String name;
|
||||
Ref<Texture2D> texture;
|
||||
Vector2 tex_offset;
|
||||
Ref<Material> material;
|
||||
Rect2 region;
|
||||
int tile_mode = 0;
|
||||
Color modulate = Color(1, 1, 1);
|
||||
|
||||
// Atlas or autotiles data
|
||||
int autotile_bitmask_mode = 0;
|
||||
Vector2 autotile_icon_coordinate;
|
||||
Size2i autotile_tile_size = Size2i(16, 16);
|
||||
|
||||
int autotile_spacing = 0;
|
||||
HashMap<Vector2i, int> autotile_bitmask_flags;
|
||||
HashMap<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
|
||||
HashMap<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
|
||||
HashMap<Vector2i, int> autotile_priority_map;
|
||||
HashMap<Vector2i, int> autotile_z_index_map;
|
||||
|
||||
Vector<CompatibilityShapeData> shapes;
|
||||
Ref<OccluderPolygon2D> occluder;
|
||||
Vector2 occluder_offset;
|
||||
Ref<NavigationPolygon> navigation;
|
||||
Vector2 navigation_offset;
|
||||
int z_index = 0;
|
||||
};
|
||||
|
||||
enum CompatibilityTileMode {
|
||||
COMPATIBILITY_TILE_MODE_SINGLE_TILE = 0,
|
||||
COMPATIBILITY_TILE_MODE_AUTO_TILE,
|
||||
COMPATIBILITY_TILE_MODE_ATLAS_TILE,
|
||||
};
|
||||
|
||||
HashMap<int, CompatibilityTileData *> compatibility_data;
|
||||
HashMap<int, int> compatibility_tilemap_mapping_tile_modes;
|
||||
HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping;
|
||||
HashMap<Vector2i, int> compatibility_size_count;
|
||||
|
||||
void _compatibility_conversion();
|
||||
|
||||
public:
|
||||
// Format of output array [source_id, atlas_coords, alternative]
|
||||
Array compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
static const int INVALID_SOURCE; // -1;
|
||||
|
||||
enum CellNeighbor {
|
||||
CELL_NEIGHBOR_RIGHT_SIDE = 0,
|
||||
CELL_NEIGHBOR_RIGHT_CORNER,
|
||||
CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
|
||||
CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
|
||||
CELL_NEIGHBOR_BOTTOM_SIDE,
|
||||
CELL_NEIGHBOR_BOTTOM_CORNER,
|
||||
CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
|
||||
CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
|
||||
CELL_NEIGHBOR_LEFT_SIDE,
|
||||
CELL_NEIGHBOR_LEFT_CORNER,
|
||||
CELL_NEIGHBOR_TOP_LEFT_SIDE,
|
||||
CELL_NEIGHBOR_TOP_LEFT_CORNER,
|
||||
CELL_NEIGHBOR_TOP_SIDE,
|
||||
CELL_NEIGHBOR_TOP_CORNER,
|
||||
CELL_NEIGHBOR_TOP_RIGHT_SIDE,
|
||||
CELL_NEIGHBOR_TOP_RIGHT_CORNER,
|
||||
CELL_NEIGHBOR_MAX,
|
||||
};
|
||||
|
||||
static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[];
|
||||
|
||||
enum TerrainMode {
|
||||
TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0,
|
||||
TERRAIN_MODE_MATCH_CORNERS,
|
||||
TERRAIN_MODE_MATCH_SIDES,
|
||||
};
|
||||
|
||||
enum TileShape {
|
||||
TILE_SHAPE_SQUARE,
|
||||
TILE_SHAPE_ISOMETRIC,
|
||||
TILE_SHAPE_HALF_OFFSET_SQUARE,
|
||||
TILE_SHAPE_HEXAGON,
|
||||
};
|
||||
|
||||
enum TileLayout {
|
||||
TILE_LAYOUT_STACKED,
|
||||
TILE_LAYOUT_STACKED_OFFSET,
|
||||
TILE_LAYOUT_STAIRS_RIGHT,
|
||||
TILE_LAYOUT_STAIRS_DOWN,
|
||||
TILE_LAYOUT_DIAMOND_RIGHT,
|
||||
TILE_LAYOUT_DIAMOND_DOWN,
|
||||
};
|
||||
|
||||
enum TileOffsetAxis {
|
||||
TILE_OFFSET_AXIS_HORIZONTAL,
|
||||
TILE_OFFSET_AXIS_VERTICAL,
|
||||
};
|
||||
|
||||
struct PackedSceneSource {
|
||||
Ref<PackedScene> scene;
|
||||
Vector2 offset;
|
||||
};
|
||||
|
||||
class TerrainsPattern {
|
||||
bool valid = false;
|
||||
int terrain = -1;
|
||||
int bits[TileSet::CELL_NEIGHBOR_MAX];
|
||||
bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
|
||||
|
||||
int not_empty_terrains_count = 0;
|
||||
|
||||
public:
|
||||
bool is_valid() const;
|
||||
bool is_erase_pattern() const;
|
||||
|
||||
bool operator<(const TerrainsPattern &p_terrains_pattern) const;
|
||||
bool operator==(const TerrainsPattern &p_terrains_pattern) const;
|
||||
bool operator!=(const TerrainsPattern &p_terrains_pattern) const {
|
||||
return !operator==(p_terrains_pattern);
|
||||
};
|
||||
|
||||
void set_terrain(int p_terrain);
|
||||
int get_terrain() const;
|
||||
|
||||
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain);
|
||||
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||
|
||||
void from_array(Array p_terrains);
|
||||
Array as_array() const;
|
||||
|
||||
TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
|
||||
TerrainsPattern() {}
|
||||
};
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
private:
|
||||
// --- TileSet data ---
|
||||
// Basic shape and layout.
|
||||
TileShape tile_shape = TILE_SHAPE_SQUARE;
|
||||
TileLayout tile_layout = TILE_LAYOUT_STACKED;
|
||||
TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL;
|
||||
Size2i tile_size = Size2i(16, 16); //Size2(64, 64);
|
||||
|
||||
// Rendering.
|
||||
bool uv_clipping = false;
|
||||
struct OcclusionLayer {
|
||||
uint32_t light_mask = 1;
|
||||
bool sdf_collision = false;
|
||||
};
|
||||
Vector<OcclusionLayer> occlusion_layers;
|
||||
|
||||
Ref<ArrayMesh> tile_lines_mesh;
|
||||
Ref<ArrayMesh> tile_filled_mesh;
|
||||
mutable bool tile_meshes_dirty = true;
|
||||
|
||||
// Physics
|
||||
struct PhysicsLayer {
|
||||
uint32_t collision_layer = 1;
|
||||
uint32_t collision_mask = 1;
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
};
|
||||
Vector<PhysicsLayer> physics_layers;
|
||||
|
||||
// Terrains
|
||||
struct Terrain {
|
||||
String name;
|
||||
Color color;
|
||||
};
|
||||
struct TerrainSet {
|
||||
TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES;
|
||||
Vector<Terrain> terrains;
|
||||
};
|
||||
Vector<TerrainSet> terrain_sets;
|
||||
|
||||
HashMap<TerrainMode, Ref<ArrayMesh>> terrain_meshes;
|
||||
HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_peering_bits_meshes;
|
||||
bool terrain_bits_meshes_dirty = true;
|
||||
|
||||
LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
|
||||
bool terrains_cache_dirty = true;
|
||||
void _update_terrains_cache();
|
||||
|
||||
// Navigation
|
||||
struct NavigationLayer {
|
||||
uint32_t layers = 1;
|
||||
};
|
||||
Vector<NavigationLayer> navigation_layers;
|
||||
|
||||
// CustomData
|
||||
struct CustomDataLayer {
|
||||
String name;
|
||||
Variant::Type type = Variant::NIL;
|
||||
};
|
||||
Vector<CustomDataLayer> custom_data_layers;
|
||||
HashMap<String, int> custom_data_layers_by_name;
|
||||
|
||||
// Per Atlas source data.
|
||||
HashMap<int, Ref<TileSetSource>> sources;
|
||||
Vector<int> source_ids;
|
||||
int next_source_id = 0;
|
||||
// ---------------------
|
||||
|
||||
LocalVector<Ref<TileMapPattern>> patterns;
|
||||
|
||||
void _compute_next_source_id();
|
||||
void _source_changed();
|
||||
|
||||
// Tile proxies
|
||||
RBMap<int, int> source_level_proxies;
|
||||
RBMap<Array, Array> coords_level_proxies;
|
||||
RBMap<Array, Array> alternative_level_proxies;
|
||||
|
||||
// Helpers
|
||||
Vector<Point2> _get_square_terrain_polygon(Vector2i p_size);
|
||||
Vector<Point2> _get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_square_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
|
||||
Vector<Point2> _get_isometric_terrain_polygon(Vector2i p_size);
|
||||
Vector<Point2> _get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_isometric_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||
|
||||
Vector<Point2> _get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
|
||||
Vector<Point2> _get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||
Vector<Point2> _get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// --- Plugins ---
|
||||
Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const;
|
||||
|
||||
// --- Accessors for TileSet data ---
|
||||
|
||||
// -- Shape and layout --
|
||||
void set_tile_shape(TileShape p_shape);
|
||||
TileShape get_tile_shape() const;
|
||||
void set_tile_layout(TileLayout p_layout);
|
||||
TileLayout get_tile_layout() const;
|
||||
void set_tile_offset_axis(TileOffsetAxis p_alignment);
|
||||
TileOffsetAxis get_tile_offset_axis() const;
|
||||
void set_tile_size(Size2i p_size);
|
||||
Size2i get_tile_size() const;
|
||||
|
||||
// -- Sources management --
|
||||
int get_next_source_id() const;
|
||||
int get_source_count() const;
|
||||
int get_source_id(int p_index) const;
|
||||
int add_source(Ref<TileSetSource> p_tile_set_source, int p_source_id_override = -1);
|
||||
void set_source_id(int p_source_id, int p_new_id);
|
||||
void remove_source(int p_source_id);
|
||||
void remove_source_ptr(TileSetSource *p_tile_set_source); // Not exposed
|
||||
bool has_source(int p_source_id) const;
|
||||
Ref<TileSetSource> get_source(int p_source_id) const;
|
||||
|
||||
// Rendering
|
||||
void set_uv_clipping(bool p_uv_clipping);
|
||||
bool is_uv_clipping() const;
|
||||
|
||||
int get_occlusion_layers_count() const;
|
||||
void add_occlusion_layer(int p_index = -1);
|
||||
void move_occlusion_layer(int p_from_index, int p_to_pos);
|
||||
void remove_occlusion_layer(int p_index);
|
||||
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
|
||||
int get_occlusion_layer_light_mask(int p_layer_index) const;
|
||||
void set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision);
|
||||
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
|
||||
|
||||
// Physics
|
||||
int get_physics_layers_count() const;
|
||||
void add_physics_layer(int p_index = -1);
|
||||
void move_physics_layer(int p_from_index, int p_to_pos);
|
||||
void remove_physics_layer(int p_index);
|
||||
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
|
||||
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
|
||||
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
|
||||
uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
|
||||
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
|
||||
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
|
||||
|
||||
// Terrain sets
|
||||
int get_terrain_sets_count() const;
|
||||
void add_terrain_set(int p_index = -1);
|
||||
void move_terrain_set(int p_from_index, int p_to_pos);
|
||||
void remove_terrain_set(int p_index);
|
||||
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
|
||||
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
|
||||
|
||||
// Terrains
|
||||
int get_terrains_count(int p_terrain_set) const;
|
||||
void add_terrain(int p_terrain_set, int p_index = -1);
|
||||
void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
|
||||
void remove_terrain(int p_terrain_set, int p_index);
|
||||
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
|
||||
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
|
||||
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
|
||||
Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
|
||||
bool is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const;
|
||||
bool is_valid_terrain_peering_bit(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
|
||||
|
||||
// Navigation
|
||||
int get_navigation_layers_count() const;
|
||||
void add_navigation_layer(int p_index = -1);
|
||||
void move_navigation_layer(int p_from_index, int p_to_pos);
|
||||
void remove_navigation_layer(int p_index);
|
||||
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
|
||||
uint32_t get_navigation_layer_layers(int p_layer_index) const;
|
||||
void set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value);
|
||||
bool get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const;
|
||||
|
||||
// Custom data
|
||||
int get_custom_data_layers_count() const;
|
||||
void add_custom_data_layer(int p_index = -1);
|
||||
void move_custom_data_layer(int p_from_index, int p_to_pos);
|
||||
void remove_custom_data_layer(int p_index);
|
||||
int get_custom_data_layer_by_name(String p_value) const;
|
||||
void set_custom_data_layer_name(int p_layer_id, String p_value);
|
||||
String get_custom_data_layer_name(int p_layer_id) const;
|
||||
void set_custom_data_layer_type(int p_layer_id, Variant::Type p_value);
|
||||
Variant::Type get_custom_data_layer_type(int p_layer_id) const;
|
||||
|
||||
// Tiles proxies.
|
||||
void set_source_level_tile_proxy(int p_source_from, int p_source_to);
|
||||
int get_source_level_tile_proxy(int p_source_from);
|
||||
bool has_source_level_tile_proxy(int p_source_from);
|
||||
void remove_source_level_tile_proxy(int p_source_from);
|
||||
|
||||
void set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to);
|
||||
Array get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
|
||||
bool has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
|
||||
void remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
|
||||
|
||||
void set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to);
|
||||
Array get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
|
||||
bool has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
|
||||
void remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
|
||||
|
||||
Array get_source_level_tile_proxies() const;
|
||||
Array get_coords_level_tile_proxies() const;
|
||||
Array get_alternative_level_tile_proxies() const;
|
||||
|
||||
Array map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const;
|
||||
|
||||
void cleanup_invalid_tile_proxies();
|
||||
void clear_tile_proxies();
|
||||
|
||||
// Patterns.
|
||||
int add_pattern(Ref<TileMapPattern> p_pattern, int p_index = -1);
|
||||
Ref<TileMapPattern> get_pattern(int p_index);
|
||||
void remove_pattern(int p_index);
|
||||
int get_patterns_count();
|
||||
|
||||
// Terrains.
|
||||
RBSet<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
|
||||
RBSet<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
|
||||
TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
|
||||
|
||||
// Helpers
|
||||
Vector<Vector2> get_tile_shape_polygon() const;
|
||||
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()) const;
|
||||
|
||||
// Used by TileMap/TileMapLayer
|
||||
Vector2 map_to_local(const Vector2i &p_pos) const;
|
||||
Vector2i local_to_map(const Vector2 &p_pos) const;
|
||||
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
|
||||
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
|
||||
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &p_coords) const;
|
||||
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) const;
|
||||
void draw_cells_outline(CanvasItem *p_canvas_item, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D()) const;
|
||||
|
||||
Vector<Point2> get_terrain_polygon(int p_terrain_set);
|
||||
Vector<Point2> get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
|
||||
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
|
||||
Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size);
|
||||
|
||||
// Resource management
|
||||
virtual void reset_state() override;
|
||||
|
||||
// Helpers.
|
||||
static Vector2i transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
|
||||
|
||||
TileSet();
|
||||
~TileSet();
|
||||
};
|
||||
|
||||
class TileSetSource : public Resource {
|
||||
GDCLASS(TileSetSource, Resource);
|
||||
|
||||
protected:
|
||||
const TileSet *tile_set = nullptr;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
|
||||
static const int INVALID_TILE_ALTERNATIVE; // -1;
|
||||
|
||||
// Not exposed.
|
||||
virtual void set_tile_set(const TileSet *p_tile_set);
|
||||
TileSet *get_tile_set() const;
|
||||
virtual void notify_tile_data_properties_should_change(){};
|
||||
virtual void add_occlusion_layer(int p_index){};
|
||||
virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
|
||||
virtual void remove_occlusion_layer(int p_index){};
|
||||
virtual void add_physics_layer(int p_index){};
|
||||
virtual void move_physics_layer(int p_from_index, int p_to_pos){};
|
||||
virtual void remove_physics_layer(int p_index){};
|
||||
virtual void add_terrain_set(int p_index){};
|
||||
virtual void move_terrain_set(int p_from_index, int p_to_pos){};
|
||||
virtual void remove_terrain_set(int p_index){};
|
||||
virtual void add_terrain(int p_terrain_set, int p_index){};
|
||||
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
|
||||
virtual void remove_terrain(int p_terrain_set, int p_index){};
|
||||
virtual void add_navigation_layer(int p_index){};
|
||||
virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
|
||||
virtual void remove_navigation_layer(int p_index){};
|
||||
virtual void add_custom_data_layer(int p_index){};
|
||||
virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
|
||||
virtual void remove_custom_data_layer(int p_index){};
|
||||
virtual void reset_state() override;
|
||||
|
||||
// Tiles.
|
||||
virtual int get_tiles_count() const = 0;
|
||||
virtual Vector2i get_tile_id(int tile_index) const = 0;
|
||||
virtual bool has_tile(Vector2i p_atlas_coords) const = 0;
|
||||
|
||||
// Alternative tiles.
|
||||
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0;
|
||||
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0;
|
||||
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0;
|
||||
};
|
||||
|
||||
class TileSetAtlasSource : public TileSetSource {
|
||||
GDCLASS(TileSetAtlasSource, TileSetSource);
|
||||
|
||||
public:
|
||||
enum TileAnimationMode {
|
||||
TILE_ANIMATION_MODE_DEFAULT,
|
||||
TILE_ANIMATION_MODE_RANDOM_START_TIMES,
|
||||
TILE_ANIMATION_MODE_MAX,
|
||||
};
|
||||
|
||||
enum TransformBits {
|
||||
TRANSFORM_FLIP_H = 1 << 12,
|
||||
TRANSFORM_FLIP_V = 1 << 13,
|
||||
TRANSFORM_TRANSPOSE = 1 << 14,
|
||||
};
|
||||
|
||||
private:
|
||||
struct TileAlternativesData {
|
||||
Vector2i size_in_atlas = Vector2i(1, 1);
|
||||
Vector2i texture_offset;
|
||||
|
||||
// Animation
|
||||
int animation_columns = 0;
|
||||
Vector2i animation_separation;
|
||||
real_t animation_speed = 1.0;
|
||||
TileSetAtlasSource::TileAnimationMode animation_mode = TILE_ANIMATION_MODE_DEFAULT;
|
||||
LocalVector<real_t> animation_frames_durations;
|
||||
|
||||
// Alternatives
|
||||
HashMap<int, TileData *> alternatives;
|
||||
Vector<int> alternatives_ids;
|
||||
int next_alternative_id = 1;
|
||||
};
|
||||
|
||||
Ref<Texture2D> texture;
|
||||
Vector2i margins;
|
||||
Vector2i separation;
|
||||
Size2i texture_region_size = Size2i(16, 16);
|
||||
|
||||
HashMap<Vector2i, TileAlternativesData> tiles;
|
||||
Vector<Vector2i> tiles_ids;
|
||||
HashMap<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
|
||||
|
||||
TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
|
||||
const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
|
||||
|
||||
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
|
||||
|
||||
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
|
||||
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
|
||||
|
||||
bool use_texture_padding = true;
|
||||
Ref<CanvasTexture> padded_texture;
|
||||
bool padded_texture_needs_update = false;
|
||||
void _queue_update_padded_texture();
|
||||
Ref<ImageTexture> _create_padded_image_texture(const Ref<Texture2D> &p_source);
|
||||
void _update_padded_texture();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// Not exposed.
|
||||
virtual void set_tile_set(const TileSet *p_tile_set) override;
|
||||
const TileSet *get_tile_set() const;
|
||||
virtual void notify_tile_data_properties_should_change() override;
|
||||
virtual void add_occlusion_layer(int p_index) override;
|
||||
virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_occlusion_layer(int p_index) override;
|
||||
virtual void add_physics_layer(int p_index) override;
|
||||
virtual void move_physics_layer(int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_physics_layer(int p_index) override;
|
||||
virtual void add_terrain_set(int p_index) override;
|
||||
virtual void move_terrain_set(int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_terrain_set(int p_index) override;
|
||||
virtual void add_terrain(int p_terrain_set, int p_index) override;
|
||||
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_terrain(int p_terrain_set, int p_index) override;
|
||||
virtual void add_navigation_layer(int p_index) override;
|
||||
virtual void move_navigation_layer(int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_navigation_layer(int p_index) override;
|
||||
virtual void add_custom_data_layer(int p_index) override;
|
||||
virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override;
|
||||
virtual void remove_custom_data_layer(int p_index) override;
|
||||
virtual void reset_state() override;
|
||||
|
||||
// Base properties.
|
||||
void set_texture(Ref<Texture2D> p_texture);
|
||||
Ref<Texture2D> get_texture() const;
|
||||
void set_margins(Vector2i p_margins);
|
||||
Vector2i get_margins() const;
|
||||
void set_separation(Vector2i p_separation);
|
||||
Vector2i get_separation() const;
|
||||
void set_texture_region_size(Vector2i p_tile_size);
|
||||
Vector2i get_texture_region_size() const;
|
||||
|
||||
// Padding.
|
||||
void set_use_texture_padding(bool p_use_padding);
|
||||
bool get_use_texture_padding() const;
|
||||
|
||||
// Base tiles.
|
||||
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
|
||||
void remove_tile(Vector2i p_atlas_coords);
|
||||
virtual bool has_tile(Vector2i p_atlas_coords) const override;
|
||||
void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
|
||||
Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
|
||||
|
||||
virtual int get_tiles_count() const override;
|
||||
virtual Vector2i get_tile_id(int p_index) const override;
|
||||
|
||||
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
|
||||
PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
|
||||
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
|
||||
|
||||
bool has_tiles_outside_texture() const;
|
||||
Vector<Vector2i> get_tiles_outside_texture() const;
|
||||
void clear_tiles_outside_texture();
|
||||
|
||||
// Animation.
|
||||
void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns);
|
||||
int get_tile_animation_columns(const Vector2i p_atlas_coords) const;
|
||||
void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation);
|
||||
Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const;
|
||||
void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed);
|
||||
real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const;
|
||||
void set_tile_animation_mode(const Vector2i p_atlas_coords, const TileSetAtlasSource::TileAnimationMode p_mode);
|
||||
TileSetAtlasSource::TileAnimationMode get_tile_animation_mode(const Vector2i p_atlas_coords) const;
|
||||
void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count);
|
||||
int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const;
|
||||
void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration);
|
||||
real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const;
|
||||
real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const;
|
||||
|
||||
// Alternative tiles.
|
||||
int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
|
||||
void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
|
||||
void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id);
|
||||
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
|
||||
int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const;
|
||||
|
||||
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
|
||||
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
|
||||
|
||||
// Get data associated to a tile.
|
||||
TileData *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
|
||||
|
||||
// Helpers.
|
||||
Vector2i get_atlas_grid_size() const;
|
||||
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
||||
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
|
||||
|
||||
static int alternative_no_transform(int p_alternative_id);
|
||||
|
||||
// Getters for texture and tile region (padded or not)
|
||||
Ref<Texture2D> get_runtime_texture() const;
|
||||
Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
||||
|
||||
~TileSetAtlasSource();
|
||||
};
|
||||
|
||||
class TileSetScenesCollectionSource : public TileSetSource {
|
||||
GDCLASS(TileSetScenesCollectionSource, TileSetSource);
|
||||
|
||||
private:
|
||||
struct SceneData {
|
||||
Ref<PackedScene> scene;
|
||||
bool display_placeholder = false;
|
||||
};
|
||||
Vector<int> scenes_ids;
|
||||
HashMap<int, SceneData> scenes;
|
||||
int next_scene_id = 1;
|
||||
|
||||
void _compute_next_alternative_id();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
// Tiles.
|
||||
int get_tiles_count() const override;
|
||||
Vector2i get_tile_id(int p_tile_index) const override;
|
||||
bool has_tile(Vector2i p_atlas_coords) const override;
|
||||
|
||||
// Alternative tiles.
|
||||
int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
|
||||
int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
|
||||
bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
|
||||
|
||||
// Scenes accessors. Lot are similar to "Alternative tiles".
|
||||
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
|
||||
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
|
||||
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
|
||||
int create_scene_tile(Ref<PackedScene> p_packed_scene = Ref<PackedScene>(), int p_id_override = -1);
|
||||
void set_scene_tile_id(int p_id, int p_new_id);
|
||||
void set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene);
|
||||
Ref<PackedScene> get_scene_tile_scene(int p_id) const;
|
||||
void set_scene_tile_display_placeholder(int p_id, bool p_packed_scene);
|
||||
bool get_scene_tile_display_placeholder(int p_id) const;
|
||||
void remove_scene_tile(int p_id);
|
||||
int get_next_scene_tile_id() const;
|
||||
};
|
||||
|
||||
class TileData : public Object {
|
||||
GDCLASS(TileData, Object);
|
||||
|
||||
private:
|
||||
const TileSet *tile_set = nullptr;
|
||||
bool allow_transform = true;
|
||||
|
||||
// Rendering
|
||||
bool flip_h = false;
|
||||
bool flip_v = false;
|
||||
bool transpose = false;
|
||||
Vector2i texture_origin;
|
||||
Ref<Material> material = Ref<Material>();
|
||||
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
|
||||
int z_index = 0;
|
||||
int y_sort_origin = 0;
|
||||
struct OcclusionLayerTileData {
|
||||
Ref<OccluderPolygon2D> occluder;
|
||||
mutable HashMap<int, Ref<OccluderPolygon2D>> transformed_occluders;
|
||||
};
|
||||
Vector<OcclusionLayerTileData> occluders;
|
||||
|
||||
// Physics
|
||||
struct PhysicsLayerTileData {
|
||||
struct PolygonShapeTileData {
|
||||
LocalVector<Vector2> polygon;
|
||||
LocalVector<Ref<ConvexPolygonShape2D>> shapes;
|
||||
mutable HashMap<int, LocalVector<Ref<ConvexPolygonShape2D>>> transformed_shapes;
|
||||
bool one_way = false;
|
||||
float one_way_margin = 1.0;
|
||||
};
|
||||
|
||||
Vector2 linear_velocity;
|
||||
double angular_velocity = 0.0;
|
||||
Vector<PolygonShapeTileData> polygons;
|
||||
};
|
||||
Vector<PhysicsLayerTileData> physics;
|
||||
// TODO add support for areas.
|
||||
|
||||
// Terrain
|
||||
int terrain_set = -1;
|
||||
int terrain = -1;
|
||||
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
// Navigation
|
||||
struct NavigationLayerTileData {
|
||||
Ref<NavigationPolygon> navigation_polygon;
|
||||
mutable HashMap<int, Ref<NavigationPolygon>> transformed_navigation_polygon;
|
||||
};
|
||||
Vector<NavigationLayerTileData> navigation;
|
||||
|
||||
// Misc
|
||||
double probability = 1.0;
|
||||
|
||||
// Custom data
|
||||
Vector<Variant> custom_data;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
Ref<NavigationPolygon> _get_navigation_polygon_bind_compat_84660(int p_layer_id) const;
|
||||
Ref<OccluderPolygon2D> _get_occluder_bind_compat_84660(int p_layer_id) const;
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Not exposed.
|
||||
void set_tile_set(const TileSet *p_tile_set);
|
||||
void notify_tile_data_properties_should_change();
|
||||
void add_occlusion_layer(int p_index);
|
||||
void move_occlusion_layer(int p_from_index, int p_to_pos);
|
||||
void remove_occlusion_layer(int p_index);
|
||||
void add_physics_layer(int p_index);
|
||||
void move_physics_layer(int p_from_index, int p_to_pos);
|
||||
void remove_physics_layer(int p_index);
|
||||
void add_terrain_set(int p_index);
|
||||
void move_terrain_set(int p_from_index, int p_to_pos);
|
||||
void remove_terrain_set(int p_index);
|
||||
void add_terrain(int p_terrain_set, int p_index);
|
||||
void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
|
||||
void remove_terrain(int p_terrain_set, int p_index);
|
||||
void add_navigation_layer(int p_index);
|
||||
void move_navigation_layer(int p_from_index, int p_to_pos);
|
||||
void remove_navigation_layer(int p_index);
|
||||
void add_custom_data_layer(int p_index);
|
||||
void move_custom_data_layer(int p_from_index, int p_to_pos);
|
||||
void remove_custom_data_layer(int p_index);
|
||||
void set_allow_transform(bool p_allow_transform);
|
||||
bool is_allowing_transform() const;
|
||||
|
||||
// To duplicate a TileData object, needed for runtiume update.
|
||||
TileData *duplicate();
|
||||
|
||||
// Rendering
|
||||
void set_flip_h(bool p_flip_h);
|
||||
bool get_flip_h() const;
|
||||
void set_flip_v(bool p_flip_v);
|
||||
bool get_flip_v() const;
|
||||
void set_transpose(bool p_transpose);
|
||||
bool get_transpose() const;
|
||||
|
||||
void set_texture_origin(Vector2i p_texture_origin);
|
||||
Vector2i get_texture_origin() const;
|
||||
void set_material(Ref<Material> p_material);
|
||||
Ref<Material> get_material() const;
|
||||
void set_modulate(Color p_modulate);
|
||||
Color get_modulate() const;
|
||||
void set_z_index(int p_z_index);
|
||||
int get_z_index() const;
|
||||
void set_y_sort_origin(int p_y_sort_origin);
|
||||
int get_y_sort_origin() const;
|
||||
|
||||
void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
|
||||
Ref<OccluderPolygon2D> get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
|
||||
|
||||
// Physics
|
||||
void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
|
||||
Vector2 get_constant_linear_velocity(int p_layer_id) const;
|
||||
void set_constant_angular_velocity(int p_layer_id, real_t p_velocity);
|
||||
real_t get_constant_angular_velocity(int p_layer_id) const;
|
||||
void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
|
||||
int get_collision_polygons_count(int p_layer_id) const;
|
||||
void add_collision_polygon(int p_layer_id);
|
||||
void remove_collision_polygon(int p_layer_id, int p_polygon_index);
|
||||
void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);
|
||||
Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const;
|
||||
void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way);
|
||||
bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const;
|
||||
void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin);
|
||||
float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const;
|
||||
int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const;
|
||||
Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
|
||||
|
||||
// Terrain
|
||||
void set_terrain_set(int p_terrain_id);
|
||||
int get_terrain_set() const;
|
||||
void set_terrain(int p_terrain_id);
|
||||
int get_terrain() const;
|
||||
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
|
||||
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||
bool is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||
|
||||
TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
|
||||
|
||||
// Navigation
|
||||
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
|
||||
Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
|
||||
|
||||
// Misc
|
||||
void set_probability(float p_probability);
|
||||
float get_probability() const;
|
||||
|
||||
// Custom data.
|
||||
void set_custom_data(String p_layer_name, Variant p_value);
|
||||
Variant get_custom_data(String p_layer_name) const;
|
||||
void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
|
||||
Variant get_custom_data_by_layer_id(int p_layer_id) const;
|
||||
|
||||
// Polygons.
|
||||
static PackedVector2Array get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TileSet::CellNeighbor);
|
||||
VARIANT_ENUM_CAST(TileSet::TerrainMode);
|
||||
VARIANT_ENUM_CAST(TileSet::TileShape);
|
||||
VARIANT_ENUM_CAST(TileSet::TileLayout);
|
||||
VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
|
||||
|
||||
VARIANT_ENUM_CAST(TileSetAtlasSource::TileAnimationMode);
|
||||
|
||||
#endif // TILE_SET_H
|
Loading…
Reference in New Issue
Block a user