summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/basis.cpp2
-rw-r--r--core/math/math_funcs.h34
-rw-r--r--core/math/quat.cpp2
-rw-r--r--core/math/vector2.cpp2
-rw-r--r--core/math/vector3.h2
-rw-r--r--core/variant/variant_internal.h17
-rw-r--r--doc/classes/JavaScript.xml27
-rw-r--r--doc/classes/JavaScriptObject.xml42
-rw-r--r--doc/classes/RigidBody3D.xml2
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--doc/classes/TileSetScenesCollectionSource.xml155
-rw-r--r--drivers/dummy/rasterizer_dummy.h1
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp38
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp333
-rw-r--r--editor/plugins/tiles/tile_map_editor.h38
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp98
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h23
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp109
-rw-r--r--editor/plugins/tiles/tile_set_editor.h6
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp511
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.h139
-rw-r--r--editor/project_manager.cpp22
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml3
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml4
-rw-r--r--modules/opensimplex/noise_texture.cpp18
-rw-r--r--modules/opensimplex/noise_texture.h4
-rw-r--r--modules/opensimplex/open_simplex_noise.cpp6
-rw-r--r--modules/opensimplex/open_simplex_noise.h2
-rw-r--r--platform/javascript/SCsub4
-rw-r--r--platform/javascript/api/api.cpp34
-rw-r--r--platform/javascript/api/javascript_singleton.h (renamed from platform/javascript/api/javascript_eval.h)22
-rw-r--r--platform/javascript/javascript_eval.cpp79
-rw-r--r--platform/javascript/javascript_singleton.cpp338
-rw-r--r--platform/javascript/js/libs/library_godot_eval.js86
-rw-r--r--platform/javascript/js/libs/library_godot_javascript_singleton.js333
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp21
-rw-r--r--platform/osx/os_osx.mm24
-rw-r--r--platform/windows/os_windows.cpp26
-rw-r--r--scene/2d/tile_map.cpp22
-rw-r--r--scene/2d/tile_map.h11
-rw-r--r--scene/3d/physics_body_3d.cpp2
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/tile_set.cpp466
-rw-r--r--scene/resources/tile_set.h92
-rw-r--r--servers/text_server.cpp2
-rw-r--r--tests/test_color.h6
-rw-r--r--tests/test_curve.h28
-rw-r--r--tests/test_expression.h24
-rw-r--r--tests/test_json.h4
-rw-r--r--tests/test_rect2.h10
54 files changed, 2756 insertions, 531 deletions
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 50299902eb..037378b9d7 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -109,7 +109,7 @@ bool Basis::is_diagonal() const {
}
bool Basis::is_rotation() const {
- return Math::is_equal_approx(determinant(), 1, UNIT_EPSILON) && is_orthogonal();
+ return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal();
}
#ifdef MATH_CHECKS
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 575c3f8902..0a3b6ae9da 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -311,20 +311,20 @@ public:
static float random(float from, float to);
static int random(int from, int to);
- static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
+ static _ALWAYS_INLINE_ bool is_equal_approx(float a, float b) {
// Check for exact equality first, required to handle "infinity" values.
if (a == b) {
return true;
}
// Then check for approximate equality.
- real_t tolerance = CMP_EPSILON * abs(a);
+ float tolerance = CMP_EPSILON * abs(a);
if (tolerance < CMP_EPSILON) {
tolerance = CMP_EPSILON;
}
return abs(a - b) < tolerance;
}
- static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) {
+ static _ALWAYS_INLINE_ bool is_equal_approx(float a, float b, float tolerance) {
// Check for exact equality first, required to handle "infinity" values.
if (a == b) {
return true;
@@ -333,7 +333,33 @@ public:
return abs(a - b) < tolerance;
}
- static _ALWAYS_INLINE_ bool is_zero_approx(real_t s) {
+ static _ALWAYS_INLINE_ bool is_zero_approx(float s) {
+ return abs(s) < CMP_EPSILON;
+ }
+
+ static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b) {
+ // Check for exact equality first, required to handle "infinity" values.
+ if (a == b) {
+ return true;
+ }
+ // Then check for approximate equality.
+ double tolerance = CMP_EPSILON * abs(a);
+ if (tolerance < CMP_EPSILON) {
+ tolerance = CMP_EPSILON;
+ }
+ return abs(a - b) < tolerance;
+ }
+
+ static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b, double tolerance) {
+ // Check for exact equality first, required to handle "infinity" values.
+ if (a == b) {
+ return true;
+ }
+ // Then check for approximate equality.
+ return abs(a - b) < tolerance;
+ }
+
+ static _ALWAYS_INLINE_ bool is_zero_approx(double s) {
return abs(s) < CMP_EPSILON;
}
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 6f13e04027..3982a0b993 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -87,7 +87,7 @@ Quat Quat::normalized() const {
}
bool Quat::is_normalized() const {
- return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON); //use less epsilon
+ return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon
}
Quat Quat::inverse() const {
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 5129ed336e..46a08b53ab 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -59,7 +59,7 @@ Vector2 Vector2::normalized() const {
bool Vector2::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
- return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
+ return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON);
}
real_t Vector2::distance_to(const Vector2 &p_vector2) const {
diff --git a/core/math/vector3.h b/core/math/vector3.h
index b47c3cc916..adfc52566f 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -423,7 +423,7 @@ Vector3 Vector3::normalized() const {
bool Vector3::is_normalized() const {
// use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
- return Math::is_equal_approx(length_squared(), 1.0, UNIT_EPSILON);
+ return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON);
}
Vector3 Vector3::inverse() const {
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 7d33d85cd6..fb791f8c0c 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -43,18 +43,21 @@ public:
v->type = p_type;
switch (p_type) {
- case Variant::AABB:
- init_aabb(v);
+ case Variant::STRING:
+ init_string(v);
break;
case Variant::TRANSFORM2D:
init_transform2d(v);
break;
+ case Variant::AABB:
+ init_aabb(v);
+ break;
+ case Variant::BASIS:
+ init_basis(v);
+ break;
case Variant::TRANSFORM:
init_transform(v);
break;
- case Variant::STRING:
- init_string(v);
- break;
case Variant::STRING_NAME:
init_string_name(v);
break;
@@ -192,6 +195,10 @@ public:
v->type = GetTypeInfo<T>::VARIANT_TYPE;
}
+ // Should be in the same order as Variant::Type for consistency.
+ // Those primitive and vector types don't need an `init_` method:
+ // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, Color, RID.
+ // Object is a special case, handled via `object_assign_null`.
_FORCE_INLINE_ static void init_string(Variant *v) {
memnew_placement(v->_data._mem, String);
v->type = Variant::STRING;
diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml
index c707a72ee8..e6d74eeb21 100644
--- a/doc/classes/JavaScript.xml
+++ b/doc/classes/JavaScript.xml
@@ -11,6 +11,24 @@
<link title="Exporting for the Web: Calling JavaScript from script">https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link>
</tutorials>
<methods>
+ <method name="create_callback">
+ <return type="JavaScriptObject">
+ </return>
+ <argument index="0" name="callable" type="Callable">
+ </argument>
+ <description>
+ Creates a reference to a [Callable] that can be used as a callback by JavaScript. The reference must be kept until the callback happens, or it won't be called at all. See [JavaScriptObject] for usage.
+ </description>
+ </method>
+ <method name="create_object" qualifiers="vararg">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="object" type="String">
+ </argument>
+ <description>
+ Creates a new JavaScript object using the [code]new[/code] constructor. The [code]object[/code] must a valid property of the JavaScript [code]window[/code]. See [JavaScriptObject] for usage.
+ </description>
+ </method>
<method name="eval">
<return type="Variant">
</return>
@@ -23,6 +41,15 @@
If [code]use_global_execution_context[/code] is [code]true[/code], the code will be evaluated in the global execution context. Otherwise, it is evaluated in the execution context of a function within the engine's runtime environment.
</description>
</method>
+ <method name="get_interface">
+ <return type="JavaScriptObject">
+ </return>
+ <argument index="0" name="interface" type="String">
+ </argument>
+ <description>
+ Returns an interface to a JavaScript object that can be used by scripts. The [code]interface[/code] must be a valid property of the JavaScript [code]window[/code]. The callback must accept a single [Array] argument, which will contain the JavaScript [code]arguments[/code]. See [JavaScriptObject] for usage.
+ </description>
+ </method>
</methods>
<constants>
</constants>
diff --git a/doc/classes/JavaScriptObject.xml b/doc/classes/JavaScriptObject.xml
new file mode 100644
index 0000000000..a9e9c77e89
--- /dev/null
+++ b/doc/classes/JavaScriptObject.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="JavaScriptObject" inherits="Reference" version="4.0">
+ <brief_description>
+ A wrapper class for native JavaScript objects.
+ </brief_description>
+ <description>
+ JavaScriptObject is used to interact with JavaScript objects retrieved or created via [method JavaScript.get_interface], [method JavaScript.create_object], or [method JavaScript.create_callback].
+ Example:
+ [codeblock]
+ extends Node
+
+ var _my_js_callback = JavaScript.create_callback(self, "myCallback") # This reference must be kept
+ var console = JavaScript.get_interface("console")
+
+ func _init():
+ var buf = JavaScript.create_object("ArrayBuffer", 10) # new ArrayBuffer(10)
+ print(buf) # prints [JavaScriptObject:OBJECT_ID]
+ var uint8arr = JavaScript.create_object("Uint8Array", buf) # new Uint8Array(buf)
+ uint8arr[1] = 255
+ prints(uint8arr[1], uint8arr.byteLength) # prints 255 10
+ console.log(uint8arr) # prints in browser console "Uint8Array(10) [ 0, 255, 0, 0, 0, 0, 0, 0, 0, 0 ]"
+
+ # Equivalent of JavaScript: Array.from(uint8arr).forEach(myCallback)
+ JavaScript.get_interface("Array").from(uint8arr).forEach(_my_js_callback)
+
+ func myCallback(args):
+ # Will be called with the parameters passed to the "forEach" callback
+ # [0, 0, [JavaScriptObject:1173]]
+ # [255, 1, [JavaScriptObject:1173]]
+ # ...
+ # [0, 9, [JavaScriptObject:1180]]
+ print(args)
+ [/codeblock]
+ Note: Only available in the "HTML5" platform.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml
index 2920c27a7a..2ee8e2697c 100644
--- a/doc/classes/RigidBody3D.xml
+++ b/doc/classes/RigidBody3D.xml
@@ -103,7 +103,7 @@
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of collisions is updated once per frame and before the physics step. Consider using signals instead.
</description>
</method>
- <method name="get_inverse_inertia_tensor">
+ <method name="get_inverse_inertia_tensor" qualifiers="const">
<return type="Basis">
</return>
<description>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index ff68a017a1..2015b1f1cd 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -20,7 +20,7 @@
<method name="add_source">
<return type="int">
</return>
- <argument index="0" name="atlas_source_id_override" type="TileSetAtlasSource">
+ <argument index="0" name="atlas_source_id_override" type="TileSetSource">
</argument>
<argument index="1" name="arg1" type="int" default="-1">
</argument>
diff --git a/doc/classes/TileSetScenesCollectionSource.xml b/doc/classes/TileSetScenesCollectionSource.xml
new file mode 100644
index 0000000000..1a00eb51c0
--- /dev/null
+++ b/doc/classes/TileSetScenesCollectionSource.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TileSetScenesCollectionSource" inherits="TileSetSource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="create_scene_tile">
+ <return type="int">
+ </return>
+ <argument index="0" name="packed_scene" type="PackedScene">
+ </argument>
+ <argument index="1" name="id_override" type="int" default="-1">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_alternative_tile_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_alternative_tiles_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_next_scene_tile_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_scene_tile_display_placeholder" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_scene_tile_id">
+ <return type="int">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_scene_tile_scene" qualifiers="const">
+ <return type="PackedScene">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_scene_tiles_count">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_id" qualifiers="const">
+ <return type="Vector2i">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tiles_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_alternative_tile" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="alternative_tile" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="has_scene_tile_id">
+ <return type="bool">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="has_tile" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_scene_tile">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_scene_tile_display_placeholder">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="display_placeholder" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_scene_tile_id">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="new_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_scene_tile_scene">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="packed_scene" type="PackedScene">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 1a27c54757..8f20a3472c 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -554,6 +554,7 @@ public:
RID particles_allocate() override { return RID(); }
void particles_initialize(RID p_rid) override {}
+ void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override {}
void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override {}
void particles_set_emitting(RID p_particles, bool p_emitting) override {}
void particles_set_amount(RID p_particles, int p_amount) override {}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 641d823d8f..ba39ce3aed 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -85,10 +85,10 @@ void ViewportRotationControl::_notification(int p_what) {
axis_menu_options.clear();
axis_menu_options.push_back(Node3DEditorViewport::VIEW_RIGHT);
axis_menu_options.push_back(Node3DEditorViewport::VIEW_TOP);
- axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT);
+ axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR);
axis_menu_options.push_back(Node3DEditorViewport::VIEW_LEFT);
axis_menu_options.push_back(Node3DEditorViewport::VIEW_BOTTOM);
- axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR);
+ axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT);
axis_colors.clear();
axis_colors.push_back(get_theme_color("axis_x_color", "Editor"));
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 476e9a072b..551d1c027a 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -513,7 +513,7 @@ void SpriteFramesEditor::_animation_select() {
if (frames->has_animation(edited_anim)) {
double value = anim_speed->get_line_edit()->get_text().to_float();
- if (!Math::is_equal_approx(value, frames->get_animation_speed(edited_anim))) {
+ if (!Math::is_equal_approx(value, (double)frames->get_animation_speed(edited_anim))) {
_animation_fps_changed(value);
}
}
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 8c5bb56287..6e82951d3d 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -195,9 +195,9 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_
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 != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
base_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
}
}
@@ -215,7 +215,7 @@ void TileAtlasView::_draw_base_tiles() {
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) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
}
@@ -229,17 +229,23 @@ void TileAtlasView::_draw_base_tiles() {
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Bottom
- rect.position = Vector2i(0, margins.y + (grid_size.y * texture_region_size.y));
- rect.set_end(texture->get_size());
- base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ int bottom_border = margins.y + (grid_size.y * texture_region_size.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);
+ }
// Left
rect.position = Vector2i(0, margins.y);
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Right.
- rect.position = Vector2i(margins.x + (grid_size.x * texture_region_size.x), margins.y);
- rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
- base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ int right_border = margins.x + (grid_size.x * texture_region_size.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)));
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ }
// Draw actual tiles, using their properties (modulation, etc...)
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
@@ -249,7 +255,7 @@ void TileAtlasView::_draw_base_tiles() {
Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
// Draw the tile.
- TileSetAtlasPluginRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
+ TileSetPluginAtlasRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
}
}
}
@@ -268,7 +274,7 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
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 != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ 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);
@@ -299,7 +305,7 @@ void TileAtlasView::_draw_base_tiles_dark() {
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 == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) {
// Draw the grid.
base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
}
@@ -331,7 +337,7 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve
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 != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
alternative_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
}
}
@@ -364,7 +370,7 @@ void TileAtlasView::_draw_alternatives() {
}
// Draw the tile.
- TileSetAtlasPluginRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
+ TileSetPluginAtlasRendering::draw_tile(alternatives_draw->get_canvas_item(), 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;
@@ -464,7 +470,7 @@ Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const
return ret;
}
- return TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ return TileSetSource::INVALID_ATLAS_COORDS;
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
@@ -506,7 +512,7 @@ Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
}
}
- return Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ 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) {
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 0b674bec39..d2665a3a2b 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -166,7 +166,7 @@ void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform
Vector<String> components = String(p_property).split("/", true);
if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") {
- TileSetAtlasPluginTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data);
+ TileSetPluginAtlasTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 73640a78c2..80ba396936 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -32,6 +32,7 @@
#include "tiles_editor_plugin.h"
+#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
@@ -55,7 +56,7 @@ void TileMapEditorTilesPlugin::_notification(int p_what) {
picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons"));
- missing_texture_texture = get_theme_icon("TileSet", "EditorIcons");
+ missing_atlas_texture_icon = get_theme_icon("TileSet", "EditorIcons");
break;
case NOTIFICATION_VISIBILITY_CHANGED:
_stop_dragging();
@@ -64,9 +65,8 @@ void TileMapEditorTilesPlugin::_notification(int p_what) {
void TileMapEditorTilesPlugin::tile_set_changed() {
_update_fix_selected_and_hovered();
- _update_bottom_panel();
_update_tile_set_sources_list();
- _update_atlas_view();
+ _update_bottom_panel();
}
void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) {
@@ -146,19 +146,38 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
for (int i = 0; i < tile_set->get_source_count(); i++) {
int source_id = tile_set->get_source_id(i);
- // TODO: handle with virtual functions
TileSetSource *source = *tile_set->get_source(source_id);
+
+ Ref<Texture2D> texture;
+ String item_text;
+
+ // Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- Ref<Texture2D> texture = atlas_source->get_texture();
+ texture = atlas_source->get_texture();
if (texture.is_valid()) {
- sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture);
+ item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id);
} else {
- sources_list->add_item(vformat("No Texture Atlas Source - (id:%d)", source_id), missing_texture_texture);
+ item_text = vformat("No Texture Atlas Source (id:%d)", source_id);
}
- } else {
- sources_list->add_item(vformat("Unknown Type Source - (id:%d)", source_id), missing_texture_texture);
}
+
+ // Scene collection source.
+ TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scene_collection_source) {
+ texture = get_theme_icon("PackedScene", "EditorIcons");
+ item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id);
+ }
+
+ // Use default if not valid.
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id);
+ }
+ if (!texture.is_valid()) {
+ texture = missing_atlas_texture_icon;
+ }
+
+ sources_list->add_item(item_text, texture);
sources_list->set_item_metadata(i, source_id);
}
@@ -176,7 +195,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current());
}
-void TileMapEditorTilesPlugin::_update_atlas_view() {
+void TileMapEditorTilesPlugin::_update_bottom_panel() {
// Update the atlas display.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -185,39 +204,161 @@ void TileMapEditorTilesPlugin::_update_atlas_view() {
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- tile_atlas_view->hide();
return;
}
int source_index = sources_list->get_current();
if (source_index >= 0 && source_index < sources_list->get_item_count()) {
+ atlas_sources_split_container->show();
+ missing_source_label->hide();
+
int source_id = sources_list->get_item_metadata(source_index);
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+
if (atlas_source) {
- tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
tile_atlas_view->show();
+ scene_tiles_list->hide();
+ invalid_source_label->hide();
+ _update_atlas_view();
+ } else if (scenes_collection_source) {
+ tile_atlas_view->hide();
+ scene_tiles_list->show();
+ invalid_source_label->hide();
+ _update_scenes_collection_view();
+ } else {
+ tile_atlas_view->hide();
+ scene_tiles_list->hide();
+ invalid_source_label->show();
}
} else {
+ atlas_sources_split_container->hide();
+ missing_source_label->show();
+
tile_atlas_view->hide();
+ scene_tiles_list->hide();
+ invalid_source_label->hide();
}
+}
- // Synchronize atlas view.
- TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+void TileMapEditorTilesPlugin::_update_atlas_view() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(sources_list->get_current());
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ ERR_FAIL_COND(!atlas_source);
+
+ tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
+ TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
tile_atlas_control->update();
}
-void TileMapEditorTilesPlugin::_update_bottom_panel() {
+void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
}
+
Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(sources_list->get_current());
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ ERR_FAIL_COND(!scenes_collection_source);
+
+ // Clear the list.
+ scene_tiles_list->clear();
+
+ // Rebuild the list.
+ for (int i = 0; i < scenes_collection_source->get_scene_tiles_count(); i++) {
+ int scene_id = scenes_collection_source->get_scene_tile_id(i);
+
+ Ref<PackedScene> scene = 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_theme_icon("PackedScene", "EditorIcons"));
+ }
+ scene_tiles_list->set_item_metadata(item_index, scene_id);
- // Update the tabs.
- missing_source_label->set_visible(tile_set.is_valid() && tile_set->get_source_count() == 0);
- atlas_sources_split_container->set_visible(tile_set.is_valid() && tile_set->get_source_count() > 0);
+ // Check if in selection.
+ if (tile_set_selection.has(TileMapCell(source_id, Vector2i(), scene_id))) {
+ scene_tiles_list->select(item_index, false);
+ }
+ }
+
+ // Icon size update.
+ int int_size = int(EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
+ scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size));
+}
+
+void TileMapEditorTilesPlugin::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, 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 TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_selected) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Add or remove the Tile form the selection.
+ int scene_id = scene_tiles_list->get_item_metadata(p_index);
+ int source_id = sources_list->get_item_metadata(sources_list->get_current());
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ ERR_FAIL_COND(!scenes_collection_source);
+
+ TileMapCell selected = TileMapCell(source_id, Vector2i(), scene_id);
+
+ // Clear the selection if shift is not pressed.
+ if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ tile_set_selection.clear();
+ }
+
+ if (p_selected) {
+ tile_set_selection.insert(selected);
+ } else {
+ if (tile_set_selection.has(selected)) {
+ tile_set_selection.erase(selected);
+ }
+ }
+
+ _update_selection_pattern_from_tileset_selection();
+}
+
+void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() {
+ scene_tiles_list->deselect_all();
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+ _update_selection_pattern_from_tileset_selection();
}
bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
@@ -257,7 +398,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!tile_map_selection.is_empty()) {
undo_redo->create_action(TTR("Delete tiles"));
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
}
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
@@ -288,7 +429,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!tile_map_selection.is_empty()) {
undo_redo->create_action(TTR("Delete tiles"));
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
}
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
@@ -364,7 +505,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
Vector2i coords = E->get();
drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
- tile_map->set_cell(coords, -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ tile_map->set_cell(coords, -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
} else {
// Select tiles
@@ -467,7 +608,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
}
- // handle the preview of the tiles to be placed.
+ // Handle the preview of the tiles to be placed.
if (is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
Map<Vector2i, TileMapCell> preview;
Rect2i drawn_grid_rect;
@@ -583,11 +724,10 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Draw the preview.
for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
+ Vector2i size = tile_set->get_tile_size();
+ Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
+ Rect2 cell_region = xform.xform(Rect2(position, size));
if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
- Vector2i size = tile_set->get_tile_size();
- Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
- Rect2 cell_region = xform.xform(Rect2(position, size));
-
tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
} else {
if (tile_set->has_source(E->get().source_id)) {
@@ -629,12 +769,10 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Draw the tile.
p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
+ } else {
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
}
} else {
- Vector2i size = tile_set->get_tile_size();
- Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
- Rect2 cell_region = xform.xform(Rect2(position, size));
-
tile_set->draw_tile_shape(p_overlay, cell_region, Color(0.0, 0.0, 0.0, 0.5), true);
}
}
@@ -669,7 +807,9 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- sum += Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile))->get_probability();
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile));
+ ERR_FAIL_COND_V(!tile_data, TileMapCell());
+ sum += tile_data->get_probability();
} else {
sum += 1.0;
}
@@ -698,7 +838,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_
return TileMapCell();
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -711,7 +851,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
// Get or create the pattern.
TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> output;
@@ -763,7 +903,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
// Get or create the pattern.
TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
// Compute the offset to align things to the bottom or right.
@@ -814,7 +954,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
// Get or create the pattern.
TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> output;
@@ -1090,8 +1230,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
tile_map_selection.clear();
selection_pattern->clear();
@@ -1101,8 +1241,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
tile_map_selection.clear();
selection_pattern->clear();
@@ -1112,8 +1252,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
int source_index = sources_list->get_current();
if (source_index < 0 || source_index >= sources_list->get_item_count()) {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
tile_map_selection.clear();
selection_pattern->clear();
@@ -1128,8 +1268,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
!tile_set->get_source(hovered_tile.source_id)->has_tile(hovered_tile.get_atlas_coords()) ||
!tile_set->get_source(hovered_tile.source_id)->has_alternative_tile(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)) {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
}
// Selection if needed.
@@ -1187,6 +1327,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
per_source[E->get().source_id].push_back(&(E->get()));
}
+ int vertical_offset = 0;
for (Map<int, List<const TileMapCell *>>::Element *E_source = per_source.front(); E_source; E_source = E_source->next()) {
// Per source.
List<const TileMapCell *> unorganized;
@@ -1199,24 +1340,21 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
// Organize using coordinates.
for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) {
const TileMapCell *current = E_cell->get();
- if (organized_pattern.has(current->get_atlas_coords())) {
- if (current->alternative_tile < organized_pattern[current->get_atlas_coords()]->alternative_tile) {
- unorganized.push_back(organized_pattern[current->get_atlas_coords()]);
- organized_pattern[current->get_atlas_coords()] = current;
- } else {
- unorganized.push_back(current);
- }
- } else {
+ if (current->alternative_tile == 0) {
organized_pattern[current->get_atlas_coords()] = current;
+ } else {
+ unorganized.push_back(current);
}
}
// Compute the encompassing rect for the organized pattern.
Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front();
- encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1));
- for (; E_cell; E_cell = E_cell->next()) {
- encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1));
- encompassing_rect_coords.expand_to(E_cell->key());
+ if (E_cell) {
+ encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1));
+ for (; E_cell; E_cell = E_cell->next()) {
+ encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1));
+ encompassing_rect_coords.expand_to(E_cell->key());
+ }
}
} else {
// Add everything unorganized.
@@ -1227,12 +1365,15 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
// Now add everything to the output pattern.
for (Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); E_cell; E_cell = E_cell->next()) {
- selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position, E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
}
Vector2i organized_size = selection_pattern->get_size();
+ int unorganized_index = 0;
for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) {
- selection_pattern->set_cell(Vector2(organized_size.x, 0), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ unorganized_index++;
}
+ vertical_offset += MAX(organized_size.y, 1);
}
CanvasItemEditor::get_singleton()->update_viewport();
}
@@ -1246,7 +1387,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords)));
}
}
- _update_atlas_view();
+ _update_bottom_panel();
}
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
@@ -1283,7 +1424,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
}
// Draw the hovered tile.
- if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
+ if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false);
}
@@ -1299,7 +1440,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
for (int x = region.position.x; x < region.get_end().x; x++) {
for (int y = region.position.y; y < region.get_end().y; y++) {
Vector2i tile = atlas->get_tile_at_coords(Vector2i(x, y));
- if (tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile != TileSetSource::INVALID_ATLAS_COORDS) {
to_draw.insert(tile);
}
}
@@ -1313,8 +1454,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
void TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited() {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_dragging_selection = false;
tile_atlas_control->update();
}
@@ -1347,12 +1488,12 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
// Update the hovered tile
hovered_tile.source_id = source_id;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = atlas->get_tile_at_coords(coords);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
hovered_tile.set_atlas_coords(coords);
hovered_tile.alternative_tile = 0;
}
@@ -1373,7 +1514,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
tile_set_selection.clear();
}
- if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) {
+ if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) {
if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0))) {
tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
} else {
@@ -1389,18 +1530,18 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
// Compute the covered area.
Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos);
Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
- if (start_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && end_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (start_tile != TileSetSource::INVALID_ATLAS_COORDS && end_tile != TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
region.size += Vector2i(1, 1);
// To update the selection, we copy the selected/not selected status of the tiles we drag from.
Vector2i start_coords = atlas->get_tile_at_coords(start_tile);
- if (mb->is_shift_pressed() && start_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) {
+ if (mb->is_shift_pressed() && start_coords != TileSetSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) {
// Remove from the selection.
for (int x = region.position.x; x < region.get_end().x; x++) {
for (int y = region.position.y; y < region.get_end().y; y++) {
Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
- if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) {
+ if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) {
tile_set_selection.erase(TileMapCell(source_id, tile_coords, 0));
}
}
@@ -1410,7 +1551,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
for (int x = region.position.x; x < region.get_end().x; x++) {
for (int y = region.position.y; y < region.get_end().y; y++) {
Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
- if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
tile_set_selection.insert(TileMapCell(source_id, tile_coords, 0));
}
}
@@ -1453,7 +1594,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
// Draw the selection.
for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
- if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) {
+ if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().get_atlas_coords(), E->get().alternative_tile);
if (rect != Rect2i()) {
alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false);
@@ -1462,7 +1603,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
}
// Draw hovered tile.
- if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) {
+ if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile);
if (rect != Rect2i()) {
alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false);
@@ -1472,8 +1613,8 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
void TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited() {
hovered_tile.source_id = -1;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_dragging_selection = false;
alternative_tiles_control->update();
}
@@ -1506,12 +1647,12 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In
// Update the hovered tile
hovered_tile.source_id = source_id;
- hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
Vector3i alternative_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position());
Vector2i coords = Vector2i(alternative_coords.x, alternative_coords.y);
int alternative = alternative_coords.z;
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetSource::INVALID_TILE_ALTERNATIVE) {
hovered_tile.set_atlas_coords(coords);
hovered_tile.alternative_tile = alternative;
}
@@ -1530,7 +1671,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In
tile_set_selection.clear();
}
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile))) {
tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
} else {
@@ -1565,13 +1706,14 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const {
void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) {
tile_map_id = p_tile_map_id;
- // Clean the selection.
+ // Clear the selection.
tile_set_selection.clear();
tile_map_selection.clear();
selection_pattern->clear();
}
void TileMapEditorTilesPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileMapEditorTilesPlugin::_scene_thumbnail_done);
ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection);
ClassDB::bind_method(D_METHOD("_get_tile_map_selection"), &TileMapEditorTilesPlugin::_get_tile_map_selection);
}
@@ -1718,20 +1860,20 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_stretch_ratio(0.25);
sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+ sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
- sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_atlas_view).unbind(1));
+ sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_bottom_panel).unbind(1));
sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current));
sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list));
- //sources_list->set_drag_forwarding(this);
atlas_sources_split_container->add_child(sources_list);
+ // Tile atlas source.
tile_atlas_view = memnew(TileAtlasView);
tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_texture_grid_visible(false);
tile_atlas_view->set_tile_shape_grid_visible(false);
tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
- //tile_atlas_view->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_view), varray(tile_atlas_view));
atlas_sources_split_container->add_child(tile_atlas_view);
tile_atlas_control = memnew(Control);
@@ -1746,6 +1888,27 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
alternative_tiles_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input));
tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
+ // Scenes collection source.
+ scene_tiles_list = memnew(ItemList);
+ scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_drag_forwarding(this);
+ scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
+ scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected));
+ scene_tiles_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_nothing_selected));
+ scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
+ atlas_sources_split_container->add_child(scene_tiles_list);
+
+ // Invalid source label.
+ invalid_source_label = memnew(Label);
+ invalid_source_label->set_text(TTR("Invalid source selected."));
+ invalid_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ invalid_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ invalid_source_label->set_align(Label::ALIGN_CENTER);
+ invalid_source_label->set_valign(Label::VALIGN_CENTER);
+ invalid_source_label->hide();
+ atlas_sources_split_container->add_child(invalid_source_label);
+
_update_bottom_panel();
}
@@ -2771,8 +2934,8 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
TileMapCell empty_cell;
empty_cell.source_id = -1;
- empty_cell.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
- empty_cell.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index f780686b82..a1fb9af3ef 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -58,7 +58,7 @@ private:
ObjectID tile_map_id;
virtual void edit(ObjectID p_tile_map_id) override;
- // Toolbar.
+ ///// Toolbar /////
HBoxContainer *toolbar;
Ref<ButtonGroup> tool_buttons_group;
@@ -84,7 +84,7 @@ private:
void _update_toolbar();
- // Tilemap editing.
+ ///// Tilemap editing. /////
bool has_mouse = false;
void _mouse_exited_viewport();
@@ -105,12 +105,12 @@ private:
Map<Vector2i, TileMapCell> drag_modified;
TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
- Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos);
- Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_mouse_pos, Vector2i p_end_mouse_pos);
+ Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell);
Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
void _stop_dragging();
- // Selection system.
+ ///// Selection system. /////
Set<Vector2i> tile_map_selection;
TileMapPattern *tile_map_clipboard = memnew(TileMapPattern);
TileMapPattern *selection_pattern = memnew(TileMapPattern);
@@ -124,22 +124,24 @@ private:
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
- // Bottom panel.
- bool tile_set_dragging_selection = false;
- Vector2i tile_set_drag_start_mouse_pos;
-
+ ///// Bottom panel. ////.
Label *missing_source_label;
- HSplitContainer *atlas_sources_split_container;
+ Label *invalid_source_label;
ItemList *sources_list;
- TileAtlasView *tile_atlas_view;
- Ref<Texture2D> missing_texture_texture;
+
+ Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
- void _update_atlas_view();
void _update_bottom_panel();
+ // Atlas sources.
TileMapCell hovered_tile;
+ TileAtlasView *tile_atlas_view;
+ HSplitContainer *atlas_sources_split_container;
+
+ bool tile_set_dragging_selection = false;
+ Vector2i tile_set_drag_start_mouse_pos;
Control *tile_atlas_control;
void _tile_atlas_control_mouse_exited();
@@ -151,6 +153,16 @@ private:
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
+ void _update_atlas_view();
+
+ // Scenes collection sources.
+ ItemList *scene_tiles_list;
+
+ void _update_scenes_collection_view();
+ void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud);
+ void _scenes_list_multi_selected(int p_index, bool p_selected);
+ void _scenes_list_nothing_selected();
+
// Update callback
virtual void tile_set_changed() override;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 324e429592..b6e1a318cb 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -53,10 +53,10 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::set_id(int p_id) {
if (source_id == p_id) {
return;
}
- ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_id));
+ ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Atlas Source ID. Another 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 atlas source list update.
+ 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("changed", "id");
}
@@ -126,7 +126,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet>
}
// -- Proxy object used by the tile inspector --
-bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
if (!tile_set_atlas_source) {
return false;
}
@@ -152,9 +152,9 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, c
return true;
} else if (alternative == 0 && p_name == "size_in_atlas") {
Vector2i as_vector2i = Vector2i(p_value);
- ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i), false);
+ ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false);
- tile_set_atlas_source->move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i);
+ tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
emit_signal("changed", "size_in_atlas");
return true;
} else if (alternative > 0 && p_name == "alternative_id") {
@@ -197,7 +197,7 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, c
return any_valid;
}
-bool TileSetAtlasSourceEditor::TileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
+bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!tile_set_atlas_source) {
return false;
}
@@ -237,7 +237,7 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_get(const StringName &p_name, V
return false;
}
-void TileSetAtlasSourceEditor::TileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
if (!tile_set_atlas_source) {
return;
}
@@ -310,12 +310,11 @@ void TileSetAtlasSourceEditor::TileProxyObject::_get_property_list(List<Property
}
}
-void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id, Set<TileSelection> p_tiles) {
+void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles) {
ERR_FAIL_COND(!p_tile_set_atlas_source);
- ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tiles.is_empty());
for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) {
- ERR_FAIL_COND(E->get().tile == TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND(E->get().tile == TileSetSource::INVALID_ATLAS_COORDS);
ERR_FAIL_COND(E->get().alternative < 0);
}
@@ -333,7 +332,6 @@ void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_
}
tile_set_atlas_source = p_tile_set_atlas_source;
- source_id = p_source_id;
tiles = Set<TileSelection>(p_tiles);
// Connect to changes.
@@ -352,7 +350,7 @@ void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_
notify_property_list_changed();
}
-void TileSetAtlasSourceEditor::TileProxyObject::_bind_methods() {
+void TileSetAtlasSourceEditor::AtlasTileProxyObject::_bind_methods() {
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
}
@@ -391,12 +389,12 @@ void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
// Fix hovered.
if (!tile_set_atlas_source->has_tile(hovered_base_tile_coords)) {
- hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
}
Vector2i coords = Vector2i(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y);
int alternative = hovered_alternative_tile_coords.z;
if (!tile_set_atlas_source->has_tile(coords) || !tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
- hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
}
@@ -405,7 +403,7 @@ void TileSetAtlasSourceEditor::_update_tile_inspector() {
// Update the proxy object.
if (has_atlas_tile_selected) {
- tile_proxy_object->edit(tile_set_atlas_source, tile_set_atlas_source_id, selection);
+ tile_proxy_object->edit(tile_set_atlas_source, selection);
}
// Update visibility.
@@ -486,7 +484,7 @@ void TileSetAtlasSourceEditor::_update_toolbar() {
}
void TileSetAtlasSourceEditor::_tile_atlas_control_mouse_exited() {
- hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
tile_atlas_control->update();
tile_atlas_control_unscaled->update();
tile_atlas_view->update();
@@ -514,7 +512,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
if (selection.size() == 1) {
// Change the cursor depending on the hovered thing.
TileSelection selected = selection.front()->get();
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position();
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
@@ -560,7 +558,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords);
for (int i = 0; i < line.size(); i++) {
- if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) {
tile_set_atlas_source->create_tile(line[i]);
drag_modified_tiles.insert(line[i]);
}
@@ -576,7 +574,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords);
for (int i = 0; i < line.size(); i++) {
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]);
- if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
drag_modified_tiles.insert(base_tile_coords);
}
}
@@ -662,17 +660,17 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Remove a first tile.
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
}
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
drag_modified_tiles.insert(coords);
}
} else {
if (mb->is_shift_pressed()) {
// Create a big tile.
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
// Setup the dragging info, only if we start on an empty tile.
drag_type = DRAG_TYPE_CREATE_BIG_TILE;
drag_start_mouse_pos = mouse_local_pos;
@@ -692,7 +690,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Create a first tile if needed.
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
tile_set_atlas_source->create_tile(coords);
drag_modified_tiles.insert(coords);
}
@@ -710,7 +708,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
if (mb->is_shift_pressed()) {
// Create a big tile.
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
// Setup the dragging info, only if we start on an empty tile.
drag_type = DRAG_TYPE_CREATE_BIG_TILE;
drag_start_mouse_pos = mouse_local_pos;
@@ -732,7 +730,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_type = DRAG_TYPE_NONE;
if (selection.size() == 1) {
TileSelection selected = selection.front()->get();
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
@@ -771,17 +769,17 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Selecting then dragging a tile.
if (drag_type == DRAG_TYPE_NONE) {
- TileSelection selected = { TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE };
+ TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE };
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
selected = { coords, 0 };
}
}
bool shift = mb->is_shift_pressed();
- if (!shift && selection.size() == 1 && selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
+ if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
// Start move dragging.
drag_type = DRAG_TYPE_MOVE_TILE;
drag_start_mouse_pos = mouse_local_pos;
@@ -812,13 +810,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Right click pressed.
TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 };
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile);
}
// Set the selection if needed.
if (selection.size() <= 1) {
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
undo_redo->create_action(TTR("Select tiles"));
undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
selection.clear();
@@ -831,15 +829,15 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
}
// Pops up the correct menu, depending on whether we have a tile or not.
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
// We have a tile.
menu_option_coords = selected.tile;
menu_option_alternative = 0;
base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
- } else if (hovered_base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
// We don't have a tile, but can create one.
menu_option_coords = hovered_base_tile_coords;
- menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
}
} else {
@@ -902,7 +900,7 @@ void TileSetAtlasSourceEditor::_end_dragging() {
for (int x = area.get_position().x; x < area.get_end().x; x++) {
for (int y = area.get_position().y; y < area.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
undo_redo->add_do_method(tile_set_atlas_source, "create_tile", coords);
undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", coords);
}
@@ -923,7 +921,7 @@ void TileSetAtlasSourceEditor::_end_dragging() {
for (int x = area.get_position().x; x < area.get_end().x; x++) {
for (int y = area.get_position().y; y < area.get_end().y; y++) {
Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
to_delete.insert(coords);
}
}
@@ -964,8 +962,8 @@ void TileSetAtlasSourceEditor::_end_dragging() {
case DRAG_TYPE_RECT_SELECT: {
Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
- ERR_FAIL_COND(start_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS);
- ERR_FAIL_COND(new_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND(start_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND(new_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS);
Rect2i region = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
region.size += Vector2i(1, 1);
@@ -977,7 +975,7 @@ void TileSetAtlasSourceEditor::_end_dragging() {
bool add_to_selection = true;
if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
Vector2i coords = tile_set_atlas_source->get_tile_at_coords(start_base_tiles_coords);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
if (selection.has({ coords, 0 })) {
add_to_selection = false;
}
@@ -991,7 +989,7 @@ void TileSetAtlasSourceEditor::_end_dragging() {
for (int y = region.position.y; y < region.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
coords = tile_set_atlas_source->get_tile_at_coords(coords);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
if (add_to_selection && !selection.has({ coords, 0 })) {
selection.insert({ coords, 0 });
} else if (!add_to_selection && selection.has({ coords, 0 })) {
@@ -1243,7 +1241,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
for (int x = area.get_position().x; x < area.get_end().x; x++) {
for (int y = area.get_position().y; y < area.get_end().y; y++) {
Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
to_paint.insert(coords);
}
}
@@ -1266,7 +1264,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
for (int x = area.get_position().x; x < area.get_end().x; x++) {
for (int y = area.get_position().y; y < area.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Vector2i origin = margins + (coords * (tile_size + separation));
tile_atlas_control->draw_rect(Rect2i(origin, tile_size), Color(1.0, 1.0, 1.0), false);
}
@@ -1290,7 +1288,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) {
Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords);
- if (hovered_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) {
// Draw existing hovered tile.
tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false);
} else {
@@ -1349,7 +1347,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
selection.clear();
TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
selection.insert(selected);
}
@@ -1364,7 +1362,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
selection.clear();
TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
- if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
selection.insert(selected);
}
@@ -1387,7 +1385,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
}
void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() {
- hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
tile_atlas_control->update();
tile_atlas_control_unscaled->update();
alternative_tiles_control->update();
@@ -1399,7 +1397,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
if (tools_button_group->get_pressed_button() == tool_select_button) {
// Draw hovered tile.
Vector2i coords = Vector2(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y);
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, hovered_alternative_tile_coords.z);
if (rect != Rect2i()) {
alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false);
@@ -1441,7 +1439,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
- TileProxyObject *tile_data = Object::cast_to<TileProxyObject>(p_edited);
+ AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
if (tile_data) {
Vector<String> components = String(p_property).split("/", true, 2);
if (components.size() == 2 && components[1] == "shapes_count") {
@@ -1518,7 +1516,7 @@ void TileSetAtlasSourceEditor::_auto_create_tiles() {
for (int x = 0; x < grid_size.x; x++) {
// Check if we have a tile at the coord
Vector2i coords = Vector2i(x, y);
- if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
// Check if the texture is empty at the given coords.
Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size);
bool is_opaque = false;
@@ -1673,7 +1671,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_inspector_label->hide();
middle_vbox_container->add_child(tile_inspector_label);
- tile_proxy_object = memnew(TileProxyObject(this));
+ tile_proxy_object = memnew(AtlasTileProxyObject(this));
tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1));
tile_inspector = memnew(EditorInspector);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index ff68aa8288..70f2cdbe01 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -45,8 +45,8 @@ class TileSetAtlasSourceEditor : public HBoxContainer {
private:
// A class to store which tiles are selected.
struct TileSelection {
- Vector2i tile = TileSetAtlasSource::INVALID_ATLAS_COORDS;
- int alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS;
+ int alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
bool operator<(const TileSelection &p_other) const {
if (tile == p_other.tile) {
@@ -80,14 +80,13 @@ private:
};
// -- Proxy object for a tile, needed by the inspector --
- class TileProxyObject : public Object {
- GDCLASS(TileProxyObject, Object);
+ class AtlasTileProxyObject : public Object {
+ GDCLASS(AtlasTileProxyObject, Object);
private:
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
- int source_id;
Set<TileSelection> tiles = Set<TileSelection>();
protected:
@@ -99,10 +98,10 @@ private:
public:
// Update the proxyed object.
- void edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id = -1, Set<TileSelection> p_tiles = Set<TileSelection>());
+ void edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles = Set<TileSelection>());
- TileProxyObject(TileSetAtlasSourceEditor *p_tiles_editor_source_tab) {
- tiles_set_atlas_source_editor = p_tiles_editor_source_tab;
+ AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) {
+ tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor;
}
};
@@ -115,7 +114,7 @@ private:
bool tile_set_atlas_source_changed_needs_update = false;
// -- Inspector --
- TileProxyObject *tile_proxy_object;
+ AtlasTileProxyObject *tile_proxy_object;
Label *tile_inspector_label;
EditorInspector *tile_inspector;
String selected_property;
@@ -175,7 +174,7 @@ private:
ADVANCED_AUTO_REMOVE_TILES,
};
Vector2i menu_option_coords;
- int menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
void _menu_option(int p_option);
// Tool buttons.
@@ -198,7 +197,7 @@ private:
Array _get_selection_as_array();
// A control on the tile atlas to draw and handle input events.
- Vector2i hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ Vector2i hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
PopupMenu *base_tile_popup_menu;
PopupMenu *empty_base_tile_popup_menu;
@@ -213,7 +212,7 @@ private:
void _tile_atlas_view_transform_changed();
// A control over the alternative tiles.
- Vector3i hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ 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;
Control *alternative_tiles_control;
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 5573fae670..05ebe408a5 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -140,20 +140,39 @@ void TileSetEditor::_update_atlas_sources_list(int force_selected_id) {
for (int i = 0; i < tile_set->get_source_count(); i++) {
int source_id = tile_set->get_source_id(i);
- // TODO: handle with virtual functions
TileSetSource *source = *tile_set->get_source(source_id);
+
+ Ref<Texture2D> texture;
+ String item_text;
+
+ // Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- Ref<Texture2D> texture = atlas_source->get_texture();
+ texture = atlas_source->get_texture();
if (texture.is_valid()) {
- sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture);
+ item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id);
} else {
- sources_list->add_item(vformat("No texture atlas source - (id:%d)", source_id), missing_texture_texture);
+ item_text = vformat(TTR("No Texture Atlas Source (id:%d)"), source_id);
}
- } else {
- sources_list->add_item(vformat("Unknown type source - (id:%d)", source_id), missing_texture_texture);
}
- sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
+
+ // Scene collection source.
+ TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scene_collection_source) {
+ texture = get_theme_icon("PackedScene", "EditorIcons");
+ item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id);
+ }
+
+ // Use default if not valid.
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id);
+ }
+ if (!texture.is_valid()) {
+ texture = missing_texture_texture;
+ }
+
+ sources_list->add_item(item_text, texture);
+ sources_list->set_item_metadata(i, source_id);
}
// Set again the current selected item if needed.
@@ -193,35 +212,63 @@ void TileSetEditor::_source_selected(int p_source_index) {
if (p_source_index >= 0) {
int source_id = sources_list->get_item_metadata(p_source_index);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(*tile_set->get_source(source_id));
if (atlas_source) {
- tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id);
no_source_selected_label->hide();
+ tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id);
tile_set_atlas_source_editor->show();
+ tile_set_scenes_collection_source_editor->hide();
+ } else if (scenes_collection_source) {
+ no_source_selected_label->hide();
+ tile_set_atlas_source_editor->hide();
+ tile_set_scenes_collection_source_editor->edit(*tile_set, scenes_collection_source, source_id);
+ tile_set_scenes_collection_source_editor->show();
} else {
no_source_selected_label->show();
tile_set_atlas_source_editor->hide();
+ tile_set_scenes_collection_source_editor->hide();
}
} else {
no_source_selected_label->show();
tile_set_atlas_source_editor->hide();
+ tile_set_scenes_collection_source_editor->hide();
}
}
-void TileSetEditor::_source_add_pressed() {
+void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
ERR_FAIL_COND(!tile_set.is_valid());
- int source_id = tile_set->get_next_source_id();
+ switch (p_id_pressed) {
+ case 0: {
+ int source_id = tile_set->get_next_source_id();
- Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
+ Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
- // Add a new source.
- undo_redo->create_action(TTR("Add atlas source"));
- undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
- undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
- undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
- undo_redo->commit_action();
+ // Add a new source.
+ undo_redo->create_action(TTR("Add atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
+ undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+
+ _update_atlas_sources_list(source_id);
+ } break;
+ case 1: {
+ int source_id = tile_set->get_next_source_id();
+
+ Ref<TileSetScenesCollectionSource> scene_collection_source = memnew(TileSetScenesCollectionSource);
- _update_atlas_sources_list(source_id);
+ // Add a new source.
+ undo_redo->create_action(TTR("Add atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", scene_collection_source, source_id);
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+
+ _update_atlas_sources_list(source_id);
+ } break;
+ default:
+ ERR_FAIL();
+ }
}
void TileSetEditor::_source_delete_pressed() {
@@ -434,6 +481,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
}
tile_set_atlas_source_editor->hide();
+ tile_set_scenes_collection_source_editor->hide();
no_source_selected_label->show();
}
@@ -464,6 +512,7 @@ TileSetEditor::TileSetEditor() {
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current));
sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list));
+ sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->set_drag_forwarding(this);
split_container_left_side->add_child(sources_list);
@@ -477,11 +526,19 @@ TileSetEditor::TileSetEditor() {
sources_delete_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_delete_pressed));
sources_bottom_actions->add_child(sources_delete_button);
- sources_add_button = memnew(Button);
+ sources_add_button = memnew(MenuButton);
sources_add_button->set_flat(true);
- sources_add_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_add_pressed));
+ sources_add_button->get_popup()->add_item(TTR("Atlas"));
+ sources_add_button->get_popup()->add_item(TTR("Scenes Collection"));
+ sources_add_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_source_add_id_pressed));
sources_bottom_actions->add_child(sources_add_button);
+ // Right side container.
+ VBoxContainer *split_container_right_side = memnew(VBoxContainer);
+ split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
+ split_container_right_side->set_v_size_flags(SIZE_EXPAND_FILL);
+ split_container->add_child(split_container_right_side);
+
// No source selected.
no_source_selected_label = memnew(Label);
no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source."));
@@ -489,16 +546,24 @@ TileSetEditor::TileSetEditor() {
no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL);
no_source_selected_label->set_align(Label::ALIGN_CENTER);
no_source_selected_label->set_valign(Label::VALIGN_CENTER);
- split_container->add_child(no_source_selected_label);
+ split_container_right_side->add_child(no_source_selected_label);
// Atlases editor.
tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor);
tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list));
- split_container->add_child(tile_set_atlas_source_editor);
+ split_container_right_side->add_child(tile_set_atlas_source_editor);
tile_set_atlas_source_editor->hide();
+ // Scenes collection editor.
+ tile_set_scenes_collection_source_editor = memnew(TileSetScenesCollectionSourceEditor);
+ tile_set_scenes_collection_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_set_scenes_collection_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_set_scenes_collection_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list));
+ split_container_right_side->add_child(tile_set_scenes_collection_source_editor);
+ tile_set_scenes_collection_source_editor->hide();
+
// Registers UndoRedo inspector callback.
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index c4aebb40a2..d508c04319 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -35,6 +35,7 @@
#include "scene/resources/tile_set.h"
#include "tile_data_editors.h"
#include "tile_set_atlas_source_editor.h"
+#include "tile_set_scenes_collection_source_editor.h"
class TileSetEditor : public VBoxContainer {
GDCLASS(TileSetEditor, VBoxContainer);
@@ -47,6 +48,7 @@ private:
Label *no_source_selected_label;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor;
+ TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor;
UndoRedo *undo_redo = EditorNode::get_undo_redo();
@@ -64,11 +66,11 @@ private:
// -- Sources management --
Button *sources_delete_button;
- Button *sources_add_button;
+ MenuButton *sources_add_button;
ItemList *sources_list;
Ref<Texture2D> missing_texture_texture;
void _source_selected(int p_source_index);
- void _source_add_pressed();
+ void _source_add_id_pressed(int p_id_pressed);
void _source_delete_pressed();
void _tile_set_changed();
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
new file mode 100644
index 0000000000..568d4ca8d7
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -0,0 +1,511 @@
+/*************************************************************************/
+/* tile_set_scenes_collection_source_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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_resource_preview.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+
+#include "scene/gui/item_list.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("changed", "id");
+}
+
+int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id() {
+ return source_id;
+}
+
+bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ bool valid = false;
+ tile_set_scenes_collection_source->set(p_name, p_value, &valid);
+ if (valid) {
+ emit_signal("changed", String(p_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;
+ }
+ bool valid = false;
+ r_ret = tile_set_scenes_collection_source->get(p_name, &valid);
+ return valid;
+}
+
+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_COND(!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);
+
+ // 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("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("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("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_COND(!p_tile_set_scenes_collection_source);
+ ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
+
+ 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(String p_what) {
+ if (p_what == "id") {
+ emit_signal("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, 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() {
+ int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
+ undo_redo->create_action(TTR("Add a Scene Tile"));
+ undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", Ref<PackedScene>(), 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.size() <= 0);
+ int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
+
+ 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);
+}
+
+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_theme_icon("PackedScene", "EditorIcons"));
+ }
+ 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;
+ }
+ }
+
+ // Reselect if needed.
+ if (to_reselect >= 0) {
+ scene_tiles_list->select(to_reselect);
+ }
+
+ // Icon size update.
+ int int_size = int(EditorSettings::get_singleton()->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_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ scene_tile_add_button->set_icon(get_theme_icon("Add", "EditorIcons"));
+ scene_tile_delete_button->set_icon(get_theme_icon("Remove", "EditorIcons"));
+ _update_scenes_list();
+ break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ if (tile_set_scenes_collection_source_changed_needs_update) {
+ // 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;
+ default:
+ 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_COND(!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 (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id) {
+ 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;
+
+ // 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.
+ int scene_id = -1;
+ 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()) {
+ scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
+ 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 file = files[i];
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
+
+ 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);
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetScenesCollectionSourceEditor::drop_data_fw);
+}
+
+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_enable_h_scroll(false);
+ middle_panel->set_custom_minimum_size(Size2i(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_undo_redo(undo_redo);
+ scenes_collection_source_inspector->set_enable_v_scroll(false);
+ scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
+ 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_undo_redo(undo_redo);
+ tile_inspector->set_enable_v_scroll(false);
+ tile_inspector->edit(tile_proxy_object);
+ 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_h_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_drag_forwarding(this);
+ 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_flat(true);
+ 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_flat(true);
+ 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);
+}
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
new file mode 100644
index 0000000000..195aa79bc4
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
@@ -0,0 +1,139 @@
+/*************************************************************************/
+/* tile_set_scenes_collection_source_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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_node.h"
+#include "scene/gui/box_container.h"
+#include "scene/resources/tile_set.h"
+
+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;
+ 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;
+
+ 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:
+ Ref<TileSet> tile_set;
+ TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
+ int tile_set_source_id = -1;
+
+ UndoRedo *undo_redo = EditorNode::get_undo_redo();
+
+ bool tile_set_scenes_collection_source_changed_needs_update = false;
+
+ // Source inspector.
+ TileSetScenesCollectionProxyObject *scenes_collection_source_proxy_object;
+ Label *scenes_collection_source_inspector_label;
+ EditorInspector *scenes_collection_source_inspector;
+
+ // Tile inspector.
+ SceneTileProxyObject *tile_proxy_object;
+ Label *tile_inspector_label;
+ EditorInspector *tile_inspector;
+
+ ItemList *scene_tiles_list;
+ Button *scene_tile_add_button;
+ Button *scene_tile_delete_button;
+
+ void _tile_set_scenes_collection_source_changed();
+ void _scenes_collection_source_proxy_object_changed(String p_what);
+ void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud);
+ void _scenes_list_item_activated(int p_index);
+
+ void _source_add_pressed();
+ void _source_delete_pressed();
+
+ // Update methods.
+ void _update_source_inspector();
+ void _update_tile_inspector();
+ void _update_scenes_list();
+ void _update_action_buttons();
+
+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);
+ 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;
+ TileSetScenesCollectionSourceEditor();
+ ~TileSetScenesCollectionSourceEditor();
+};
+
+#endif
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 1ffe4853a0..2d0ff24723 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -100,7 +100,6 @@ private:
FileDialog *fdialog_install;
String zip_path;
String zip_title;
- String zip_root;
AcceptDialog *dialog_error;
String fav_dir;
@@ -201,9 +200,7 @@ private:
char fname[16384];
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
- String fname_str = String(fname);
- if (fname_str.ends_with("project.godot")) {
- zip_root = fname_str.substr(0, fname_str.rfind("project.godot"));
+ if (String(fname).ends_with("project.godot")) {
break;
}
@@ -524,7 +521,24 @@ private:
return;
}
+ // Find the zip_root
+ String zip_root;
int ret = unzGoToFirstFile(pkg);
+ while (ret == UNZ_OK) {
+ unz_file_info info;
+ char fname[16384];
+ unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+ String name = fname;
+ if (name.ends_with("project.godot")) {
+ zip_root = name.substr(0, name.rfind("project.godot"));
+ break;
+ }
+
+ ret = unzGoToNextFile(pkg);
+ }
+
+ ret = unzGoToFirstFile(pkg);
Vector<String> failed_files;
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index 38c5138482..a12412a9cd 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -31,6 +31,9 @@
<member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
The [OpenSimplexNoise] instance used to generate the noise.
</member>
+ <member name="noise_offset" type="Vector2" setter="set_noise_offset" getter="get_noise_offset" default="Vector2( 0, 0 )">
+ An offset used to specify the noise space coordinate of the top left corner of the generated noise. This value is ignored if [member seamless] is enabled.
+ </member>
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
[b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index ad82f87213..2fdbd61ee7 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -31,8 +31,10 @@
</argument>
<argument index="1" name="height" type="int">
</argument>
+ <argument index="2" name="noise_offset" type="Vector2" default="Vector2( 0, 0 )">
+ </argument>
<description>
- Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters.
+ Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. If [code]noise_offset[/code] is specified, then the offset value is used as the coordinates of the top-left corner of the generated noise.
</description>
</method>
<method name="get_noise_1d" qualifiers="const">
diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp
index 7272d32fac..9e0155da94 100644
--- a/modules/opensimplex/noise_texture.cpp
+++ b/modules/opensimplex/noise_texture.cpp
@@ -52,6 +52,9 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
+ ClassDB::bind_method(D_METHOD("set_noise_offset", "noise_offset"), &NoiseTexture::set_noise_offset);
+ ClassDB::bind_method(D_METHOD("get_noise_offset"), &NoiseTexture::get_noise_offset);
+
ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless);
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless);
@@ -71,6 +74,7 @@ void NoiseTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "noise_offset"), "set_noise_offset", "get_noise_offset");
}
void NoiseTexture::_validate_property(PropertyInfo &property) const {
@@ -130,7 +134,7 @@ Ref<Image> NoiseTexture::_generate_texture() {
if (seamless) {
image = ref_noise->get_seamless_image(size.x);
} else {
- image = ref_noise->get_image(size.x, size.y);
+ image = ref_noise->get_image(size.x, size.y, noise_offset);
}
if (as_normal_map) {
@@ -198,6 +202,14 @@ void NoiseTexture::set_height(int p_height) {
_queue_update();
}
+void NoiseTexture::set_noise_offset(Vector2 p_noise_offset) {
+ if (noise_offset == p_noise_offset) {
+ return;
+ }
+ noise_offset = p_noise_offset;
+ _queue_update();
+}
+
void NoiseTexture::set_seamless(bool p_seamless) {
if (p_seamless == seamless) {
return;
@@ -245,6 +257,10 @@ int NoiseTexture::get_height() const {
return size.y;
}
+Vector2 NoiseTexture::get_noise_offset() const {
+ return noise_offset;
+}
+
RID NoiseTexture::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index 6983ae18fe..20a53bb58b 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -56,6 +56,7 @@ private:
Ref<OpenSimplexNoise> noise;
Vector2i size = Vector2i(512, 512);
+ Vector2 noise_offset;
bool seamless = false;
bool as_normal_map = false;
float bump_strength = 8.0;
@@ -79,6 +80,9 @@ public:
void set_width(int p_width);
void set_height(int p_height);
+ void set_noise_offset(Vector2 p_noise_offset);
+ Vector2 get_noise_offset() const;
+
void set_seamless(bool p_seamless);
bool get_seamless();
diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp
index 3773946112..f0a8867284 100644
--- a/modules/opensimplex/open_simplex_noise.cpp
+++ b/modules/opensimplex/open_simplex_noise.cpp
@@ -96,7 +96,7 @@ void OpenSimplexNoise::set_lacunarity(float p_lacunarity) {
emit_changed();
}
-Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
+Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height, const Vector2 &p_noise_offset) const {
Vector<uint8_t> data;
data.resize(p_width * p_height);
@@ -104,7 +104,7 @@ Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
for (int i = 0; i < p_height; i++) {
for (int j = 0; j < p_width; j++) {
- float v = get_noise_2d(j, i);
+ float v = get_noise_2d(float(j) + p_noise_offset.x, float(i) + p_noise_offset.y);
v = v * 0.5 + 0.5; // Normalize [0..1]
wd8[(i * p_width + j)] = uint8_t(CLAMP(v * 255.0, 0, 255));
}
@@ -161,7 +161,7 @@ void OpenSimplexNoise::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity);
ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity);
- ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image);
+ ClassDB::bind_method(D_METHOD("get_image", "width", "height", "noise_offset"), &OpenSimplexNoise::get_image, DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image);
ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &OpenSimplexNoise::get_noise_1d);
diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h
index 847c157409..bb50c523d2 100644
--- a/modules/opensimplex/open_simplex_noise.h
+++ b/modules/opensimplex/open_simplex_noise.h
@@ -75,7 +75,7 @@ public:
void set_lacunarity(float p_lacunarity);
float get_lacunarity() const { return lacunarity; }
- Ref<Image> get_image(int p_width, int p_height) const;
+ Ref<Image> get_image(int p_width, int p_height, const Vector2 &p_noise_offset = Vector2()) const;
Ref<Image> get_seamless_image(int p_size) const;
float get_noise_1d(float x) const;
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index 24be07d8ec..d68ab7f7c9 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -6,7 +6,7 @@ javascript_files = [
"audio_driver_javascript.cpp",
"display_server_javascript.cpp",
"http_client_javascript.cpp",
- "javascript_eval.cpp",
+ "javascript_singleton.cpp",
"javascript_main.cpp",
"os_javascript.cpp",
"api/javascript_tools_editor_plugin.cpp",
@@ -26,7 +26,7 @@ sys_env.AddJSLibraries(
if env["tools"]:
sys_env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"])
if env["javascript_eval"]:
- sys_env.AddJSLibraries(["js/libs/library_godot_eval.js"])
+ sys_env.AddJSLibraries(["js/libs/library_godot_javascript_singleton.js"])
for lib in sys_env["JS_LIBS"]:
sys_env.Append(LINKFLAGS=["--js-library", lib])
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index 2f7bde065f..039ce815e4 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -30,13 +30,14 @@
#include "api.h"
#include "core/config/engine.h"
-#include "javascript_eval.h"
+#include "javascript_singleton.h"
#include "javascript_tools_editor_plugin.h"
static JavaScript *javascript_eval;
void register_javascript_api() {
JavaScriptToolsEditorPlugin::initialize();
+ ClassDB::register_virtual_class<JavaScriptObject>();
ClassDB::register_virtual_class<JavaScript>();
javascript_eval = memnew(JavaScript);
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval));
@@ -61,10 +62,41 @@ JavaScript::~JavaScript() {}
void JavaScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScript::get_interface);
+ ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScript::create_callback);
+ {
+ MethodInfo mi;
+ mi.name = "create_object";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING, "object"));
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi);
+ }
}
#if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED)
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
return Variant();
}
+
+Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+ return Ref<JavaScriptObject>();
+}
+
+Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+ return Ref<JavaScriptObject>();
+}
+
+Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Ref<JavaScriptObject>();
+ }
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ return Ref<JavaScriptObject>();
+ }
+ return Ref<JavaScriptObject>();
+}
#endif
diff --git a/platform/javascript/api/javascript_eval.h b/platform/javascript/api/javascript_singleton.h
index 24f7648ed9..45e9950acb 100644
--- a/platform/javascript/api/javascript_eval.h
+++ b/platform/javascript/api/javascript_singleton.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* javascript_eval.h */
+/* javascript_singleton.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,10 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef JAVASCRIPT_EVAL_H
-#define JAVASCRIPT_EVAL_H
+#ifndef JAVASCRIPT_SINGLETON_H
+#define JAVASCRIPT_SINGLETON_H
#include "core/object/class_db.h"
+#include "core/object/reference.h"
+
+class JavaScriptObject : public Reference {
+private:
+ GDCLASS(JavaScriptObject, Reference);
+
+protected:
+ virtual bool _set(const StringName &p_name, const Variant &p_value) { return false; }
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const { return false; }
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const {}
+};
class JavaScript : public Object {
private:
@@ -44,10 +55,13 @@ protected:
public:
Variant eval(const String &p_code, bool p_use_global_exec_context = false);
+ Ref<JavaScriptObject> get_interface(const String &p_interface);
+ Ref<JavaScriptObject> create_callback(const Callable &p_callable);
+ Variant _create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
static JavaScript *get_singleton();
JavaScript();
~JavaScript();
};
-#endif // JAVASCRIPT_EVAL_H
+#endif // JAVASCRIPT_SINGLETON_H
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
deleted file mode 100644
index cb19dd20d4..0000000000
--- a/platform/javascript/javascript_eval.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* javascript_eval.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_EVAL_ENABLED
-
-#include "api/javascript_eval.h"
-#include "emscripten.h"
-
-extern "C" {
-union js_eval_ret {
- uint32_t b;
- double d;
- char *s;
-};
-
-extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
-}
-
-void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
- PackedByteArray *arr = (PackedByteArray *)p_arr;
- VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write;
- arr->resize(p_len);
- *write = arr->write;
- return arr->ptrw();
-}
-
-Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
- union js_eval_ret js_data;
- PackedByteArray arr;
- VectorWriteProxy<uint8_t> arr_write;
-
- Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write));
-
- switch (return_type) {
- case Variant::BOOL:
- return js_data.b;
- case Variant::FLOAT:
- return js_data.d;
- case Variant::STRING: {
- String str = String::utf8(js_data.s);
- free(js_data.s); // Must free the string allocated in JS.
- return str;
- }
- case Variant::PACKED_BYTE_ARRAY:
- arr_write = VectorWriteProxy<uint8_t>();
- return arr;
- default:
- return Variant();
- }
-}
-
-#endif // JAVASCRIPT_EVAL_ENABLED
diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp
new file mode 100644
index 0000000000..67908a18da
--- /dev/null
+++ b/platform/javascript/javascript_singleton.cpp
@@ -0,0 +1,338 @@
+/*************************************************************************/
+/* javascript_singleton.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef JAVASCRIPT_EVAL_ENABLED
+
+#include "api/javascript_singleton.h"
+#include "emscripten.h"
+
+extern "C" {
+typedef union {
+ int64_t i;
+ double r;
+ void *p;
+} godot_js_wrapper_ex;
+
+typedef int (*GodotJSWrapperVariant2JSCallback)(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
+typedef void (*GodotJSWrapperFreeLockCallback)(void **p_lock, int p_type);
+extern int godot_js_wrapper_interface_get(const char *p_name);
+extern int godot_js_wrapper_object_call(int p_id, const char *p_method, void **p_args, int p_argc, GodotJSWrapperVariant2JSCallback p_variant2js_callback, godot_js_wrapper_ex *p_cb_rval, void **p_lock, GodotJSWrapperFreeLockCallback p_lock_callback);
+extern int godot_js_wrapper_object_get(int p_id, godot_js_wrapper_ex *p_val, const char *p_prop);
+extern int godot_js_wrapper_object_getvar(int p_id, int p_type, godot_js_wrapper_ex *p_val);
+extern int godot_js_wrapper_object_setvar(int p_id, int p_key_type, godot_js_wrapper_ex *p_key_ex, int p_val_type, godot_js_wrapper_ex *p_val_ex);
+extern void godot_js_wrapper_object_set(int p_id, const char *p_name, int p_type, godot_js_wrapper_ex *p_val);
+extern void godot_js_wrapper_object_unref(int p_id);
+extern int godot_js_wrapper_create_cb(void *p_ref, void (*p_callback)(void *p_ref, int p_arg_id, int p_argc));
+extern int godot_js_wrapper_create_object(const char *p_method, void **p_args, int p_argc, GodotJSWrapperVariant2JSCallback p_variant2js_callback, godot_js_wrapper_ex *p_cb_rval, void **p_lock, GodotJSWrapperFreeLockCallback p_lock_callback);
+};
+
+class JavaScriptObjectImpl : public JavaScriptObject {
+private:
+ friend class JavaScript;
+
+ int _js_id = 0;
+ Callable _callable;
+
+ static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
+ static void _free_lock(void **p_lock, int p_type);
+ static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val);
+ static void *_alloc_variants(int p_size);
+ static void _callback(void *p_ref, int p_arg_id, int p_argc);
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value) override;
+ bool _get(const StringName &p_name, Variant &r_ret) const override;
+ void _get_property_list(List<PropertyInfo> *p_list) const override;
+
+public:
+ Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override;
+ void setvar(const Variant &p_key, const Variant &p_value, bool *r_valid = nullptr) override;
+ Variant call(const StringName &p_method, const Variant **p_args, int p_argc, Callable::CallError &r_error) override;
+ JavaScriptObjectImpl() {}
+ JavaScriptObjectImpl(int p_id) { _js_id = p_id; }
+ ~JavaScriptObjectImpl() {
+ if (_js_id) {
+ godot_js_wrapper_object_unref(_js_id);
+ }
+ }
+};
+
+bool JavaScriptObjectImpl::_set(const StringName &p_name, const Variant &p_value) {
+ ERR_FAIL_COND_V_MSG(!_js_id, false, "Invalid JS instance");
+ const String name = p_name;
+ godot_js_wrapper_ex exchange;
+ void *lock = nullptr;
+ const Variant *v = &p_value;
+ int type = _variant2js((const void **)&v, 0, &exchange, &lock);
+ godot_js_wrapper_object_set(_js_id, name.utf8().get_data(), type, &exchange);
+ if (lock) {
+ _free_lock(&lock, type);
+ }
+ return true;
+}
+
+bool JavaScriptObjectImpl::_get(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V_MSG(!_js_id, false, "Invalid JS instance");
+ const String name = p_name;
+ godot_js_wrapper_ex exchange;
+ int type = godot_js_wrapper_object_get(_js_id, &exchange, name.utf8().get_data());
+ r_ret = _js2variant(type, &exchange);
+ return true;
+}
+
+Variant JavaScriptObjectImpl::getvar(const Variant &p_key, bool *r_valid) const {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ godot_js_wrapper_ex exchange;
+ void *lock = nullptr;
+ const Variant *v = &p_key;
+ int prop_type = _variant2js((const void **)&v, 0, &exchange, &lock);
+ int type = godot_js_wrapper_object_getvar(_js_id, prop_type, &exchange);
+ if (lock) {
+ _free_lock(&lock, prop_type);
+ }
+ if (type < 0) {
+ return Variant();
+ }
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return _js2variant(type, &exchange);
+}
+
+void JavaScriptObjectImpl::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ godot_js_wrapper_ex kex, vex;
+ void *klock = nullptr;
+ void *vlock = nullptr;
+ const Variant *kv = &p_key;
+ const Variant *vv = &p_value;
+ int ktype = _variant2js((const void **)&kv, 0, &kex, &klock);
+ int vtype = _variant2js((const void **)&vv, 0, &vex, &vlock);
+ int ret = godot_js_wrapper_object_setvar(_js_id, ktype, &kex, vtype, &vex);
+ if (klock) {
+ _free_lock(&klock, ktype);
+ }
+ if (vlock) {
+ _free_lock(&vlock, vtype);
+ }
+ if (ret == 0 && r_valid) {
+ *r_valid = true;
+ }
+}
+
+void JavaScriptObjectImpl::_get_property_list(List<PropertyInfo> *p_list) const {
+}
+
+void JavaScriptObjectImpl::_free_lock(void **p_lock, int p_type) {
+ ERR_FAIL_COND_MSG(*p_lock == nullptr, "No lock to free!");
+ const Variant::Type type = (Variant::Type)p_type;
+ switch (type) {
+ case Variant::STRING: {
+ CharString *cs = (CharString *)(*p_lock);
+ memdelete(cs);
+ *p_lock = nullptr;
+ } break;
+ default:
+ ERR_FAIL_MSG("Unknown lock type to free. Likely a bug.");
+ }
+}
+
+Variant JavaScriptObjectImpl::_js2variant(int p_type, godot_js_wrapper_ex *p_val) {
+ Variant::Type type = (Variant::Type)p_type;
+ switch (type) {
+ case Variant::BOOL:
+ return Variant((bool)p_val->i);
+ case Variant::INT:
+ return p_val->i;
+ case Variant::FLOAT:
+ return p_val->r;
+ case Variant::STRING: {
+ String out((const char *)p_val->p);
+ free(p_val->p);
+ return out;
+ }
+ case Variant::OBJECT: {
+ return memnew(JavaScriptObjectImpl(p_val->i));
+ }
+ default:
+ return Variant();
+ }
+}
+
+int JavaScriptObjectImpl::_variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock) {
+ const Variant **args = (const Variant **)p_args;
+ const Variant *v = args[p_pos];
+ Variant::Type type = v->get_type();
+ switch (type) {
+ case Variant::BOOL:
+ r_val->i = v->operator bool() ? 1 : 0;
+ break;
+ case Variant::INT: {
+ const int64_t tmp = v->operator int64_t();
+ if (tmp >= 1 << 31) {
+ r_val->r = (double)tmp;
+ return Variant::FLOAT;
+ }
+ r_val->i = v->operator int64_t();
+ } break;
+ case Variant::FLOAT:
+ r_val->r = v->operator real_t();
+ break;
+ case Variant::STRING: {
+ CharString *cs = memnew(CharString(v->operator String().utf8()));
+ r_val->p = (void *)cs->get_data();
+ *p_lock = (void *)cs;
+ } break;
+ case Variant::OBJECT: {
+ JavaScriptObject *js_obj = Object::cast_to<JavaScriptObject>(v->operator Object *());
+ r_val->i = js_obj != nullptr ? ((JavaScriptObjectImpl *)js_obj)->_js_id : 0;
+ } break;
+ default:
+ break;
+ }
+ return type;
+}
+
+Variant JavaScriptObjectImpl::call(const StringName &p_method, const Variant **p_args, int p_argc, Callable::CallError &r_error) {
+ godot_js_wrapper_ex exchange;
+ const String method = p_method;
+ void *lock = nullptr;
+ const int type = godot_js_wrapper_object_call(_js_id, method.utf8().get_data(), (void **)p_args, p_argc, &_variant2js, &exchange, &lock, &_free_lock);
+ r_error.error = Callable::CallError::CALL_OK;
+ if (type < 0) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return Variant();
+ }
+ return _js2variant(type, &exchange);
+}
+
+void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
+ const JavaScriptObjectImpl *obj = (JavaScriptObjectImpl *)p_ref;
+ ERR_FAIL_COND_MSG(obj->_callable.is_null(), "JavaScript callback failed.");
+ Vector<const Variant *> argp;
+ Array arg_arr;
+ for (int i = 0; i < p_argc; i++) {
+ godot_js_wrapper_ex exchange;
+ exchange.i = i;
+ int type = godot_js_wrapper_object_getvar(p_args_id, Variant::INT, &exchange);
+ arg_arr.push_back(_js2variant(type, &exchange));
+ }
+ Variant arg = arg_arr;
+ const Variant *argv[1] = { &arg };
+ Callable::CallError err;
+ Variant ret;
+ obj->_callable.call(argv, 1, ret, err);
+}
+
+Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+ Ref<JavaScriptObjectImpl> out = memnew(JavaScriptObjectImpl);
+ out->_callable = p_callable;
+ out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::_callback);
+ return out;
+}
+
+Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+ int js_id = godot_js_wrapper_interface_get(p_interface.utf8().get_data());
+ ERR_FAIL_COND_V_MSG(!js_id, Ref<JavaScriptObject>(), "No interface '" + p_interface + "' registered.");
+ return Ref<JavaScriptObject>(memnew(JavaScriptObjectImpl(js_id)));
+}
+
+Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Ref<JavaScriptObject>();
+ }
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ return Ref<JavaScriptObject>();
+ }
+ godot_js_wrapper_ex exchange;
+ const String object = *p_args[0];
+ void *lock = nullptr;
+ const Variant **args = p_argcount > 1 ? &p_args[1] : nullptr;
+ const int type = godot_js_wrapper_create_object(object.utf8().get_data(), (void **)args, p_argcount - 1, &JavaScriptObjectImpl::_variant2js, &exchange, &lock, &JavaScriptObjectImpl::_free_lock);
+ r_error.error = Callable::CallError::CALL_OK;
+ if (type < 0) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return Ref<JavaScriptObject>();
+ }
+ return JavaScriptObjectImpl::_js2variant(type, &exchange);
+}
+
+extern "C" {
+union js_eval_ret {
+ uint32_t b;
+ double d;
+ char *s;
+};
+
+extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
+}
+
+void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
+ PackedByteArray *arr = (PackedByteArray *)p_arr;
+ VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write;
+ arr->resize(p_len);
+ *write = arr->write;
+ return arr->ptrw();
+}
+
+Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
+ union js_eval_ret js_data;
+ PackedByteArray arr;
+ VectorWriteProxy<uint8_t> arr_write;
+
+ Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write));
+
+ switch (return_type) {
+ case Variant::BOOL:
+ return js_data.b;
+ case Variant::FLOAT:
+ return js_data.d;
+ case Variant::STRING: {
+ String str = String::utf8(js_data.s);
+ free(js_data.s); // Must free the string allocated in JS.
+ return str;
+ }
+ case Variant::PACKED_BYTE_ARRAY:
+ arr_write = VectorWriteProxy<uint8_t>();
+ return arr;
+ default:
+ return Variant();
+ }
+}
+#endif // JAVASCRIPT_EVAL_ENABLED
diff --git a/platform/javascript/js/libs/library_godot_eval.js b/platform/javascript/js/libs/library_godot_eval.js
deleted file mode 100644
index 9ab392b813..0000000000
--- a/platform/javascript/js/libs/library_godot_eval.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*************************************************************************/
-/* library_godot_eval.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-const GodotEval = {
- godot_js_eval__deps: ['$GodotRuntime'],
- godot_js_eval__sig: 'iiiiiii',
- godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) {
- const js_code = GodotRuntime.parseString(p_js);
- let eval_ret = null;
- try {
- if (p_use_global_ctx) {
- // indirect eval call grants global execution context
- const global_eval = eval; // eslint-disable-line no-eval
- eval_ret = global_eval(js_code);
- } else {
- eval_ret = eval(js_code); // eslint-disable-line no-eval
- }
- } catch (e) {
- GodotRuntime.error(e);
- }
-
- switch (typeof eval_ret) {
- case 'boolean':
- GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
- return 1; // BOOL
-
- case 'number':
- GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
- return 3; // REAL
-
- case 'string':
- GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
- return 4; // STRING
-
- case 'object':
- if (eval_ret === null) {
- break;
- }
-
- if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
- eval_ret = new Uint8Array(eval_ret.buffer);
- } else if (eval_ret instanceof ArrayBuffer) {
- eval_ret = new Uint8Array(eval_ret);
- }
- if (eval_ret instanceof Uint8Array) {
- const func = GodotRuntime.get_func(p_callback);
- const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length);
- HEAPU8.set(eval_ret, bytes_ptr);
- return 20; // POOL_BYTE_ARRAY
- }
- break;
-
- // no default
- }
- return 0; // NIL
- },
-};
-
-mergeInto(LibraryManager.library, GodotEval);
diff --git a/platform/javascript/js/libs/library_godot_javascript_singleton.js b/platform/javascript/js/libs/library_godot_javascript_singleton.js
new file mode 100644
index 0000000000..09ef4a1a5d
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_javascript_singleton.js
@@ -0,0 +1,333 @@
+/*************************************************************************/
+/* library_godot_eval.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+const GodotJSWrapper = {
+
+ $GodotJSWrapper__deps: ['$GodotRuntime', '$IDHandler'],
+ $GodotJSWrapper__postset: 'GodotJSWrapper.proxies = new Map();',
+ $GodotJSWrapper: {
+ proxies: null,
+
+ MyProxy: function (val) {
+ const id = IDHandler.add(this);
+ GodotJSWrapper.proxies.set(val, id);
+ let refs = 1;
+ this.ref = function () {
+ refs++;
+ };
+ this.unref = function () {
+ refs--;
+ if (refs === 0) {
+ IDHandler.remove(id);
+ GodotJSWrapper.proxies.delete(val);
+ }
+ };
+ this.get_val = function () {
+ return val;
+ };
+ this.get_id = function () {
+ return id;
+ };
+ },
+
+ get_proxied: function (val) {
+ const id = GodotJSWrapper.proxies.get(val);
+ if (id === undefined) {
+ const proxy = new GodotJSWrapper.MyProxy(val);
+ return proxy.get_id();
+ }
+ IDHandler.get(id).ref();
+ return id;
+ },
+
+ get_proxied_value: function (id) {
+ const proxy = IDHandler.get(id);
+ if (proxy === undefined) {
+ return undefined;
+ }
+ return proxy.get_val();
+ },
+
+ variant2js: function (type, val) {
+ switch (type) {
+ case 0:
+ return null;
+ case 1:
+ return !!GodotRuntime.getHeapValue(val, 'i64');
+ case 2:
+ return GodotRuntime.getHeapValue(val, 'i64');
+ case 3:
+ return GodotRuntime.getHeapValue(val, 'double');
+ case 4:
+ return GodotRuntime.parseString(GodotRuntime.getHeapValue(val, '*'));
+ case 21: // OBJECT
+ return GodotJSWrapper.get_proxied_value(GodotRuntime.getHeapValue(val, 'i64'));
+ default:
+ return undefined;
+ }
+ },
+
+ js2variant: function (p_val, p_exchange) {
+ if (p_val === undefined || p_val === null) {
+ return 0; // NIL
+ }
+ const type = typeof (p_val);
+ if (type === 'boolean') {
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'i64');
+ return 1; // BOOL
+ } else if (type === 'number') {
+ if (Number.isInteger(p_val)) {
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'i64');
+ return 2; // INT
+ }
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'double');
+ return 3; // REAL
+ } else if (type === 'string') {
+ const c_str = GodotRuntime.allocString(p_val);
+ GodotRuntime.setHeapValue(p_exchange, c_str, '*');
+ return 4; // STRING
+ }
+ const id = GodotJSWrapper.get_proxied(p_val);
+ GodotRuntime.setHeapValue(p_exchange, id, 'i64');
+ return 21;
+ },
+ },
+
+ godot_js_wrapper_interface_get__sig: 'ii',
+ godot_js_wrapper_interface_get: function (p_name) {
+ const name = GodotRuntime.parseString(p_name);
+ if (typeof (window[name]) !== 'undefined') {
+ return GodotJSWrapper.get_proxied(window[name]);
+ }
+ return 0;
+ },
+
+ godot_js_wrapper_object_get__sig: 'iiii',
+ godot_js_wrapper_object_get: function (p_id, p_exchange, p_prop) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return 0;
+ }
+ if (p_prop) {
+ const prop = GodotRuntime.parseString(p_prop);
+ try {
+ return GodotJSWrapper.js2variant(obj[prop], p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error getting variable ${prop} on object`, obj);
+ return 0; // NIL
+ }
+ }
+ return GodotJSWrapper.js2variant(obj, p_exchange);
+ },
+
+ godot_js_wrapper_object_set__sig: 'viiii',
+ godot_js_wrapper_object_set: function (p_id, p_name, p_type, p_exchange) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return;
+ }
+ const name = GodotRuntime.parseString(p_name);
+ try {
+ obj[name] = GodotJSWrapper.variant2js(p_type, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error setting variable ${name} on object`, obj);
+ }
+ },
+
+ godot_js_wrapper_object_call__sig: 'iiiiiiiii',
+ godot_js_wrapper_object_call: function (p_id, p_method, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const method = GodotRuntime.parseString(p_method);
+ const convert = GodotRuntime.get_func(p_convert_callback);
+ const freeLock = GodotRuntime.get_func(p_free_lock_callback);
+ const args = new Array(p_argc);
+ for (let i = 0; i < p_argc; i++) {
+ const type = convert(p_args, i, p_exchange, p_lock);
+ const lock = GodotRuntime.getHeapValue(p_lock, '*');
+ args[i] = GodotJSWrapper.variant2js(type, p_exchange);
+ if (lock) {
+ freeLock(p_lock, type);
+ }
+ }
+ try {
+ const res = obj[method](...args);
+ return GodotJSWrapper.js2variant(res, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error calling method ${method} on:`, obj, 'error:', e);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_object_unref__sig: 'vi',
+ godot_js_wrapper_object_unref: function (p_id) {
+ const proxy = IDHandler.get(p_id);
+ if (proxy !== undefined) {
+ proxy.unref();
+ }
+ },
+
+ godot_js_wrapper_create_cb__sig: 'vii',
+ godot_js_wrapper_create_cb: function (p_ref, p_func) {
+ const func = GodotRuntime.get_func(p_func);
+ let id = 0;
+ const cb = function () {
+ if (!GodotJSWrapper.get_proxied_value(id)) {
+ return;
+ }
+ const args = Array.from(arguments);
+ func(p_ref, GodotJSWrapper.get_proxied(args), args.length);
+ };
+ id = GodotJSWrapper.get_proxied(cb);
+ return id;
+ },
+
+ godot_js_wrapper_object_getvar__sig: 'iiii',
+ godot_js_wrapper_object_getvar: function (p_id, p_type, p_exchange) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const prop = GodotJSWrapper.variant2js(p_type, p_exchange);
+ if (prop === undefined || prop === null) {
+ return -1;
+ }
+ try {
+ return GodotJSWrapper.js2variant(obj[prop], p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error getting variable ${prop} on object`, obj, e);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_object_setvar__sig: 'iiiiii',
+ godot_js_wrapper_object_setvar: function (p_id, p_key_type, p_key_ex, p_val_type, p_val_ex) {
+ const obj = GodotJSWrapper.get_proxied_value(p_id);
+ if (obj === undefined) {
+ return -1;
+ }
+ const key = GodotJSWrapper.variant2js(p_key_type, p_key_ex);
+ try {
+ obj[key] = GodotJSWrapper.variant2js(p_val_type, p_val_ex);
+ return 0;
+ } catch (e) {
+ GodotRuntime.error(`Error setting variable ${key} on object`, obj);
+ return -1;
+ }
+ },
+
+ godot_js_wrapper_create_object__sig: 'iiiiiiii',
+ godot_js_wrapper_create_object: function (p_object, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
+ const name = GodotRuntime.parseString(p_object);
+ if (typeof (window[name]) === 'undefined') {
+ return -1;
+ }
+ const convert = GodotRuntime.get_func(p_convert_callback);
+ const freeLock = GodotRuntime.get_func(p_free_lock_callback);
+ const args = new Array(p_argc);
+ for (let i = 0; i < p_argc; i++) {
+ const type = convert(p_args, i, p_exchange, p_lock);
+ const lock = GodotRuntime.getHeapValue(p_lock, '*');
+ args[i] = GodotJSWrapper.variant2js(type, p_exchange);
+ if (lock) {
+ freeLock(p_lock, type);
+ }
+ }
+ try {
+ const res = new window[name](...args);
+ return GodotJSWrapper.js2variant(res, p_exchange);
+ } catch (e) {
+ GodotRuntime.error(`Error calling constructor ${name} with args:`, args, 'error:', e);
+ return -1;
+ }
+ },
+};
+
+autoAddDeps(GodotJSWrapper, '$GodotJSWrapper');
+mergeInto(LibraryManager.library, GodotJSWrapper);
+
+const GodotEval = {
+ godot_js_eval__deps: ['$GodotRuntime'],
+ godot_js_eval__sig: 'iiiiiii',
+ godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) {
+ const js_code = GodotRuntime.parseString(p_js);
+ let eval_ret = null;
+ try {
+ if (p_use_global_ctx) {
+ // indirect eval call grants global execution context
+ const global_eval = eval; // eslint-disable-line no-eval
+ eval_ret = global_eval(js_code);
+ } else {
+ eval_ret = eval(js_code); // eslint-disable-line no-eval
+ }
+ } catch (e) {
+ GodotRuntime.error(e);
+ }
+
+ switch (typeof eval_ret) {
+ case 'boolean':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
+ return 1; // BOOL
+
+ case 'number':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
+ return 3; // REAL
+
+ case 'string':
+ GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
+ return 4; // STRING
+
+ case 'object':
+ if (eval_ret === null) {
+ break;
+ }
+
+ if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
+ eval_ret = new Uint8Array(eval_ret.buffer);
+ } else if (eval_ret instanceof ArrayBuffer) {
+ eval_ret = new Uint8Array(eval_ret);
+ }
+ if (eval_ret instanceof Uint8Array) {
+ const func = GodotRuntime.get_func(p_callback);
+ const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length);
+ HEAPU8.set(eval_ret, bytes_ptr);
+ return 20; // POOL_BYTE_ARRAY
+ }
+ break;
+
+ // no default
+ }
+ return 0; // NIL
+ },
+};
+
+mergeInto(LibraryManager.library, GodotEval);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 09e1f9461c..e7d3c9552e 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -164,7 +164,12 @@ bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
String OS_LinuxBSD::get_config_path() const {
if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
+ if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.config` or `.` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".config") : ".";
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".config");
} else {
@@ -174,7 +179,12 @@ String OS_LinuxBSD::get_config_path() const {
String OS_LinuxBSD::get_data_path() const {
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
+ if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.local/share` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".local/share") : get_config_path();
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".local/share");
} else {
@@ -184,7 +194,12 @@ String OS_LinuxBSD::get_data_path() const {
String OS_LinuxBSD::get_cache_path() const {
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
+ if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.cache` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file(".cache") : get_config_path();
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file(".cache");
} else {
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index e6feda5a9b..e199ea1b0d 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -188,8 +188,14 @@ MainLoop *OS_OSX::get_main_loop() const {
}
String OS_OSX::get_config_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
+ if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file("Library/Application Support") : ".";
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Application Support");
} else {
@@ -198,16 +204,28 @@ String OS_OSX::get_config_path() const {
}
String OS_OSX::get_data_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
+ if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+ return get_config_path();
+ }
} else {
return get_config_path();
}
}
String OS_OSX::get_cache_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
+ if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Libary/Caches` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("HOME") ? get_environment("HOME").plus_file("Library/Caches") : get_config_path();
+ }
} else if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Caches");
} else {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index f517190a89..e0259882b0 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -631,8 +631,14 @@ MainLoop *OS_Windows::get_main_loop() const {
}
String OS_Windows::get_config_path() const {
- if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not?
- return get_environment("XDG_CONFIG_HOME");
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
+ if (has_environment("XDG_CONFIG_HOME")) {
+ if (get_environment("XDG_CONFIG_HOME").is_abs_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification.");
+ return has_environment("APPDATA") ? get_environment("APPDATA") : ".";
+ }
} else if (has_environment("APPDATA")) {
return get_environment("APPDATA");
} else {
@@ -641,16 +647,28 @@ String OS_Windows::get_config_path() const {
}
String OS_Windows::get_data_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
+ if (get_environment("XDG_DATA_HOME").is_abs_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+ return get_config_path();
+ }
} else {
return get_config_path();
}
}
String OS_Windows::get_cache_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
+ if (get_environment("XDG_CACHE_HOME").is_abs_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification.");
+ return has_environment("TEMP") ? get_environment("TEMP") : get_config_path();
+ }
} else if (has_environment("TEMP")) {
return get_environment("TEMP");
} else {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 0afead0863..188f8d300d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -64,13 +64,13 @@ int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const {
}
Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const {
- ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_ATLAS_COORDS);
return pattern[p_coords].get_atlas_coords();
}
int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const {
- ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_TILE_ALTERNATIVE);
return pattern[p_coords].alternative_tile;
}
@@ -113,7 +113,7 @@ void TileMapPattern::clear() {
};
void TileMapPattern::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(-1), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell);
ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id);
@@ -520,12 +520,12 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i
Vector2i atlas_coords = p_atlas_coords;
int alternative_tile = p_alternative_tile;
- if ((source_id == -1 || atlas_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) &&
- (source_id != -1 || atlas_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE)) {
+ if ((source_id == -1 || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) &&
+ (source_id != -1 || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) {
WARN_PRINT("Setting a cell a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly.");
source_id = -1;
- atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
- alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ atlas_coords = TileSetSource::INVALID_ATLAS_COORDS;
+ alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
}
if (!E && source_id == -1) {
@@ -602,7 +602,7 @@ Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords) const {
const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
- return TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ return TileSetSource::INVALID_ATLAS_COORDS;
}
return E->get().get_atlas_coords();
@@ -613,7 +613,7 @@ int TileMap::get_cell_alternative_tile(const Vector2i &p_coords) const {
const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
- return TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ return TileSetSource::INVALID_TILE_ALTERNATIVE;
}
return E->get().alternative_tile;
@@ -721,7 +721,7 @@ void TileMap::fix_invalid_tiles() {
for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
TileSetSource *source = *tile_set->get_source(E->get().source_id);
if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
- set_cell(E->key(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ set_cell(E->key(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
}
}
@@ -1716,7 +1716,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMap::get_cell_source_id);
ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMap::get_cell_atlas_coords);
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMap::get_cell_alternative_tile);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index e9dbccbdb9..2703e1980e 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -48,7 +48,7 @@ union TileMapCell {
};
uint64_t _u64t;
- TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) {
source_id = p_source_id;
set_atlas_coords(p_atlas_coords);
alternative_tile = p_alternative_tile;
@@ -112,16 +112,19 @@ struct TileMapQuadrant {
// Debug.
RID debug_canvas_item;
- // Rendering
+ // Rendering.
List<RID> canvas_items;
List<RID> occluders;
// Physics.
List<RID> bodies;
- // Navigation
+ // Navigation.
Map<Vector2i, Vector<RID>> navigation_regions;
+ // Scenes.
+ Map<Vector2i, String> scenes;
+
void operator=(const TileMapQuadrant &q) {
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
@@ -248,7 +251,7 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
- void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
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;
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 93851aaedd..e895d18604 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -517,7 +517,7 @@ Vector3 RigidBody3D::get_angular_velocity() const {
return angular_velocity;
}
-Basis RigidBody3D::get_inverse_inertia_tensor() {
+Basis RigidBody3D::get_inverse_inertia_tensor() const {
return inverse_inertia_tensor;
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index cf18b8ca82..818ff97730 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -183,7 +183,7 @@ public:
void set_angular_velocity(const Vector3 &p_velocity);
Vector3 get_angular_velocity() const override;
- Basis get_inverse_inertia_tensor();
+ Basis get_inverse_inertia_tensor() const;
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index f24ea7e39b..b9c1659fd1 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -657,6 +657,7 @@ void register_scene_types() {
ClassDB::register_class<TileSet>();
ClassDB::register_virtual_class<TileSetSource>();
ClassDB::register_class<TileSetAtlasSource>();
+ ClassDB::register_class<TileSetScenesCollectionSource>();
ClassDB::register_class<TileData>();
ClassDB::register_class<TileMap>();
ClassDB::register_class<ParallaxBackground>();
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index c4b8a56f54..3cc9627af3 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -106,15 +106,15 @@ void TileSet::_compute_next_source_id() {
}
// Sources management
-int TileSet::add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_atlas_source_id_override) {
- ERR_FAIL_COND_V(!p_tile_atlas_source.is_valid(), -1);
+int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source_id_override) {
+ ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), -1);
ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), -1, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override));
int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id;
- sources[new_source_id] = p_tile_atlas_source;
+ sources[new_source_id] = p_tile_set_source;
source_ids.append(new_source_id);
source_ids.sort();
- p_tile_atlas_source->set_tile_set(this);
+ p_tile_set_source->set_tile_set(this);
_compute_next_source_id();
sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed));
@@ -668,8 +668,8 @@ void TileSet::reset_state() {
custom_data_layers.clear();
}
-const Vector2i TileSetAtlasSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
-const int TileSetAtlasSource::INVALID_TILE_ALTERNATIVE = -1;
+const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
+const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1;
#ifndef DISABLE_DEPRECATED
void TileSet::compatibility_conversion() {
@@ -1086,7 +1086,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
} else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) {
- // Create atlas if it does not exists.
+ // Create source only if it does not exists.
int source_id = components[1].to_int();
if (!has_source(source_id)) {
@@ -1399,10 +1399,11 @@ void TileSet::_bind_methods() {
TileSet::TileSet() {
// Instanciatie and list all plugins.
- tile_set_plugins_vector.append(memnew(TileSetAtlasPluginRendering));
- tile_set_plugins_vector.append(memnew(TileSetAtlasPluginPhysics));
- tile_set_plugins_vector.append(memnew(TileSetAtlasPluginTerrain));
- tile_set_plugins_vector.append(memnew(TileSetAtlasPluginNavigation));
+ tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering));
+ tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics));
+ tile_set_plugins_vector.append(memnew(TileSetPluginAtlasTerrain));
+ tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation));
+ tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections));
}
TileSet::~TileSet() {
@@ -1530,13 +1531,13 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value)
// Compute the vector2i if we have coordinates.
Vector<String> coords_split = components[0].split(":");
- Vector2i coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ Vector2i coords = TileSetSource::INVALID_ATLAS_COORDS;
if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) {
coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int());
}
// Properties.
- if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
// Create the tile if needed.
if (!has_tile(coords)) {
create_tile(coords);
@@ -1549,7 +1550,7 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value)
tiles[coords].next_alternative_id = p_value;
} else if (components[1].is_valid_integer()) {
int alternative_id = components[1].to_int();
- if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
// Create the alternative if needed ?
if (!has_alternative_tile(coords, alternative_id)) {
create_alternative_tile(coords, alternative_id);
@@ -1594,7 +1595,7 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
return true;
} else if (components[1].is_valid_integer()) {
int alternative_id = components[1].to_int();
- if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
+ if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
if (components.size() >= 3) {
bool valid;
r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid);
@@ -1745,7 +1746,7 @@ int TileSetAtlasSource::get_tiles_count() const {
}
Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
- ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetSource::INVALID_ATLAS_COORDS);
return tiles_ids[p_index];
}
@@ -1798,7 +1799,7 @@ bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2
for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) {
for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) {
Vector2i coords = get_tile_at_coords(Vector2i(x, y));
- if (coords != p_atlas_coords && coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) {
return false;
}
}
@@ -1880,7 +1881,7 @@ void TileSetAtlasSource::clear_tiles_outside_texture() {
int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
- ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && (tiles[p_atlas_coords].alternatives.has(p_alternative_id_override) || tiles[p_atlas_coords].alternatives.has(p_alternative_id_override)), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
+ ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id;
@@ -2034,6 +2035,202 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo
};
}
+/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
+
+void TileSetScenesCollectionSource::_compute_next_alternative_id() {
+ while (scenes.has(next_scene_id)) {
+ next_scene_id = (next_scene_id % 1073741823) + 1; // 2 ** 30
+ };
+}
+
+int TileSetScenesCollectionSource::get_tiles_count() const {
+ return 1;
+}
+
+Vector2i TileSetScenesCollectionSource::get_tile_id(int p_tile_index) const {
+ ERR_FAIL_COND_V(p_tile_index != 0, TileSetSource::INVALID_ATLAS_COORDS);
+ return Vector2i();
+}
+
+bool TileSetScenesCollectionSource::has_tile(Vector2i p_atlas_coords) const {
+ return p_atlas_coords == Vector2i();
+}
+
+int TileSetScenesCollectionSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const {
+ return scenes_ids.size();
+}
+
+int TileSetScenesCollectionSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
+ ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+ ERR_FAIL_INDEX_V(p_index, scenes_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+
+ return scenes_ids[p_index];
+}
+
+bool TileSetScenesCollectionSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), false);
+ return scenes.has(p_alternative_tile);
+}
+
+int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_scene, int p_id_override) {
+ ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), -1, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override));
+
+ int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id;
+
+ scenes[new_scene_id] = SceneData();
+ scenes_ids.append(new_scene_id);
+ scenes_ids.sort();
+ set_scene_tile_scene(new_scene_id, p_packed_scene);
+ _compute_next_alternative_id();
+
+ emit_signal("changed");
+
+ return new_scene_id;
+}
+
+void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) {
+ ERR_FAIL_COND(p_new_id < 0);
+ ERR_FAIL_COND(!has_scene_tile_id(p_id));
+ ERR_FAIL_COND(has_scene_tile_id(p_new_id));
+
+ scenes[p_new_id] = SceneData();
+ scenes[p_new_id] = scenes[p_id];
+ scenes_ids.append(p_new_id);
+ scenes_ids.sort();
+
+ _compute_next_alternative_id();
+
+ scenes.erase(p_id);
+ scenes_ids.erase(p_id);
+
+ emit_signal("changed");
+}
+
+void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+ if (p_packed_scene.is_valid()) {
+ // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work.
+ ERR_FAIL_COND(!p_packed_scene->get_state().is_valid());
+ ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1);
+
+ // Check if it extends CanvasItem.
+ String type = p_packed_scene->get_state()->get_node_type(0);
+ bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D");
+ ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path()));
+
+ scenes[p_id].scene = p_packed_scene;
+ } else {
+ scenes[p_id].scene = Ref<PackedScene>();
+ }
+ emit_signal("changed");
+}
+
+Ref<PackedScene> TileSetScenesCollectionSource::get_scene_tile_scene(int p_id) const {
+ ERR_FAIL_COND_V(!scenes.has(p_id), Ref<PackedScene>());
+ return scenes[p_id].scene;
+}
+
+void TileSetScenesCollectionSource::set_scene_tile_display_placeholder(int p_id, bool p_display_placeholder) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+
+ scenes[p_id].display_placeholder = p_display_placeholder;
+
+ emit_signal("changed");
+}
+
+bool TileSetScenesCollectionSource::get_scene_tile_display_placeholder(int p_id) const {
+ ERR_FAIL_COND_V(!scenes.has(p_id), false);
+ return scenes[p_id].display_placeholder;
+}
+
+void TileSetScenesCollectionSource::remove_scene_tile(int p_id) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+
+ scenes.erase(p_id);
+ scenes_ids.erase(p_id);
+ emit_signal("changed");
+}
+
+int TileSetScenesCollectionSource::get_next_scene_tile_id() const {
+ return next_scene_id;
+}
+
+bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_integer()) {
+ int scene_id = components[1].to_int();
+ if (components.size() >= 3 && components[2] == "scene") {
+ if (has_scene_tile_id(scene_id)) {
+ set_scene_tile_scene(scene_id, p_value);
+ } else {
+ create_scene_tile(p_value, scene_id);
+ }
+ return true;
+ } else if (components.size() >= 3 && components[2] == "display_placeholder") {
+ if (!has_scene_tile_id(scene_id)) {
+ create_scene_tile(p_value, scene_id);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool TileSetScenesCollectionSource::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_integer() && scenes.has(components[1].to_int())) {
+ if (components.size() >= 3 && components[2] == "scene") {
+ r_ret = scenes[components[1].to_int()].scene;
+ return true;
+ } else if (components.size() >= 3 && components[2] == "display_placeholder") {
+ r_ret = scenes[components[1].to_int()].scene;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TileSetScenesCollectionSource::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < scenes_ids.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("scenes/%d/scene", scenes_ids[i]), PROPERTY_HINT_RESOURCE_TYPE, "TileSetScenesCollectionSource"));
+
+ PropertyInfo property_info = PropertyInfo(Variant::BOOL, vformat("scenes/%d/display_placeholder", scenes_ids[i]));
+ if (scenes[scenes_ids[i]].display_placeholder == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+}
+
+void TileSetScenesCollectionSource::_bind_methods() {
+ // Base tiles
+ ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id);
+ ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile);
+
+ // Alternative tiles
+ ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id);
+ ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile);
+
+ ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("set_scene_tile_id", "id", "new_id"), &TileSetScenesCollectionSource::set_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("set_scene_tile_scene", "id", "packed_scene"), &TileSetScenesCollectionSource::set_scene_tile_scene);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_scene", "id"), &TileSetScenesCollectionSource::get_scene_tile_scene);
+ ClassDB::bind_method(D_METHOD("set_scene_tile_display_placeholder", "id", "display_placeholder"), &TileSetScenesCollectionSource::set_scene_tile_display_placeholder);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_display_placeholder", "id"), &TileSetScenesCollectionSource::get_scene_tile_display_placeholder);
+ ClassDB::bind_method(D_METHOD("remove_scene_tile", "id"), &TileSetScenesCollectionSource::remove_scene_tile);
+ ClassDB::bind_method(D_METHOD("get_next_scene_tile_id"), &TileSetScenesCollectionSource::get_next_scene_tile_id);
+}
+
/////////////////////////////// TileData //////////////////////////////////////
void TileData::set_tile_set(const TileSet *p_tile_set) {
@@ -2834,10 +3031,10 @@ void TileData::_bind_methods() {
ADD_SIGNAL(MethodInfo("changed"));
}
-/////////////////////////////// TileSetAtlasPluginTerrain //////////////////////////////////////
+/////////////////////////////// TileSetPluginAtlasTerrain //////////////////////////////////////
// --- PLUGINS ---
-void TileSetAtlasPluginTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
Rect2 bit_rect;
bit_rect.size = Vector2(p_size) / 3;
switch (p_bit) {
@@ -2872,7 +3069,7 @@ void TileSetAtlasPluginTerrain::_draw_square_corner_or_side_terrain_bit(CanvasIt
p_canvas_item->draw_rect(bit_rect, p_color);
}
-void TileSetAtlasPluginTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -2919,7 +3116,7 @@ void TileSetAtlasPluginTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_ca
}
}
-void TileSetAtlasPluginTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -2958,7 +3155,7 @@ void TileSetAtlasPluginTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canv
}
}
-void TileSetAtlasPluginTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3021,7 +3218,7 @@ void TileSetAtlasPluginTerrain::_draw_isometric_corner_or_side_terrain_bit(Canva
}
}
-void TileSetAtlasPluginTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3068,7 +3265,7 @@ void TileSetAtlasPluginTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p
}
}
-void TileSetAtlasPluginTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+void TileSetPluginAtlasTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3107,7 +3304,7 @@ void TileSetAtlasPluginTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_c
}
}
-void TileSetAtlasPluginTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+void TileSetPluginAtlasTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3272,7 +3469,7 @@ void TileSetAtlasPluginTerrain::_draw_half_offset_corner_or_side_terrain_bit(Can
}
}
-void TileSetAtlasPluginTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+void TileSetPluginAtlasTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3383,7 +3580,7 @@ void TileSetAtlasPluginTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem
}
}
-void TileSetAtlasPluginTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+void TileSetPluginAtlasTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
PackedColorArray color_array;
color_array.push_back(p_color);
@@ -3498,7 +3695,7 @@ void TileSetAtlasPluginTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p
} \
}
-void TileSetAtlasPluginTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) {
+void TileSetPluginAtlasTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) {
ERR_FAIL_COND(!p_tile_set);
ERR_FAIL_COND(!p_tile_data);
@@ -3632,9 +3829,9 @@ void TileSetAtlasPluginTerrain::draw_terrains(CanvasItem *p_canvas_item, Transfo
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
-/////////////////////////////// TileSetAtlasPluginRendering //////////////////////////////////////
+/////////////////////////////// TileSetPluginAtlasRendering //////////////////////////////////////
-void TileSetAtlasPluginRendering::tilemap_notification(TileMap *p_tile_map, int p_what) {
+void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) {
switch (p_what) {
case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
bool visible = p_tile_map->is_visible_in_tree();
@@ -3672,7 +3869,7 @@ void TileSetAtlasPluginRendering::tilemap_notification(TileMap *p_tile_map, int
}
}
-void TileSetAtlasPluginRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) {
+void TileSetPluginAtlasRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
@@ -3687,6 +3884,12 @@ void TileSetAtlasPluginRendering::draw_tile(RID p_canvas_item, Vector2i p_positi
return;
}
+ // Check if we are in the texture, return otherwise.
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
+ return;
+ }
+
// Get tile data.
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
@@ -3724,7 +3927,7 @@ void TileSetAtlasPluginRendering::draw_tile(RID p_canvas_item, Vector2i p_positi
}
}
-void TileSetAtlasPluginRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+void TileSetPluginAtlasRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
ERR_FAIL_COND(!p_tile_map);
ERR_FAIL_COND(!p_tile_map->is_inside_tree());
Ref<TileSet> tile_set = p_tile_map->get_tileset();
@@ -3858,14 +4061,14 @@ void TileSetAtlasPluginRendering::update_dirty_quadrants(TileMap *p_tile_map, Se
}
}
-void TileSetAtlasPluginRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
quadrant_order_dirty = true;
}
-void TileSetAtlasPluginRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
// Free the canvas items.
for (List<RID>::Element *E = p_quadrant->canvas_items.front(); E; E = E->next()) {
RenderingServer::get_singleton()->free(E->get());
@@ -3879,9 +4082,60 @@ void TileSetAtlasPluginRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQ
p_quadrant->occluders.clear();
}
-/////////////////////////////// TileSetAtlasPluginPhysics //////////////////////////////////////
+void TileSetPluginAtlasRendering::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+
+ // Draw a placeholder for scenes needing one.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = p_tile_map->get_cell(E_cell->get());
-void TileSetAtlasPluginPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) {
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
+ // Generate a random color from the hashed values of the tiles.
+ Array to_hash;
+ to_hash.push_back(c.source_id);
+ to_hash.push_back(c.get_atlas_coords());
+ to_hash.push_back(c.alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw a placeholder tile.
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////// TileSetPluginAtlasPhysics //////////////////////////////////////
+
+void TileSetPluginAtlasPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) {
switch (p_what) {
case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
// Update the bodies transforms.
@@ -3905,7 +4159,7 @@ void TileSetAtlasPluginPhysics::tilemap_notification(TileMap *p_tile_map, int p_
}
}
-void TileSetAtlasPluginPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
ERR_FAIL_COND(!p_tile_map);
ERR_FAIL_COND(!p_tile_map->is_inside_tree());
Ref<TileSet> tile_set = p_tile_map->get_tileset();
@@ -3971,7 +4225,7 @@ void TileSetAtlasPluginPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self
}
}
-void TileSetAtlasPluginPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
@@ -4016,7 +4270,7 @@ void TileSetAtlasPluginPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuad
}
}
-void TileSetAtlasPluginPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
// Remove a quadrant.
for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
@@ -4024,7 +4278,7 @@ void TileSetAtlasPluginPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQua
p_quadrant->bodies.clear();
}
-void TileSetAtlasPluginPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
// Draw the debug collision shapes.
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
@@ -4071,9 +4325,9 @@ void TileSetAtlasPluginPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap
}
};
-/////////////////////////////// TileSetAtlasPluginNavigation //////////////////////////////////////
+/////////////////////////////// TileSetPluginAtlasNavigation //////////////////////////////////////
-void TileSetAtlasPluginNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) {
+void TileSetPluginAtlasNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) {
switch (p_what) {
case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
if (p_tile_map->is_inside_tree()) {
@@ -4098,7 +4352,7 @@ void TileSetAtlasPluginNavigation::tilemap_notification(TileMap *p_tile_map, int
}
}
-void TileSetAtlasPluginNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+void TileSetPluginAtlasNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
ERR_FAIL_COND(!p_tile_map);
ERR_FAIL_COND(!p_tile_map->is_inside_tree());
Ref<TileSet> tile_set = p_tile_map->get_tileset();
@@ -4169,7 +4423,7 @@ void TileSetAtlasPluginNavigation::update_dirty_quadrants(TileMap *p_tile_map, S
}
}
-void TileSetAtlasPluginNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
// Clear navigation shapes in the quadrant.
for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) {
for (int i = 0; i < E->get().size(); i++) {
@@ -4183,7 +4437,7 @@ void TileSetAtlasPluginNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMap
p_quadrant->navigation_regions.clear();
}
-void TileSetAtlasPluginNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
// Draw the debug collision shapes.
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
@@ -4240,11 +4494,129 @@ void TileSetAtlasPluginNavigation::draw_quadrant_debug(TileMap *p_tile_map, Tile
Vector<Color> colors;
colors.push_back(random_variation_color);
- RS::get_singleton()->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors);
+ rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////// TileSetPluginScenesCollections //////////////////////////////////////
+
+void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ // Clear the scenes.
+ for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) {
+ Node *node = p_tile_map->get_node(E->get());
+ if (node) {
+ node->queue_delete();
+ }
+ }
+
+ q.scenes.clear();
+
+ // Recreate the scenes.
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = p_tile_map->get_cell(E_cell->get());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scenes_collection_source) {
+ Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile);
+ if (packed_scene.is_valid()) {
+ Node *scene = packed_scene->instance();
+ p_tile_map->add_child(scene);
+ Control *scene_as_control = Object::cast_to<Control>(scene);
+ Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene);
+ if (scene_as_control) {
+ scene_as_control->set_position(p_tile_map->map_to_world(E_cell->get()) + scene_as_control->get_position());
+ } else if (scene_as_node2d) {
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()));
+ scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform());
}
+ q.scenes[E_cell->get()] = scene->get_name();
}
}
}
}
+
+ q_list_element = q_list_element->next();
+ }
+}
+
+void TileSetPluginScenesCollections::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Clear the scenes.
+ for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) {
+ Node *node = p_tile_map->get_node(E->get());
+ if (node) {
+ node->queue_delete();
+ }
+ }
+
+ p_quadrant->scenes.clear();
+}
+
+void TileSetPluginScenesCollections::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+
+ // Draw a placeholder for scenes needing one.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = p_tile_map->get_cell(E_cell->get());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scenes_collection_source) {
+ if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) {
+ // Generate a random color from the hashed values of the tiles.
+ Array to_hash;
+ to_hash.push_back(c.source_id);
+ to_hash.push_back(c.alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw a placeholder tile.
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
+ }
+ }
+ }
}
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 20cf183a20..ae80299d85 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -57,10 +57,10 @@ class TileData;
// Forward-declare the plugins.
class TileSetPlugin;
-class TileSetAtlasPluginRendering;
-class TileSetAtlasPluginPhysics;
-class TileSetAtlasPluginNavigation;
-class TileSetAtlasPluginTerrain;
+class TileSetPluginAtlasRendering;
+class TileSetPluginAtlasPhysics;
+class TileSetPluginAtlasNavigation;
+class TileSetPluginAtlasTerrain;
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
@@ -264,7 +264,7 @@ public:
int get_next_source_id() const;
int get_source_count() const;
int get_source_id(int p_index) const;
- int add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_source_id_override = -1);
+ 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);
bool has_source(int p_source_id) const;
@@ -338,6 +338,9 @@ protected:
const TileSet *tile_set = nullptr;
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);
virtual void notify_tile_data_properties_should_change(){};
@@ -358,9 +361,6 @@ class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
public:
- static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
- static const int INVALID_TILE_ALTERNATIVE; // -1;
-
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
Vector2i texture_offset;
@@ -443,6 +443,52 @@ public:
~TileSetAtlasSource();
};
+class TileSetScenesCollectionSource : public TileSetSource {
+ GDCLASS(TileSetScenesCollectionSource, TileSetSource);
+
+private:
+ struct SceneData {
+ Ref<PackedScene> scene;
+ bool display_placeholder = false;
+ };
+ Vector<int> scenes_ids;
+ Map<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 sccessors. 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);
@@ -572,8 +618,8 @@ public:
virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
};
-class TileSetAtlasPluginRendering : public TileSetPlugin {
- GDCLASS(TileSetAtlasPluginRendering, TileSetPlugin);
+class TileSetPluginAtlasRendering : public TileSetPlugin {
+ GDCLASS(TileSetPluginAtlasRendering, TileSetPlugin);
private:
static constexpr float fp_adjust = 0.00001;
@@ -585,13 +631,14 @@ public:
virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
// Other.
static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
};
-class TileSetAtlasPluginTerrain : public TileSetPlugin {
- GDCLASS(TileSetAtlasPluginTerrain, TileSetPlugin);
+class TileSetPluginAtlasTerrain : public TileSetPlugin {
+ GDCLASS(TileSetPluginAtlasTerrain, TileSetPlugin);
private:
static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
@@ -607,13 +654,11 @@ private:
static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
public:
- //virtual void tilemap_notification(const TileMap * p_tile_map, int p_what);
-
static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data);
};
-class TileSetAtlasPluginPhysics : public TileSetPlugin {
- GDCLASS(TileSetAtlasPluginPhysics, TileSetPlugin);
+class TileSetPluginAtlasPhysics : public TileSetPlugin {
+ GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin);
public:
// Tilemap updates
@@ -624,14 +669,23 @@ public:
virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
};
-class TileSetAtlasPluginNavigation : public TileSetPlugin {
- GDCLASS(TileSetAtlasPluginNavigation, TileSetPlugin);
+class TileSetPluginAtlasNavigation : public TileSetPlugin {
+ GDCLASS(TileSetPluginAtlasNavigation, TileSetPlugin);
public:
// Tilemap updates
virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
- //virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+};
+
+class TileSetPluginScenesCollections : public TileSetPlugin {
+ GDCLASS(TileSetPluginScenesCollections, TileSetPlugin);
+
+public:
+ // Tilemap updates
+ virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
};
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 8b03565291..daed612b02 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -987,7 +987,7 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
while (i < ranges.size()) {
int j = i + 1;
while (j < ranges.size()) {
- if (Math::is_equal_approx(ranges[i].y, ranges[j].x, UNIT_EPSILON)) {
+ if (Math::is_equal_approx(ranges[i].y, ranges[j].x, (real_t)UNIT_EPSILON)) {
ranges.write[i].y = ranges[j].y;
ranges.remove(j);
continue;
diff --git a/tests/test_color.h b/tests/test_color.h
index eb8d7dcbd4..ad4a7cd3f2 100644
--- a/tests/test_color.h
+++ b/tests/test_color.h
@@ -101,13 +101,13 @@ TEST_CASE("[Color] Reading methods") {
const Color dark_blue = Color(0, 0, 0.5, 0.4);
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_h(), 240 / 360.0),
+ Math::is_equal_approx(dark_blue.get_h(), 240.0f / 360.0f),
"The returned HSV hue should match the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_s(), 1),
+ Math::is_equal_approx(dark_blue.get_s(), 1.0f),
"The returned HSV saturation should match the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_v(), 0.5),
+ Math::is_equal_approx(dark_blue.get_v(), 0.5f),
"The returned HSV value should match the expected value.");
}
diff --git a/tests/test_curve.h b/tests/test_curve.h
index 019941a7ce..3055cfd97b 100644
--- a/tests/test_curve.h
+++ b/tests/test_curve.h
@@ -83,13 +83,13 @@ TEST_CASE("[Curve] Custom curve with free tangents") {
Math::is_equal_approx(curve->interpolate(-0.1), 0),
"Custom free curve should return the expected value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.1), 0.352),
+ Math::is_equal_approx(curve->interpolate(0.1), (real_t)0.352),
"Custom free curve should return the expected value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.4), 0.352),
+ Math::is_equal_approx(curve->interpolate(0.4), (real_t)0.352),
"Custom free curve should return the expected value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.7), 0.896),
+ Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.896),
"Custom free curve should return the expected value at offset 0.1.");
CHECK_MESSAGE(
Math::is_equal_approx(curve->interpolate(1), 1),
@@ -102,13 +102,13 @@ TEST_CASE("[Curve] Custom curve with free tangents") {
Math::is_equal_approx(curve->interpolate_baked(-0.1), 0),
"Custom free curve should return the expected baked value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.1), 0.352),
+ Math::is_equal_approx(curve->interpolate_baked(0.1), (real_t)0.352),
"Custom free curve should return the expected baked value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.4), 0.352),
+ Math::is_equal_approx(curve->interpolate_baked(0.4), (real_t)0.352),
"Custom free curve should return the expected baked value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.7), 0.896),
+ Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.896),
"Custom free curve should return the expected baked value at offset 0.1.");
CHECK_MESSAGE(
Math::is_equal_approx(curve->interpolate_baked(1), 1),
@@ -172,13 +172,13 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
Math::is_equal_approx(curve->interpolate(-0.1), 0),
"Custom linear curve should return the expected value at offset -0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.1), 0.4),
+ Math::is_equal_approx(curve->interpolate(0.1), (real_t)0.4),
"Custom linear curve should return the expected value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.4), 0.4),
+ Math::is_equal_approx(curve->interpolate(0.4), (real_t)0.4),
"Custom linear curve should return the expected value at offset 0.4.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.7), 0.8),
+ Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.8),
"Custom linear curve should return the expected value at offset 0.7.");
CHECK_MESSAGE(
Math::is_equal_approx(curve->interpolate(1), 1),
@@ -191,13 +191,13 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
Math::is_equal_approx(curve->interpolate_baked(-0.1), 0),
"Custom linear curve should return the expected baked value at offset -0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.1), 0.4),
+ Math::is_equal_approx(curve->interpolate_baked(0.1), (real_t)0.4),
"Custom linear curve should return the expected baked value at offset 0.1.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.4), 0.4),
+ Math::is_equal_approx(curve->interpolate_baked(0.4), (real_t)0.4),
"Custom linear curve should return the expected baked value at offset 0.4.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.7), 0.8),
+ Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.8),
"Custom linear curve should return the expected baked value at offset 0.7.");
CHECK_MESSAGE(
Math::is_equal_approx(curve->interpolate_baked(1), 1),
@@ -210,10 +210,10 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
curve->remove_point(10);
ERR_PRINT_ON;
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate(0.7), 0.8),
+ Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.8),
"Custom free curve should return the expected value at offset 0.7 after removing point at invalid index 10.");
CHECK_MESSAGE(
- Math::is_equal_approx(curve->interpolate_baked(0.7), 0.8),
+ Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.8),
"Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10.");
}
} // namespace TestCurve
diff --git a/tests/test_expression.h b/tests/test_expression.h
index 0ef60d1a19..cb1d29389f 100644
--- a/tests/test_expression.h
+++ b/tests/test_expression.h
@@ -83,42 +83,42 @@ TEST_CASE("[Expression] Floating-point arithmetic") {
expression.parse("-123.456") == OK,
"Float identity should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), -123.456),
+ Math::is_equal_approx(double(expression.execute()), -123.456),
"Float identity should return the expected result.");
CHECK_MESSAGE(
expression.parse("2.0 + 3.0") == OK,
"Float addition should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 5),
+ Math::is_equal_approx(double(expression.execute()), 5),
"Float addition should return the expected result.");
CHECK_MESSAGE(
expression.parse("3.0 / 10") == OK,
"Float / integer division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 0.3),
+ Math::is_equal_approx(double(expression.execute()), 0.3),
"Float / integer division should return the expected result.");
CHECK_MESSAGE(
expression.parse("3 / 10.0") == OK,
"Basic integer / float division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 0.3),
+ Math::is_equal_approx(double(expression.execute()), 0.3),
"Basic integer / float division should return the expected result.");
CHECK_MESSAGE(
expression.parse("3.0 / 10.0") == OK,
"Float / float division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 0.3),
+ Math::is_equal_approx(double(expression.execute()), 0.3),
"Float / float division should return the expected result.");
CHECK_MESSAGE(
expression.parse("2.5 * (6.0 + 14.25) / 2.0 - 5.12345") == OK,
"Float multiplication-addition-subtraction-division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 20.18905),
+ Math::is_equal_approx(double(expression.execute()), 20.18905),
"Float multiplication-addition-subtraction-division should return the expected result.");
}
@@ -129,7 +129,7 @@ TEST_CASE("[Expression] Scientific notation") {
expression.parse("2.e5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 200'000),
+ Math::is_equal_approx(double(expression.execute()), 200'000),
"The expression should return the expected result.");
// The middle "e" is ignored here.
@@ -137,14 +137,14 @@ TEST_CASE("[Expression] Scientific notation") {
expression.parse("2e5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 25),
+ Math::is_equal_approx(double(expression.execute()), 25),
"The expression should return the expected result.");
CHECK_MESSAGE(
expression.parse("2e.5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 2),
+ Math::is_equal_approx(double(expression.execute()), 2),
"The expression should return the expected result.");
}
@@ -176,14 +176,14 @@ TEST_CASE("[Expression] Built-in functions") {
expression.parse("snapped(sin(0.5), 0.01)") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(float(expression.execute()), 0.48),
+ Math::is_equal_approx(double(expression.execute()), 0.48),
"`snapped(sin(0.5), 0.01)` should return the expected result.");
CHECK_MESSAGE(
expression.parse("pow(2.0, -2500)") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_zero_approx(float(expression.execute())),
+ Math::is_zero_approx(double(expression.execute())),
"`pow(2.0, -2500)` should return the expected result (asymptotically zero).");
}
@@ -410,7 +410,7 @@ TEST_CASE("[Expression] Unusual expressions") {
"The expression should parse successfully.");
ERR_PRINT_OFF;
CHECK_MESSAGE(
- Math::is_inf(float(expression.execute())),
+ Math::is_inf(double(expression.execute())),
"`-25.4 / 0` should return inf.");
ERR_PRINT_ON;
diff --git a/tests/test_json.h b/tests/test_json.h
index e652a8fced..f1cb4799dc 100644
--- a/tests/test_json.h
+++ b/tests/test_json.h
@@ -80,7 +80,7 @@ TEST_CASE("[JSON] Parsing single data types") {
err_line == 0,
"Parsing an integer number as JSON should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(result, 123'456),
+ (int)result == 123'456,
"Parsing an integer number as JSON should return the expected value.");
json.parse("0.123456", result, err_str, err_line);
@@ -155,7 +155,7 @@ TEST_CASE("[JSON] Parsing objects (dictionaries)") {
dictionary["bugs"] == Variant(),
"The parsed JSON should contain the expected values.");
CHECK_MESSAGE(
- Math::is_equal_approx(Dictionary(dictionary["apples"])["blue"], -20),
+ (int)Dictionary(dictionary["apples"])["blue"] == -20,
"The parsed JSON should contain the expected values.");
CHECK_MESSAGE(
dictionary["empty_object"].hash() == Dictionary().hash(),
diff --git a/tests/test_rect2.h b/tests/test_rect2.h
index 5d58428fc0..821aa69970 100644
--- a/tests/test_rect2.h
+++ b/tests/test_rect2.h
@@ -312,19 +312,19 @@ TEST_CASE("[Rect2i] Basic setters") {
TEST_CASE("[Rect2i] Area getters") {
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2i(0, 100, 1280, 720).get_area(), 921'600),
+ Rect2i(0, 100, 1280, 720).get_area() == 921'600,
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2i(0, 100, -1280, -720).get_area(), 921'600),
+ Rect2i(0, 100, -1280, -720).get_area() == 921'600,
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2i(0, 100, 1280, -720).get_area(), -921'600),
+ Rect2i(0, 100, 1280, -720).get_area() == -921'600,
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2i(0, 100, -1280, 720).get_area(), -921'600),
+ Rect2i(0, 100, -1280, 720).get_area() == -921'600,
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_zero_approx(Rect2i(0, 100, 0, 720).get_area()),
+ Rect2i(0, 100, 0, 720).get_area() == 0,
"get_area() should return the expected value.");
CHECK_MESSAGE(