summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/class_db.cpp18
-rw-r--r--core/object.h1
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/Material.xml2
-rw-r--r--doc/classes/SubViewportContainer.xml1
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--editor/code_editor.cpp9
-rw-r--r--editor/create_dialog.cpp57
-rw-r--r--editor/editor_audio_buses.cpp13
-rw-r--r--editor/editor_audio_buses.h1
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp131
-rw-r--r--editor/import/editor_scene_importer_gltf.h19
-rw-r--r--editor/import/resource_importer_texture.cpp7
-rw-r--r--editor/import/resource_importer_texture.h1
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp1
-rw-r--r--editor/project_manager.cpp4
-rw-r--r--modules/regex/doc_classes/RegEx.xml3
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml2
-rw-r--r--platform/javascript/javascript_main.cpp12
-rw-r--r--platform/uwp/export/export.cpp18
-rw-r--r--scene/2d/path_2d.cpp6
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/3d/path_3d.cpp6
-rw-r--r--scene/3d/path_3d.h2
-rw-r--r--scene/gui/rich_text_label.cpp3
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.cpp5
26 files changed, 245 insertions, 83 deletions
diff --git a/core/class_db.cpp b/core/class_db.cpp
index eed9ec17cb..05c9850c39 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -1386,7 +1386,23 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
if (r_valid != nullptr) {
*r_valid = true;
}
- return default_values[p_class][p_property];
+
+ Variant var = default_values[p_class][p_property];
+
+#ifdef DEBUG_ENABLED
+ // Some properties may have an instantiated Object as default value,
+ // (like Path2D's `curve` used to have), but that's not a good practice.
+ // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT
+ // to be auto-instantiated when created in the editor.
+ if (var.get_type() == Variant::OBJECT) {
+ Object *obj = var.get_validated_object();
+ if (obj) {
+ WARN_PRINT(vformat("Instantiated %s used as default value for %s's \"%s\" property.", obj->get_class(), p_class, p_property));
+ }
+ }
+#endif
+
+ return var;
}
RWLock *ClassDB::lock = nullptr;
diff --git a/core/object.h b/core/object.h
index 95662f6208..5b46a0f93a 100644
--- a/core/object.h
+++ b/core/object.h
@@ -124,6 +124,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player
PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading
+ PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 263fb6c022..3318f2757d 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -57,7 +57,7 @@
</member>
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" override="true" enum="Control.MouseFilter" default="2" />
<member name="percent_visible" type="float" setter="set_percent_visible" getter="get_percent_visible" default="1.0">
- Limits the count of visible characters. If you set [code]percent_visible[/code] to 50, only up to half of the text's characters will display on screen. Useful to animate the text in a dialog box.
+ Limits the amount of visible characters. If you set [code]percent_visible[/code] to 0.5, only up to half of the text's characters will display on screen. Useful to animate the text in a dialog box.
</member>
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" override="true" default="4" />
<member name="text" type="String" setter="set_text" getter="get_text" default="&quot;&quot;">
diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml
index a37c8127f0..f3686876ca 100644
--- a/doc/classes/Material.xml
+++ b/doc/classes/Material.xml
@@ -17,7 +17,7 @@
</member>
<member name="render_priority" type="int" setter="set_render_priority" getter="get_render_priority" default="0">
Sets the render priority for transparent objects in 3D scenes. Higher priority objects will be sorted in front of lower priority objects.
- [b]Note:[/b] this only applies to sorting of transparent objects. This will not impact how transparent objects are sorted relative to opaque objects. This is because opaque objects are sorted based on depth, while transparent objects are sorted from back to front (subject to priority).
+ [b]Note:[/b] this only applies to sorting of transparent objects. This will not impact how transparent objects are sorted relative to opaque objects. This is because opaque objects are not sorted, while transparent objects are sorted from back to front (subject to priority).
</member>
</members>
<constants>
diff --git a/doc/classes/SubViewportContainer.xml b/doc/classes/SubViewportContainer.xml
index e6a0bd4866..16d483e7f8 100644
--- a/doc/classes/SubViewportContainer.xml
+++ b/doc/classes/SubViewportContainer.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
A [Container] node that holds a [SubViewport], automatically setting its size.
+ [b]Note:[/b] Changing a SubViewportContainer's [member Control.rect_scale] will cause its contents to appear distorted. To change its visual size without causing distortion, adjust the node's margins instead (if it's not already in a container).
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index c647f83598..9a78e45d46 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -44,6 +44,8 @@
<argument index="1" name="neighbor_id" type="int">
</argument>
<description>
+ Determines when the auto-tiler should consider two different auto-tile IDs to be bound together.
+ [b]Note:[/b] [code]neighbor_id[/code] will be [code]-1[/code] ([constant TileMap.INVALID_CELL]) when checking a tile against an empty neighbor tile.
</description>
</method>
<method name="autotile_clear_bitmask_map">
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 9888013ce6..95c7cc0bf1 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -307,18 +307,19 @@ void FindReplaceBar::_update_results_count() {
break;
}
+ int pos_subsequent = pos + searched.length();
if (is_whole_words()) {
- from_pos++; // Making sure we won't hit the same match next time, if we get out via a continue.
- if (pos > 0 && !is_symbol(full_text[pos - 1])) {
+ from_pos = pos + 1; // Making sure we won't hit the same match next time, if we get out via a continue.
+ if (pos > 0 && !(is_symbol(full_text[pos - 1]) || full_text[pos - 1] == '\n')) {
continue;
}
- if (pos + searched.length() < full_text.length() && !is_symbol(full_text[pos + searched.length()])) {
+ if (pos_subsequent < full_text.length() && !(is_symbol(full_text[pos_subsequent]) || full_text[pos_subsequent] == '\n')) {
continue;
}
}
results_count++;
- from_pos = pos + searched.length();
+ from_pos = pos_subsequent;
}
}
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index e4a853dbd7..73468f8ce0 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -347,20 +347,22 @@ void CreateDialog::_update_search() {
} else {
bool found = false;
String type2 = type;
+ bool cpp_type2 = cpp_type;
if (!cpp_type && !search_loaded_scripts.has(type)) {
search_loaded_scripts[type] = ed.script_class_load_script(type);
}
- while (type2 != "" && (cpp_type ? ClassDB::is_parent_class(type2, base_type) : ed.script_class_is_parent(type2, base_type)) && type2 != base_type) {
+ while (type2 != "" && (cpp_type2 ? ClassDB::is_parent_class(type2, base_type) : ed.script_class_is_parent(type2, base_type)) && type2 != base_type) {
if (search_box->get_text().is_subsequence_ofi(type2)) {
found = true;
break;
}
- type2 = cpp_type ? ClassDB::get_parent_class(type2) : ed.script_class_get_base(type2);
+ type2 = cpp_type2 ? ClassDB::get_parent_class(type2) : ed.script_class_get_base(type2);
+ cpp_type2 = cpp_type2 || ClassDB::class_exists(type2); // Built-in class can't inherit from custom type, so we can skip the check if it's already true.
- if (!cpp_type && !search_loaded_scripts.has(type2)) {
+ if (!cpp_type2 && !search_loaded_scripts.has(type2)) {
search_loaded_scripts[type2] = ed.script_class_load_script(type2);
}
}
@@ -511,30 +513,45 @@ String CreateDialog::get_selected_type() {
Object *CreateDialog::instance_selected() {
TreeItem *selected = search_options->get_selected();
- if (selected) {
- Variant md = selected->get_metadata(0);
+ if (!selected) {
+ return nullptr;
+ }
- String custom;
- if (md.get_type() != Variant::NIL) {
- custom = md;
- }
+ Variant md = selected->get_metadata(0);
+ String custom;
+ if (md.get_type() != Variant::NIL) {
+ custom = md;
+ }
- if (custom != String()) {
- if (ScriptServer::is_global_class(custom)) {
- Object *obj = EditorNode::get_editor_data().script_class_instance(custom);
- Node *n = Object::cast_to<Node>(obj);
- if (n) {
- n->set_name(custom);
- }
- return obj;
+ Object *obj = nullptr;
+
+ if (!custom.empty()) {
+ if (ScriptServer::is_global_class(custom)) {
+ obj = EditorNode::get_editor_data().script_class_instance(custom);
+ Node *n = Object::cast_to<Node>(obj);
+ if (n) {
+ n->set_name(custom);
}
- return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
+ obj = n;
} else {
- return ClassDB::instance(selected->get_text(0));
+ obj = EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
+ }
+ } else {
+ obj = ClassDB::instance(selected->get_text(0));
+ }
+
+ // Check if any Object-type property should be instantiated.
+ List<PropertyInfo> pinfo;
+ obj->get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().type == Variant::OBJECT && E->get().usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) {
+ Object *prop = ClassDB::instance(E->get().class_name);
+ obj->set(E->get().name, prop);
}
}
- return nullptr;
+ return obj;
}
void CreateDialog::_item_selected() {
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index f8dec13a5c..5cf5201b18 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -545,6 +545,17 @@ void EditorAudioBus::_gui_input(const Ref<InputEvent> &p_event) {
}
}
+void EditorAudioBus::_unhandled_key_input(Ref<InputEvent> p_event) {
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_DELETE) {
+ TreeItem *current_effect = effects->get_selected();
+ if (current_effect && current_effect->get_metadata(0).get_type() == Variant::INT) {
+ _delete_effect_pressed(0);
+ accept_event();
+ }
+ }
+}
+
void EditorAudioBus::_bus_popup_pressed(int p_option) {
if (p_option == 2) {
// Reset volume
@@ -738,6 +749,7 @@ void EditorAudioBus::_bind_methods() {
ClassDB::bind_method("update_bus", &EditorAudioBus::update_bus);
ClassDB::bind_method("update_send", &EditorAudioBus::update_send);
ClassDB::bind_method("_gui_input", &EditorAudioBus::_gui_input);
+ ClassDB::bind_method("_unhandled_key_input", &EditorAudioBus::_unhandled_key_input);
ClassDB::bind_method("get_drag_data_fw", &EditorAudioBus::get_drag_data_fw);
ClassDB::bind_method("can_drop_data_fw", &EditorAudioBus::can_drop_data_fw);
ClassDB::bind_method("drop_data_fw", &EditorAudioBus::drop_data_fw);
@@ -761,6 +773,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
add_child(vb);
set_v_size_flags(SIZE_EXPAND_FILL);
+ set_process_unhandled_key_input(true);
track_name = memnew(LineEdit);
track_name->connect("text_entered", callable_mp(this, &EditorAudioBus::_name_changed));
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 6b2d9e4436..65caf84f0f 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -92,6 +92,7 @@ class EditorAudioBus : public PanelContainer {
mutable bool hovering_drop;
void _gui_input(const Ref<InputEvent> &p_event);
+ void _unhandled_key_input(Ref<InputEvent> p_event);
void _bus_popup_pressed(int p_option);
void _name_changed(const String &p_new_name);
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index e340f41e3b..1a232658fe 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -286,7 +286,16 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) {
node->xform.basis.set_quat_scale(node->rotation, node->scale);
node->xform.origin = node->translation;
}
-
+ if (n.has("extensions")) {
+ Dictionary extensions = n["extensions"];
+ if (extensions.has("KHR_lights_punctual")) {
+ Dictionary lights_punctual = extensions["KHR_lights_punctual"];
+ if (lights_punctual.has("light")) {
+ GLTFLightIndex light = lights_punctual["light"];
+ node->light = light;
+ }
+ }
+ }
if (n.has("children")) {
const Array &children = n["children"];
for (int j = 0; j < children.size(); j++) {
@@ -2245,6 +2254,58 @@ void EditorSceneImporterGLTF::_remove_duplicate_skins(GLTFState &state) {
}
}
+Error EditorSceneImporterGLTF::_parse_lights(GLTFState &state) {
+ if (!state.json.has("extensions")) {
+ return OK;
+ }
+ Dictionary extensions = state.json["extensions"];
+ if (!extensions.has("KHR_lights_punctual")) {
+ return OK;
+ }
+ Dictionary lights_punctual = extensions["KHR_lights_punctual"];
+ if (!lights_punctual.has("lights")) {
+ return OK;
+ }
+
+ const Array &lights = lights_punctual["lights"];
+
+ for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) {
+ const Dictionary &d = lights[light_i];
+
+ GLTFLight light;
+ ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
+ const String &type = d["type"];
+ light.type = type;
+
+ if (d.has("color")) {
+ const Array &arr = d["color"];
+ ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
+ const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
+ light.color = c;
+ }
+ if (d.has("intensity")) {
+ light.intensity = d["intensity"];
+ }
+ if (d.has("range")) {
+ light.range = d["range"];
+ }
+ if (type == "spot") {
+ const Dictionary &spot = d["spot"];
+ light.inner_cone_angle = spot["innerConeAngle"];
+ light.outer_cone_angle = spot["outerConeAngle"];
+ ERR_FAIL_COND_V_MSG(light.inner_cone_angle >= light.outer_cone_angle, ERR_PARSE_ERROR, "The inner angle must be smaller than the outer angle.");
+ } else if (type != "point" && type != "directional") {
+ ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Light type is unknown.");
+ }
+
+ state.lights.push_back(light);
+ }
+
+ print_verbose("glTF: Total lights: " + itos(state.lights.size()));
+
+ return OK;
+}
+
Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) {
if (!state.json.has("cameras")) {
return OK;
@@ -2488,6 +2549,58 @@ MeshInstance3D *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &stat
return mi;
}
+Light3D *EditorSceneImporterGLTF::_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
+ const GLTFNode *gltf_node = state.nodes[node_index];
+
+ ERR_FAIL_INDEX_V(gltf_node->light, state.lights.size(), nullptr);
+
+ print_verbose("glTF: Creating light for: " + gltf_node->name);
+
+ const GLTFLight &l = state.lights[gltf_node->light];
+
+ float intensity = l.intensity;
+ if (intensity > 10) {
+ // GLTF spec has the default around 1, but Blender defaults lights to 100.
+ // The only sane way to handle this is to check where it came from and
+ // handle it accordingly. If it's over 10, it probably came from Blender.
+ intensity /= 100;
+ }
+
+ if (l.type == "directional") {
+ DirectionalLight3D *light = memnew(DirectionalLight3D);
+ light->set_param(Light3D::PARAM_ENERGY, intensity);
+ light->set_color(l.color);
+ return light;
+ }
+
+ const float range = CLAMP(l.range, 0, 4096);
+ // Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
+ // We want to have double intensity give double brightness, so we need half the attenuation.
+ const float attenuation = range / intensity;
+ if (l.type == "point") {
+ OmniLight3D *light = memnew(OmniLight3D);
+ light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(OmniLight3D::PARAM_RANGE, range);
+ light->set_color(l.color);
+ return light;
+ }
+ if (l.type == "spot") {
+ SpotLight3D *light = memnew(SpotLight3D);
+ light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(SpotLight3D::PARAM_RANGE, range);
+ light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l.outer_cone_angle));
+ light->set_color(l.color);
+
+ // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
+ // The points in desmos are not exact, except for (1, infinity).
+ float angle_ratio = l.inner_cone_angle / l.outer_cone_angle;
+ float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
+ light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
+ return light;
+ }
+ return nullptr;
+}
+
Camera3D *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
const GLTFNode *gltf_node = state.nodes[node_index];
@@ -2561,6 +2674,8 @@ void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene
current_node = _generate_mesh_instance(state, scene_parent, node_index);
} else if (gltf_node->camera >= 0) {
current_node = _generate_camera(state, scene_parent, node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(state, scene_parent, node_index);
} else {
current_node = _generate_spatial(state, scene_parent, node_index);
}
@@ -3037,22 +3152,28 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_fla
return nullptr;
}
- /* STEP 14 PARSE CAMERAS */
+ /* STEP 14 PARSE LIGHTS */
+ err = _parse_lights(state);
+ if (err != OK) {
+ return NULL;
+ }
+
+ /* STEP 15 PARSE CAMERAS */
err = _parse_cameras(state);
if (err != OK) {
return nullptr;
}
- /* STEP 15 PARSE ANIMATIONS */
+ /* STEP 16 PARSE ANIMATIONS */
err = _parse_animations(state);
if (err != OK) {
return nullptr;
}
- /* STEP 16 ASSIGN SCENE NAMES */
+ /* STEP 17 ASSIGN SCENE NAMES */
_assign_scene_names(state);
- /* STEP 17 MAKE SCENE! */
+ /* STEP 18 MAKE SCENE! */
Node3D *scene = _generate_scene(state, p_bake_fps);
return scene;
diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h
index d45410fa57..95c6b87af5 100644
--- a/editor/import/editor_scene_importer_gltf.h
+++ b/editor/import/editor_scene_importer_gltf.h
@@ -32,6 +32,7 @@
#define EDITOR_SCENE_IMPORTER_GLTF_H
#include "editor/import/resource_importer_scene.h"
+#include "scene/3d/light_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
@@ -50,6 +51,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
typedef int GLTFImageIndex;
typedef int GLTFMaterialIndex;
typedef int GLTFMeshIndex;
+ typedef int GLTFLightIndex;
typedef int GLTFNodeIndex;
typedef int GLTFSkeletonIndex;
typedef int GLTFSkinIndex;
@@ -113,6 +115,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
GLTFNodeIndex fake_joint_parent = -1;
+ GLTFLightIndex light = -1;
+
GLTFNode() {}
};
@@ -218,6 +222,17 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
GLTFCamera() {}
};
+ struct GLTFLight {
+ Color color = Color(1.0f, 1.0f, 1.0f);
+ float intensity = 1.0f;
+ String type = "";
+ float range = Math_INF;
+ float inner_cone_angle = 0.0f;
+ float outer_cone_angle = Math_PI / 4.0;
+
+ GLTFLight() {}
+ };
+
struct GLTFAnimation {
bool loop = false;
@@ -271,6 +286,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
Vector<GLTFSkin> skins;
Vector<GLTFCamera> cameras;
+ Vector<GLTFLight> lights;
Set<String> unique_names;
@@ -349,12 +365,13 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
void _remove_duplicate_skins(GLTFState &state);
Error _parse_cameras(GLTFState &state);
-
+ Error _parse_lights(GLTFState &state);
Error _parse_animations(GLTFState &state);
BoneAttachment3D *_generate_bone_attachment(GLTFState &state, Skeleton3D *skeleton, const GLTFNodeIndex node_index);
MeshInstance3D *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
Camera3D *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
+ Light3D *_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
Node3D *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
void _generate_scene_node(GLTFState &state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index a13324f0fc..3a0e624a8f 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -181,15 +181,14 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_option, cons
}
int ResourceImporterTexture::get_preset_count() const {
- return 4;
+ return 3;
}
String ResourceImporterTexture::get_preset_name(int p_idx) const {
static const char *preset_names[] = {
- "2D, Detect 3D",
+ "2D/3D (Auto-Detect)",
"2D",
- "2D Pixel",
- "3D"
+ "3D",
};
return preset_names[p_idx];
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index b770d240eb..12eb7f67c2 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -93,7 +93,6 @@ public:
enum Preset {
PRESET_DETECT,
PRESET_2D,
- PRESET_2D_PIXEL,
PRESET_3D,
};
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 321b4432ab..2586f17fe1 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -665,7 +665,6 @@ void Skeleton3DEditor::create_editors() {
options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option));
- options->hide();
const Color section_color = get_theme_color("prop_subsection", "Editor");
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 499f7d4017..cbba4b4834 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1741,10 +1741,6 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
select_project(clicked_index);
}
- if (_selected_project_keys.has(clicked_project.project_key)) {
- clicked_project.control->grab_focus();
- }
-
emit_signal(SIGNAL_SELECTION_CHANGED);
if (!mb->get_control() && mb->is_doubleclick()) {
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index 3130c53331..c00fa96b2e 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -32,8 +32,7 @@
[codeblock]
for result in regex.search_all("d01, d03, d0c, x3f and x42"):
print(result.get_string("digit"))
- # Would print 01 03 3f 42
- # Note that d0c would not match
+ # Would print 01 03 0 3f 42
[/codeblock]
[b]Note:[/b] Godot's regex implementation is based on the [url=https://www.pcre.org/]PCRE2[/url] library. You can view the full pattern reference [url=https://www.pcre.org/current/doc/html/pcre2pattern.html]here[/url].
[b]Tip:[/b] You can use [url=https://regexr.com/]Regexr[/url] to test regular expressions online.
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 151e881b6f..a45de60aef 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -49,7 +49,7 @@
</methods>
<members>
<member name="names" type="Dictionary" setter="" getter="get_names" default="{}">
- A dictionary of named groups and its corresponding group number. Only groups with that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
+ A dictionary of named groups and its corresponding group number. Only groups that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
</member>
<member name="strings" type="Array" setter="" getter="get_strings" default="[ ]">
An [Array] of the match and its capturing groups.
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 740a72fafa..fd61c46e63 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -80,6 +80,9 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
Main::start();
os->get_main_loop()->init();
emscripten_resume_main_loop();
+ // Immediately run the first iteration.
+ // We are inside an animation frame, we want to immediately draw on the newly setup canvas.
+ main_loop_callback();
}
int main(int argc, char *argv[]) {
@@ -91,14 +94,15 @@ int main(int argc, char *argv[]) {
// Sync from persistent state into memory and then
// run the 'main_after_fs_sync' function.
/* clang-format off */
- EM_ASM(
+ EM_ASM({
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');
FS.syncfs(true, function(err) {
- ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
+ requestAnimationFrame(function() {
+ ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
+ });
});
-
- );
+ });
/* clang-format on */
return 0;
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index db42908f89..cb4716bd65 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -743,23 +743,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
// TODO: Add resource creation or image rescaling to enable other scales:
// 1.25, 1.5, 2.0
- real_t scales[] = { 1.0 };
- bool valid_w = false;
- bool valid_h = false;
-
- for (int i = 0; i < 1; i++) {
- int w = ceil(p_width * scales[i]);
- int h = ceil(p_height * scales[i]);
-
- if (w == p_image->get_width()) {
- valid_w = true;
- }
- if (h == p_image->get_height()) {
- valid_h = true;
- }
- }
-
- return valid_w && valid_h;
+ return p_width == p_image->get_width() && p_height == p_image->get_height();
}
Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const {
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 00e9af3bb7..f2f549e851 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -149,11 +149,7 @@ void Path2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path2D::set_curve);
ClassDB::bind_method(D_METHOD("get_curve"), &Path2D::get_curve);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D"), "set_curve", "get_curve");
-}
-
-Path2D::Path2D() {
- set_curve(Ref<Curve2D>(memnew(Curve2D))); //create one by default
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_curve", "get_curve");
}
/////////////////////////////////////////////////////////////////////////////////
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 288ef698e7..38fcca0323 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -55,7 +55,7 @@ public:
void set_curve(const Ref<Curve2D> &p_curve);
Ref<Curve2D> get_curve() const;
- Path2D();
+ Path2D() {}
};
class PathFollow2D : public Node2D {
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index dcfdf8efcf..40d988ff9f 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -77,15 +77,11 @@ void Path3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path3D::set_curve);
ClassDB::bind_method(D_METHOD("get_curve"), &Path3D::get_curve);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_curve", "get_curve");
ADD_SIGNAL(MethodInfo("curve_changed"));
}
-Path3D::Path3D() {
- set_curve(Ref<Curve3D>(memnew(Curve3D))); //create one by default
-}
-
//////////////
void PathFollow3D::_update_transform() {
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 5a33016bc6..7f227a8a6f 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -49,7 +49,7 @@ public:
void set_curve(const Ref<Curve3D> &p_curve);
Ref<Curve3D> get_curve() const;
- Path3D();
+ Path3D() {}
};
class PathFollow3D : public Node3D {
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 2f5af0eda0..662eae394f 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1970,6 +1970,9 @@ void RichTextLabel::clear() {
selection.click = nullptr;
selection.active = false;
current_idx = 1;
+ if (scroll_follow) {
+ scroll_following = true;
+ }
if (fixed_width != -1) {
minimum_size_changed();
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
index cfd02cef2c..ae549ed2e4 100644
--- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
@@ -75,10 +75,7 @@ void BroadPhase2DHashGrid::_check_motion(Element *p_elem) {
if (pairing != E->get()->colliding) {
if (pairing) {
if (pair_callback) {
- void *ud = pair_callback(p_elem->owner, p_elem->subindex, E->key()->owner, E->key()->subindex, pair_userdata);
- if (ud) {
- E->get()->ud = ud;
- }
+ E->get()->ud = pair_callback(p_elem->owner, p_elem->subindex, E->key()->owner, E->key()->subindex, pair_userdata);
}
} else {
if (unpair_callback) {