summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/extension/gdextension_interface.cpp75
-rw-r--r--core/extension/gdextension_interface.h20
-rw-r--r--core/io/file_access.cpp12
-rw-r--r--core/io/file_access.h4
-rw-r--r--core/io/xml_parser.cpp10
-rw-r--r--core/io/xml_parser.h4
-rw-r--r--doc/classes/ArrayMesh.xml14
-rw-r--r--doc/classes/EditorPlugin.xml15
-rw-r--r--doc/classes/EditorResourceConversionPlugin.xml23
-rw-r--r--doc/classes/ImporterMesh.xml10
-rw-r--r--doc/classes/Mesh.xml7
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--editor/doc_tools.cpp62
-rw-r--r--editor/editor_file_system.cpp36
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_plugin.cpp11
-rw-r--r--editor/editor_plugin.h4
-rw-r--r--editor/import/collada.cpp164
-rw-r--r--editor/import/resource_importer_obj.cpp14
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp19
-rw-r--r--modules/text_server_adv/text_server_adv.cpp36
-rw-r--r--modules/text_server_adv/text_server_adv.h3
-rw-r--r--modules/text_server_adv/thorvg_svg_in_ot.cpp33
-rw-r--r--modules/text_server_fb/text_server_fb.cpp30
-rw-r--r--modules/text_server_fb/text_server_fb.h4
-rw-r--r--modules/text_server_fb/thorvg_svg_in_ot.cpp33
-rw-r--r--platform/android/os_android.cpp16
-rw-r--r--scene/resources/mesh.cpp5
-rw-r--r--scene/resources/resource_format_text.cpp3
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--tests/core/io/test_xml_parser.h2
-rw-r--r--tests/scene/test_arraymesh.h21
32 files changed, 407 insertions, 288 deletions
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index bd4fa61bd5..4df3df160f 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -31,8 +31,11 @@
#include "gdextension_interface.h"
#include "core/config/engine.h"
+#include "core/io/file_access.h"
+#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
#include "core/object/script_language_extension.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/memory.h"
#include "core/variant/variant.h"
#include "core/version.h"
@@ -678,6 +681,59 @@ static const char32_t *gdextension_string_operator_index_const(GDExtensionConstS
return &self->ptr()[p_index];
}
+static void gdextension_string_operator_plus_eq_string(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b) {
+ String *self = (String *)p_self;
+ const String *b = (const String *)p_b;
+ *self += *b;
+}
+
+static void gdextension_string_operator_plus_eq_char(GDExtensionStringPtr p_self, char32_t p_b) {
+ String *self = (String *)p_self;
+ *self += p_b;
+}
+
+static void gdextension_string_operator_plus_eq_cstr(GDExtensionStringPtr p_self, const char *p_b) {
+ String *self = (String *)p_self;
+ *self += p_b;
+}
+
+static void gdextension_string_operator_plus_eq_wcstr(GDExtensionStringPtr p_self, const wchar_t *p_b) {
+ String *self = (String *)p_self;
+ *self += p_b;
+}
+
+static void gdextension_string_operator_plus_eq_c32str(GDExtensionStringPtr p_self, const char32_t *p_b) {
+ String *self = (String *)p_self;
+ *self += p_b;
+}
+
+static GDExtensionInt gdextension_xml_parser_open_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size) {
+ XMLParser *xml = (XMLParser *)p_instance;
+ return (GDExtensionInt)xml->_open_buffer(p_buffer, p_size);
+}
+
+static void gdextension_file_access_store_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length) {
+ FileAccess *fa = (FileAccess *)p_instance;
+ fa->store_buffer(p_src, p_length);
+}
+
+static uint64_t gdextension_file_access_get_buffer(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length) {
+ const FileAccess *fa = (FileAccess *)p_instance;
+ return fa->get_buffer(p_dst, p_length);
+}
+
+static int64_t gdextension_worker_thread_pool_add_native_group_task(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, GDExtensionConstStringPtr p_description) {
+ WorkerThreadPool *p = (WorkerThreadPool *)p_instance;
+ const String *description = (const String *)p_description;
+ return (int64_t)p->add_native_group_task(p_func, p_userdata, p_elements, p_tasks, p_high_priority, *description);
+}
+
+static int64_t gdextension_worker_thread_pool_add_native_task(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, bool p_high_priority, GDExtensionConstStringPtr p_description) {
+ WorkerThreadPool *p = (WorkerThreadPool *)p_instance;
+ const String *description = (const String *)p_description;
+ return (int64_t)p->add_native_task(p_func, p_userdata, p_high_priority, *description);
+}
+
/* Packed array functions */
static uint8_t *gdextension_packed_byte_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
@@ -1025,6 +1081,25 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) {
gde_interface.string_to_wide_chars = gdextension_string_to_wide_chars;
gde_interface.string_operator_index = gdextension_string_operator_index;
gde_interface.string_operator_index_const = gdextension_string_operator_index_const;
+ gde_interface.string_operator_plus_eq_string = gdextension_string_operator_plus_eq_string;
+ gde_interface.string_operator_plus_eq_char = gdextension_string_operator_plus_eq_char;
+ gde_interface.string_operator_plus_eq_cstr = gdextension_string_operator_plus_eq_cstr;
+ gde_interface.string_operator_plus_eq_wcstr = gdextension_string_operator_plus_eq_wcstr;
+ gde_interface.string_operator_plus_eq_c32str = gdextension_string_operator_plus_eq_c32str;
+
+ /* XMLParser extra utilities */
+
+ gde_interface.xml_parser_open_buffer = gdextension_xml_parser_open_buffer;
+
+ /* FileAccess extra utilities */
+
+ gde_interface.file_access_store_buffer = gdextension_file_access_store_buffer;
+ gde_interface.file_access_get_buffer = gdextension_file_access_get_buffer;
+
+ /* WorkerThreadPool extra utilities */
+
+ gde_interface.worker_thread_pool_add_native_group_task = gdextension_worker_thread_pool_add_native_group_task;
+ gde_interface.worker_thread_pool_add_native_task = gdextension_worker_thread_pool_add_native_task;
/* Packed array functions */
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 190298ee8a..a16eef2fcf 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -503,6 +503,26 @@ typedef struct {
char32_t *(*string_operator_index)(GDExtensionStringPtr p_self, GDExtensionInt p_index);
const char32_t *(*string_operator_index_const)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index);
+ void (*string_operator_plus_eq_string)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b);
+ void (*string_operator_plus_eq_char)(GDExtensionStringPtr p_self, char32_t p_b);
+ void (*string_operator_plus_eq_cstr)(GDExtensionStringPtr p_self, const char *p_b);
+ void (*string_operator_plus_eq_wcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b);
+ void (*string_operator_plus_eq_c32str)(GDExtensionStringPtr p_self, const char32_t *p_b);
+
+ /* XMLParser extra utilities */
+
+ GDExtensionInt (*xml_parser_open_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size);
+
+ /* FileAccess extra utilities */
+
+ void (*file_access_store_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length);
+ uint64_t (*file_access_get_buffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length);
+
+ /* WorkerThreadPool extra utilities */
+
+ int64_t (*worker_thread_pool_add_native_group_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, GDExtensionConstStringPtr p_description);
+ int64_t (*worker_thread_pool_add_native_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, bool p_high_priority, GDExtensionConstStringPtr p_description);
+
/* Packed array functions */
uint8_t *(*packed_byte_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 77d1bdcc5c..cacbcb28a4 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -292,7 +292,7 @@ real_t FileAccess::get_real() const {
Variant FileAccess::get_var(bool p_allow_objects) const {
uint32_t len = get_32();
- Vector<uint8_t> buff = _get_buffer(len);
+ Vector<uint8_t> buff = get_buffer(len);
ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant());
const uint8_t *r = buff.ptr();
@@ -469,7 +469,7 @@ uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
return i;
}
-Vector<uint8_t> FileAccess::_get_buffer(int64_t p_length) const {
+Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
Vector<uint8_t> data;
ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0.");
@@ -663,7 +663,7 @@ void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
}
}
-void FileAccess::_store_buffer(const Vector<uint8_t> &p_buffer) {
+void FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
uint64_t len = p_buffer.size();
if (len == 0) {
return;
@@ -687,7 +687,7 @@ void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
store_32(len);
- _store_buffer(buff);
+ store_buffer(buff);
}
Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {
@@ -829,7 +829,7 @@ void FileAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
- ClassDB::bind_method(D_METHOD("get_buffer", "length"), &FileAccess::_get_buffer);
+ ClassDB::bind_method(D_METHOD("get_buffer", "length"), (Vector<uint8_t>(FileAccess::*)(int64_t) const) & FileAccess::get_buffer);
ClassDB::bind_method(D_METHOD("get_line"), &FileAccess::get_line);
ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &FileAccess::get_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text, DEFVAL(false));
@@ -847,7 +847,7 @@ void FileAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
- ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &FileAccess::_store_buffer);
+ ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (void(FileAccess::*)(const Vector<uint8_t> &)) & FileAccess::store_buffer);
ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line);
ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string);
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 3116ed521f..3e51ba11ed 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -127,7 +127,7 @@ public:
Variant get_var(bool p_allow_objects = false) const;
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
- Vector<uint8_t> _get_buffer(int64_t p_length) const;
+ Vector<uint8_t> get_buffer(int64_t p_length) const;
virtual String get_line() const;
virtual String get_token() const;
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
@@ -162,7 +162,7 @@ public:
virtual String get_pascal_string();
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
- void _store_buffer(const Vector<uint8_t> &p_buffer);
+ void store_buffer(const Vector<uint8_t> &p_buffer);
void store_var(const Variant &p_var, bool p_full_objects = false);
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index 09836419be..5c0a017bfc 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -354,10 +354,10 @@ void XMLParser::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_node_offset"), &XMLParser::get_node_offset);
ClassDB::bind_method(D_METHOD("get_attribute_count"), &XMLParser::get_attribute_count);
ClassDB::bind_method(D_METHOD("get_attribute_name", "idx"), &XMLParser::get_attribute_name);
- ClassDB::bind_method(D_METHOD("get_attribute_value", "idx"), (String(XMLParser::*)(int) const) & XMLParser::get_attribute_value);
+ ClassDB::bind_method(D_METHOD("get_attribute_value", "idx"), &XMLParser::get_attribute_value);
ClassDB::bind_method(D_METHOD("has_attribute", "name"), &XMLParser::has_attribute);
- ClassDB::bind_method(D_METHOD("get_named_attribute_value", "name"), (String(XMLParser::*)(const String &) const) & XMLParser::get_attribute_value);
- ClassDB::bind_method(D_METHOD("get_named_attribute_value_safe", "name"), &XMLParser::get_attribute_value_safe);
+ ClassDB::bind_method(D_METHOD("get_named_attribute_value", "name"), &XMLParser::get_named_attribute_value);
+ ClassDB::bind_method(D_METHOD("get_named_attribute_value_safe", "name"), &XMLParser::get_named_attribute_value_safe);
ClassDB::bind_method(D_METHOD("is_empty"), &XMLParser::is_empty);
ClassDB::bind_method(D_METHOD("get_current_line"), &XMLParser::get_current_line);
ClassDB::bind_method(D_METHOD("skip_section"), &XMLParser::skip_section);
@@ -422,7 +422,7 @@ bool XMLParser::has_attribute(const String &p_name) const {
return false;
}
-String XMLParser::get_attribute_value(const String &p_name) const {
+String XMLParser::get_named_attribute_value(const String &p_name) const {
int idx = -1;
for (int i = 0; i < attributes.size(); i++) {
if (attributes[i].name == p_name) {
@@ -436,7 +436,7 @@ String XMLParser::get_attribute_value(const String &p_name) const {
return attributes[idx].value;
}
-String XMLParser::get_attribute_value_safe(const String &p_name) const {
+String XMLParser::get_named_attribute_value_safe(const String &p_name) const {
int idx = -1;
for (int i = 0; i < attributes.size(); i++) {
if (attributes[i].name == p_name) {
diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h
index b4ae5c93b6..b96478c7a5 100644
--- a/core/io/xml_parser.h
+++ b/core/io/xml_parser.h
@@ -109,8 +109,8 @@ public:
String get_attribute_name(int p_idx) const;
String get_attribute_value(int p_idx) const;
bool has_attribute(const String &p_name) const;
- String get_attribute_value(const String &p_name) const;
- String get_attribute_value_safe(const String &p_name) const; // do not print error if doesn't exist
+ String get_named_attribute_value(const String &p_name) const;
+ String get_named_attribute_value_safe(const String &p_name) const; // do not print error if doesn't exist
bool is_empty() const;
int get_current_line() const;
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index f7764d5e32..7b86afcc4c 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -65,11 +65,15 @@
<param index="1" name="arrays" type="Array" />
<param index="2" name="blend_shapes" type="Array[]" default="[]" />
<param index="3" name="lods" type="Dictionary" default="{}" />
- <param index="4" name="compress_flags" type="int" enum="Mesh.ArrayFormat" default="0" />
- <description>
- Creates a new surface.
- Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
- The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ <param index="4" name="flags" type="int" enum="Mesh.ArrayFormat" default="0" />
+ <description>
+ Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
+ Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
+ The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
+ [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
</method>
<method name="clear_blend_shapes">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 2124a97751..370be8e9f3 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -467,6 +467,14 @@
See [method add_inspector_plugin] for an example of how to register a plugin.
</description>
</method>
+ <method name="add_resource_conversion_plugin">
+ <return type="void" />
+ <param index="0" name="plugin" type="EditorResourceConversionPlugin" />
+ <description>
+ Registers a new [EditorResourceConversionPlugin]. Resource conversion plugins are used to add custom resource converters to the editor inspector.
+ See [EditorResourceConversionPlugin] for an example of how to create a resource conversion plugin.
+ </description>
+ </method>
<method name="add_scene_format_importer_plugin">
<return type="void" />
<param index="0" name="scene_format_importer" type="EditorSceneFormatImporter" />
@@ -632,6 +640,13 @@
Removes a gizmo plugin registered by [method add_node_3d_gizmo_plugin].
</description>
</method>
+ <method name="remove_resource_conversion_plugin">
+ <return type="void" />
+ <param index="0" name="plugin" type="EditorResourceConversionPlugin" />
+ <description>
+ Removes a resource conversion plugin registered by [method add_resource_conversion_plugin].
+ </description>
+ </method>
<method name="remove_scene_format_importer_plugin">
<return type="void" />
<param index="0" name="scene_format_importer" type="EditorSceneFormatImporter" />
diff --git a/doc/classes/EditorResourceConversionPlugin.xml b/doc/classes/EditorResourceConversionPlugin.xml
index c40bb1d91e..6bdfbbe40c 100644
--- a/doc/classes/EditorResourceConversionPlugin.xml
+++ b/doc/classes/EditorResourceConversionPlugin.xml
@@ -1,8 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorResourceConversionPlugin" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Plugin for adding custom converters from one resource format to another in the editor resource picker context menu; for example, converting a [StandardMaterial3D] to a [ShaderMaterial].
</brief_description>
<description>
+ [EditorResourceConversionPlugin] is invoked when the context menu is brought up for a resource in the editor inspector. Relevant conversion plugins will appear as menu options to convert the given resource to a target type.
+ Below shows an example of a basic plugin that will convert an [ImageTexture] to a [PortableCompressedTexture2D].
+ [codeblocks]
+ [gdscript]
+ extends EditorResourceConversionPlugin
+
+ func _handles(resource : Resource):
+ return resource is ImageTexture
+
+ func _converts_to():
+ return "PortableCompressedTexture2D"
+
+ func _convert(itex : Resource):
+ var ptex = PortableCompressedTexture2D.new()
+ ptex.create_from_image(itex.get_image(), PortableCompressedTexture2D.COMPRESSION_MODE_LOSSLESS)
+ return ptex
+ [/gdscript]
+ [/codeblocks]
+ To use an [EditorResourceConversionPlugin], register it using the [method EditorPlugin.add_resource_conversion_plugin] method first.
</description>
<tutorials>
</tutorials>
@@ -11,17 +31,20 @@
<return type="Resource" />
<param index="0" name="resource" type="Resource" />
<description>
+ Takes an input [Resource] and converts it to the type given in [method _converts_to]. The returned [Resource] is the result of the conversion, and the input [Resource] remains unchanged.
</description>
</method>
<method name="_converts_to" qualifiers="virtual const">
<return type="String" />
<description>
+ Returns the class name of the target type of [Resource] that this plugin converts source resources to.
</description>
</method>
<method name="_handles" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="resource" type="Resource" />
<description>
+ Called to determine whether a particular [Resource] can be converted to the target resource type by this plugin.
</description>
</method>
</methods>
diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index b80857a7bf..10479dfcfe 100644
--- a/doc/classes/ImporterMesh.xml
+++ b/doc/classes/ImporterMesh.xml
@@ -27,9 +27,13 @@
<param index="5" name="name" type="String" default="&quot;&quot;" />
<param index="6" name="flags" type="int" default="0" />
<description>
- Creates a new surface, analogous to [method ArrayMesh.add_surface_from_arrays].
- Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
- The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
+ Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
+ The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
+ [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
</method>
<method name="clear">
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 94e80ffb2b..1c1f48588f 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -227,10 +227,10 @@
Contains custom color channel 3. [PackedByteArray] if [code](format &gt;&gt; [constant ARRAY_FORMAT_CUSTOM3_SHIFT]) &amp; [constant ARRAY_FORMAT_CUSTOM_MASK])[/code] is [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RG_HALF] or [constant ARRAY_CUSTOM_RGBA_HALF]. [PackedFloat32Array] otherwise.
</constant>
<constant name="ARRAY_BONES" value="10" enum="ArrayType">
- [PackedFloat32Array] or [PackedInt32Array] of bone indices. Each element is a group of 4 numbers.
+ [PackedFloat32Array] or [PackedInt32Array] of bone indices. Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag.
</constant>
<constant name="ARRAY_WEIGHTS" value="11" enum="ArrayType">
- [PackedFloat32Array] of bone weights. Each element in groups of 4 floats.
+ [PackedFloat32Array] or [PackedFloat64Array] of bone weights in the range [code]0.0[/code] to [code]1.0[/code] (inclusive). Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag.
</constant>
<constant name="ARRAY_INDEX" value="12" enum="ArrayType">
[PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices.
@@ -341,6 +341,9 @@
<constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the mesh contains up to 8 bone influences per vertex. This flag indicates that [constant ARRAY_BONES] and [constant ARRAY_WEIGHTS] elements will have double length.
</constant>
+ <constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark that the mesh intentionally contains no vertex array.
+ </constant>
<constant name="BLEND_SHAPE_MODE_NORMALIZED" value="0" enum="BlendShapeMode">
Blend shapes are normalized.
</constant>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 33170e6606..3d7fb0d445 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3810,6 +3810,8 @@
</constant>
<constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
</constant>
+ <constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
+ </constant>
<constant name="PRIMITIVE_POINTS" value="0" enum="PrimitiveType">
Primitive to draw consists of points.
</constant>
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 85adc312f2..c675060b2b 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -1021,15 +1021,15 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
if (parser->get_node_name() == element) {
DocData::MethodDoc method;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- method.name = parser->get_attribute_value("name");
+ method.name = parser->get_named_attribute_value("name");
if (parser->has_attribute("qualifiers")) {
- method.qualifiers = parser->get_attribute_value("qualifiers");
+ method.qualifiers = parser->get_named_attribute_value("qualifiers");
}
if (parser->has_attribute("is_deprecated")) {
- method.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ method.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
}
if (parser->has_attribute("is_experimental")) {
- method.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ method.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
}
while (parser->read() == OK) {
@@ -1037,21 +1037,21 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
String name = parser->get_node_name();
if (name == "return") {
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
- method.return_type = parser->get_attribute_value("type");
+ method.return_type = parser->get_named_attribute_value("type");
if (parser->has_attribute("enum")) {
- method.return_enum = parser->get_attribute_value("enum");
+ method.return_enum = parser->get_named_attribute_value("enum");
}
} else if (name == "returns_error") {
ERR_FAIL_COND_V(!parser->has_attribute("number"), ERR_FILE_CORRUPT);
- method.errors_returned.push_back(parser->get_attribute_value("number").to_int());
+ method.errors_returned.push_back(parser->get_named_attribute_value("number").to_int());
} else if (name == "param") {
DocData::ArgumentDoc argument;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- argument.name = parser->get_attribute_value("name");
+ argument.name = parser->get_named_attribute_value("name");
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
- argument.type = parser->get_attribute_value("type");
+ argument.type = parser->get_named_attribute_value("type");
if (parser->has_attribute("enum")) {
- argument.enumeration = parser->get_attribute_value("enum");
+ argument.enumeration = parser->get_named_attribute_value("enum");
}
method.arguments.push_back(argument);
@@ -1153,21 +1153,21 @@ Error DocTools::_load(Ref<XMLParser> parser) {
ERR_FAIL_COND_V(parser->get_node_name() != "class", ERR_FILE_CORRUPT);
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- String name = parser->get_attribute_value("name");
+ String name = parser->get_named_attribute_value("name");
class_list[name] = DocData::ClassDoc();
DocData::ClassDoc &c = class_list[name];
c.name = name;
if (parser->has_attribute("inherits")) {
- c.inherits = parser->get_attribute_value("inherits");
+ c.inherits = parser->get_named_attribute_value("inherits");
}
if (parser->has_attribute("is_deprecated")) {
- c.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ c.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
}
if (parser->has_attribute("is_experimental")) {
- c.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ c.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
}
while (parser->read() == OK) {
@@ -1193,7 +1193,7 @@ Error DocTools::_load(Ref<XMLParser> parser) {
if (name3 == "link") {
DocData::TutorialDoc tutorial;
if (parser->has_attribute("title")) {
- tutorial.title = parser->get_attribute_value("title");
+ tutorial.title = parser->get_named_attribute_value("title");
}
parser->read();
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
@@ -1231,23 +1231,23 @@ Error DocTools::_load(Ref<XMLParser> parser) {
DocData::PropertyDoc prop2;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- prop2.name = parser->get_attribute_value("name");
+ prop2.name = parser->get_named_attribute_value("name");
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
- prop2.type = parser->get_attribute_value("type");
+ prop2.type = parser->get_named_attribute_value("type");
if (parser->has_attribute("setter")) {
- prop2.setter = parser->get_attribute_value("setter");
+ prop2.setter = parser->get_named_attribute_value("setter");
}
if (parser->has_attribute("getter")) {
- prop2.getter = parser->get_attribute_value("getter");
+ prop2.getter = parser->get_named_attribute_value("getter");
}
if (parser->has_attribute("enum")) {
- prop2.enumeration = parser->get_attribute_value("enum");
+ prop2.enumeration = parser->get_named_attribute_value("enum");
}
if (parser->has_attribute("is_deprecated")) {
- prop2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ prop2.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
}
if (parser->has_attribute("is_experimental")) {
- prop2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ prop2.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
}
if (!parser->is_empty()) {
parser->read();
@@ -1274,11 +1274,11 @@ Error DocTools::_load(Ref<XMLParser> parser) {
DocData::ThemeItemDoc prop2;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- prop2.name = parser->get_attribute_value("name");
+ prop2.name = parser->get_named_attribute_value("name");
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
- prop2.type = parser->get_attribute_value("type");
+ prop2.type = parser->get_named_attribute_value("type");
ERR_FAIL_COND_V(!parser->has_attribute("data_type"), ERR_FILE_CORRUPT);
- prop2.data_type = parser->get_attribute_value("data_type");
+ prop2.data_type = parser->get_named_attribute_value("data_type");
if (!parser->is_empty()) {
parser->read();
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
@@ -1303,21 +1303,21 @@ Error DocTools::_load(Ref<XMLParser> parser) {
if (name3 == "constant") {
DocData::ConstantDoc constant2;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
- constant2.name = parser->get_attribute_value("name");
+ constant2.name = parser->get_named_attribute_value("name");
ERR_FAIL_COND_V(!parser->has_attribute("value"), ERR_FILE_CORRUPT);
- constant2.value = parser->get_attribute_value("value");
+ constant2.value = parser->get_named_attribute_value("value");
constant2.is_value_valid = true;
if (parser->has_attribute("enum")) {
- constant2.enumeration = parser->get_attribute_value("enum");
+ constant2.enumeration = parser->get_named_attribute_value("enum");
}
if (parser->has_attribute("is_bitfield")) {
- constant2.is_bitfield = parser->get_attribute_value("is_bitfield").to_lower() == "true";
+ constant2.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
}
if (parser->has_attribute("is_deprecated")) {
- constant2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true";
+ constant2.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
}
if (parser->has_attribute("is_experimental")) {
- constant2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true";
+ constant2.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
}
if (!parser->is_empty()) {
parser->read();
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 63008ec65c..a9b4c08698 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -721,6 +721,7 @@ void EditorFileSystem::scan() {
new_filesystem = nullptr;
_update_scan_actions();
scanning = false;
+ _update_pending_script_classes();
emit_signal(SNAME("filesystem_changed"));
emit_signal(SNAME("sources_changed"), sources_changed.size() > 0);
first_scan = false;
@@ -923,6 +924,10 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc
fi->modified_time = mt;
fi->import_modified_time = 0;
fi->import_valid = true;
+
+ if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) {
+ _queue_update_script_class(path);
+ }
}
}
@@ -1174,7 +1179,9 @@ void EditorFileSystem::scan_changes() {
sp.low = 0;
scan_total = 0;
_scan_fs_changes(filesystem, sp);
- if (_update_scan_actions()) {
+ bool changed = _update_scan_actions();
+ _update_pending_script_classes();
+ if (changed) {
emit_signal(SNAME("filesystem_changed"));
}
}
@@ -1223,7 +1230,9 @@ void EditorFileSystem::_notification(int p_what) {
set_process(false);
thread_sources.wait_to_finish();
- if (_update_scan_actions()) {
+ bool changed = _update_scan_actions();
+ _update_pending_script_classes();
+ if (changed) {
emit_signal(SNAME("filesystem_changed"));
}
emit_signal(SNAME("sources_changed"), sources_changed.size() > 0);
@@ -1239,6 +1248,7 @@ void EditorFileSystem::_notification(int p_what) {
new_filesystem = nullptr;
thread.wait_to_finish();
_update_scan_actions();
+ _update_pending_script_classes();
emit_signal(SNAME("filesystem_changed"));
emit_signal(SNAME("sources_changed"), sources_changed.size() > 0);
first_scan = false;
@@ -1508,11 +1518,20 @@ void EditorFileSystem::_update_script_classes() {
lang = ScriptServer::get_language(j)->get_name();
}
}
+ if (lang.is_empty()) {
+ continue; // No lang found that can handle this global class
+ }
ScriptServer::add_global_class(efd->files[index]->script_class_name, efd->files[index]->script_class_extends, lang, path);
EditorNode::get_editor_data().script_class_set_icon_path(efd->files[index]->script_class_name, efd->files[index]->script_class_icon_path);
EditorNode::get_editor_data().script_class_set_name(efd->files[index]->file, efd->files[index]->script_class_name);
}
+ }
+
+ // Parse documentation second, as it requires the class names to be correct and registered
+ for (const String &path : update_script_paths) {
+ int index = -1;
+ EditorFileSystemDirectory *efd = find_file(path, &index);
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
@@ -1545,14 +1564,16 @@ void EditorFileSystem::_update_script_classes() {
ResourceSaver::add_custom_savers();
}
+void EditorFileSystem::_update_pending_script_classes() {
+ if (!update_script_paths.is_empty()) {
+ _update_script_classes();
+ }
+}
+
void EditorFileSystem::_queue_update_script_class(const String &p_path) {
update_script_mutex.lock();
- bool call_update = update_script_paths.is_empty();
update_script_paths.insert(p_path);
update_script_mutex.unlock();
- if (call_update) {
- call_deferred(SNAME("_update_script_classes"));
- }
}
void EditorFileSystem::update_file(const String &p_file) {
@@ -1582,6 +1603,7 @@ void EditorFileSystem::update_file(const String &p_file) {
fs->files.remove_at(cpos);
}
+ _update_pending_script_classes();
call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
return;
}
@@ -1646,6 +1668,7 @@ void EditorFileSystem::update_file(const String &p_file) {
_queue_update_script_class(p_file);
}
+ _update_pending_script_classes();
call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
}
@@ -2428,7 +2451,6 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_file", "path"), &EditorFileSystem::update_file);
ClassDB::bind_method(D_METHOD("get_filesystem_path", "path"), &EditorFileSystem::get_filesystem_path);
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
- ClassDB::bind_method(D_METHOD("_update_script_classes"), &EditorFileSystem::_update_script_classes);
ClassDB::bind_method(D_METHOD("reimport_files", "files"), &EditorFileSystem::reimport_files);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 1a0c8e2f78..03b1fb4a49 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -261,6 +261,7 @@ class EditorFileSystem : public Node {
HashSet<String> update_script_paths;
void _queue_update_script_class(const String &p_path);
void _update_script_classes();
+ void _update_pending_script_classes();
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 07db3241ff..7f02148dfc 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -45,6 +45,7 @@
#include "editor/inspector_dock.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/project_settings_editor.h"
@@ -853,6 +854,14 @@ void EditorPlugin::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plu
EditorDebuggerNode::get_singleton()->remove_debugger_plugin(p_plugin);
}
+void EditorPlugin::add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
+ EditorNode::get_singleton()->add_resource_conversion_plugin(p_plugin);
+}
+
+void EditorPlugin::remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
+ EditorNode::get_singleton()->remove_resource_conversion_plugin(p_plugin);
+}
+
void EditorPlugin::_editor_project_settings_changed() {
emit_signal(SNAME("project_settings_changed"));
}
@@ -908,6 +917,8 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::remove_node_3d_gizmo_plugin);
ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin);
ClassDB::bind_method(D_METHOD("remove_inspector_plugin", "plugin"), &EditorPlugin::remove_inspector_plugin);
+ ClassDB::bind_method(D_METHOD("add_resource_conversion_plugin", "plugin"), &EditorPlugin::add_resource_conversion_plugin);
+ ClassDB::bind_method(D_METHOD("remove_resource_conversion_plugin", "plugin"), &EditorPlugin::remove_resource_conversion_plugin);
ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled);
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 03a8d9ba93..a5a17acdf1 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -48,6 +48,7 @@ class EditorInspector;
class EditorInspectorPlugin;
class EditorNode3DGizmoPlugin;
class EditorPaths;
+class EditorResourceConversionPlugin;
class EditorResourcePreview;
class EditorSceneFormatImporter;
class EditorScenePostImportPlugin;
@@ -304,6 +305,9 @@ public:
void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
+ void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
+ void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
+
void enable_plugin();
void disable_plugin();
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index fcd706627d..a8d746fde6 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -262,7 +262,7 @@ void Collada::_parse_asset(XMLParser &parser) {
COLLADA_PRINT("up axis: " + parser.get_node_data());
} else if (name == "unit") {
- state.unit_scale = parser.get_attribute_value("meter").to_float();
+ state.unit_scale = parser.get_named_attribute_value("meter").to_float();
COLLADA_PRINT("unit scale: " + rtos(state.unit_scale));
}
@@ -273,7 +273,7 @@ void Collada::_parse_asset(XMLParser &parser) {
}
void Collada::_parse_image(XMLParser &parser) {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
if (!parser.is_empty()) {
@@ -286,7 +286,7 @@ void Collada::_parse_image(XMLParser &parser) {
if (state.version < State::Version(1, 4, 0)) {
/* <1.4 */
- String path = parser.get_attribute_value("source").strip_edges();
+ String path = parser.get_named_attribute_value("source").strip_edges();
if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().path_join(path.uri_decode()));
@@ -337,9 +337,9 @@ void Collada::_parse_material(XMLParser &parser) {
Material material;
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
if (parser.has_attribute("name")) {
- material.name = parser.get_attribute_value("name");
+ material.name = parser.get_named_attribute_value("name");
}
if (state.version < State::Version(1, 4, 0)) {
@@ -348,7 +348,7 @@ void Collada::_parse_material(XMLParser &parser) {
} else {
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT && parser.get_node_name() == "instance_effect") {
- material.instance_effect = _uri_to_id(parser.get_attribute_value("url"));
+ material.instance_effect = _uri_to_id(parser.get_named_attribute_value("url"));
} else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "material") {
break; //end of <asset>
}
@@ -549,7 +549,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
_parse_effect_material(parser, effect, id); // try again
} else if (parser.get_node_name() == "newparam") {
- String name = parser.get_attribute_value("sid");
+ String name = parser.get_named_attribute_value("sid");
Variant value = _parse_param(parser);
effect.params[name] = value;
COLLADA_PRINT("param: " + name + " value:" + String(value));
@@ -591,7 +591,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
}
} else if (parser.get_node_name() == "texture") {
- String sampler = parser.get_attribute_value("texture");
+ String sampler = parser.get_named_attribute_value("texture");
if (!effect.params.has(sampler)) {
ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data());
} else {
@@ -609,7 +609,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
} else if (what == "emission") {
effect.emission.texture = uri;
} else if (what == "bump") {
- if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") {
+ if (parser.has_attribute("bumptype") && parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
@@ -654,7 +654,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "texture") {
- String sampler = parser.get_attribute_value("texture");
+ String sampler = parser.get_named_attribute_value("texture");
if (!effect.params.has(sampler)) {
ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data());
} else {
@@ -665,7 +665,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
} else {
String uri = effect.params[surface];
- if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") {
+ if (parser.has_attribute("bumptype") && parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
@@ -703,11 +703,11 @@ void Collada::_parse_effect(XMLParser &parser) {
return;
}
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
Effect effect;
if (parser.has_attribute("name")) {
- effect.name = parser.get_attribute_value("name");
+ effect.name = parser.get_named_attribute_value("name");
}
_parse_effect_material(parser, effect, id);
@@ -724,7 +724,7 @@ void Collada::_parse_camera(XMLParser &parser) {
return;
}
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
state.camera_data_map[id] = CameraData();
CameraData &camera = state.camera_data_map[id];
@@ -780,7 +780,7 @@ void Collada::_parse_light(XMLParser &parser) {
return;
}
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
state.light_data_map[id] = LightData();
LightData &light = state.light_data_map[id];
@@ -848,7 +848,7 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
CurveData &curvedata = state.curve_data_map[p_id];
curvedata.name = p_name;
- String closed = parser.get_attribute_value_safe("closed").to_lower();
+ String closed = parser.get_named_attribute_value_safe("closed").to_lower();
curvedata.closed = closed == "true" || closed == "1";
COLLADA_PRINT("curve name: " + p_name);
@@ -865,7 +865,7 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
String section = parser.get_node_name();
if (section == "source") {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
curvedata.sources[id] = CurveData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -888,15 +888,15 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
} else if (section == "accessor") { // child of source (below a technique tag)
if (curvedata.sources.has(current_source)) {
- curvedata.sources[current_source].stride = parser.get_attribute_value("stride").to_int();
+ curvedata.sources[current_source].stride = parser.get_named_attribute_value("stride").to_int();
COLLADA_PRINT("section: " + current_source + " stride " + itos(curvedata.sources[current_source].stride));
}
} else if (section == "control_vertices") {
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
curvedata.control_vertices[semantic] = source;
@@ -945,7 +945,7 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
String section = parser.get_node_name();
if (section == "source") {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
meshdata.sources[id] = MeshData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -961,19 +961,19 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
} else if (section == "accessor") { // child of source (below a technique tag)
if (meshdata.sources.has(current_source)) {
- meshdata.sources[current_source].stride = parser.get_attribute_value("stride").to_int();
+ meshdata.sources[current_source].stride = parser.get_named_attribute_value("stride").to_int();
COLLADA_PRINT("section: " + current_source + " stride " + itos(meshdata.sources[current_source].stride));
}
} else if (section == "vertices") {
MeshData::Vertices vert;
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
int last_ref = 0;
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
if (semantic == "TEXCOORD") {
semantic = "TEXCOORD" + itos(last_ref++);
@@ -998,22 +998,22 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
MeshData::Primitives prim;
if (parser.has_attribute("material")) {
- prim.material = parser.get_attribute_value("material");
+ prim.material = parser.get_named_attribute_value("material");
}
- prim.count = parser.get_attribute_value("count").to_int();
+ prim.count = parser.get_named_attribute_value("count").to_int();
prim.vertex_size = 0;
int last_ref = 0;
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
if (semantic == "TEXCOORD") {
semantic = "TEXCOORD" + itos(last_ref++);
}
- int offset = parser.get_attribute_value("offset").to_int();
+ int offset = parser.get_named_attribute_value("offset").to_int();
MeshData::Primitives::SourceRef sref;
sref.source = source;
@@ -1074,7 +1074,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
state.skin_controller_data_map[p_id] = SkinControllerData();
SkinControllerData &skindata = state.skin_controller_data_map[p_id];
- skindata.base = _uri_to_id(parser.get_attribute_value("source"));
+ skindata.base = _uri_to_id(parser.get_named_attribute_value("source"));
String current_source;
@@ -1091,7 +1091,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
COLLADA_PRINT("skeleton bind shape transform: " + skindata.bind_shape);
} else if (section == "source") {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
skindata.sources[id] = SkinControllerData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -1125,7 +1125,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
if (skindata.sources.has(current_source)) {
int stride = 1;
if (parser.has_attribute("stride")) {
- stride = parser.get_attribute_value("stride").to_int();
+ stride = parser.get_named_attribute_value("stride").to_int();
}
skindata.sources[current_source].stride = stride;
@@ -1138,8 +1138,8 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
joint.sources[semantic] = source;
@@ -1155,15 +1155,15 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
} else if (section == "vertex_weights") {
SkinControllerData::Weights weights;
- weights.count = parser.get_attribute_value("count").to_int();
+ weights.count = parser.get_named_attribute_value("count").to_int();
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
- int offset = parser.get_attribute_value("offset").to_int();
+ int offset = parser.get_named_attribute_value("offset").to_int();
SkinControllerData::Weights::SourceRef sref;
sref.source = source;
@@ -1228,8 +1228,8 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
state.morph_controller_data_map[p_id] = MorphControllerData();
MorphControllerData &morphdata = state.morph_controller_data_map[p_id];
- morphdata.mesh = _uri_to_id(parser.get_attribute_value("source"));
- morphdata.mode = parser.get_attribute_value("method");
+ morphdata.mesh = _uri_to_id(parser.get_named_attribute_value("source"));
+ morphdata.mode = parser.get_named_attribute_value("method");
String current_source;
while (parser.read() == OK) {
@@ -1237,7 +1237,7 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
String section = parser.get_node_name();
if (section == "source") {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
morphdata.sources[id] = MorphControllerData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -1261,7 +1261,7 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
if (morphdata.sources.has(current_source)) {
int stride = 1;
if (parser.has_attribute("stride")) {
- stride = parser.get_attribute_value("stride").to_int();
+ stride = parser.get_named_attribute_value("stride").to_int();
}
morphdata.sources[current_source].stride = stride;
@@ -1272,8 +1272,8 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "input") {
- String semantic = parser.get_attribute_value("semantic");
- String source = _uri_to_id(parser.get_attribute_value("source"));
+ String semantic = parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(parser.get_named_attribute_value("source"));
morphdata.targets[semantic] = source;
@@ -1295,7 +1295,7 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
}
void Collada::_parse_controller(XMLParser &parser) {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
if (parser.is_empty()) {
return;
@@ -1320,7 +1320,7 @@ Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
String type = parser.get_node_name();
NodeGeometry *geom = memnew(NodeGeometry);
geom->controller = type == "instance_controller";
- geom->source = _uri_to_id(parser.get_attribute_value_safe("url"));
+ geom->source = _uri_to_id(parser.get_named_attribute_value_safe("url"));
if (parser.is_empty()) { //nothing else to parse...
return geom;
@@ -1329,8 +1329,8 @@ Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "instance_material") {
- String symbol = parser.get_attribute_value("symbol");
- String target = _uri_to_id(parser.get_attribute_value("target"));
+ String symbol = parser.get_named_attribute_value("symbol");
+ String target = _uri_to_id(parser.get_named_attribute_value("target"));
NodeGeometry::Material mat;
mat.target = target;
@@ -1370,7 +1370,7 @@ Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &parser) {
NodeCamera *cam = memnew(NodeCamera);
- cam->camera = _uri_to_id(parser.get_attribute_value_safe("url"));
+ cam->camera = _uri_to_id(parser.get_named_attribute_value_safe("url"));
if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness
cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5);
@@ -1391,7 +1391,7 @@ Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &parser) {
Collada::Node *Collada::_parse_visual_instance_light(XMLParser &parser) {
NodeLight *cam = memnew(NodeLight);
- cam->light = _uri_to_id(parser.get_attribute_value_safe("url"));
+ cam->light = _uri_to_id(parser.get_named_attribute_value_safe("url"));
if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness
cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5);
@@ -1437,7 +1437,7 @@ Collada::Node *Collada::_parse_visual_node_instance_data(XMLParser &parser) {
Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
String name;
- String id = parser.get_attribute_value_safe("id");
+ String id = parser.get_named_attribute_value_safe("id");
bool found_name = false;
@@ -1455,25 +1455,25 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
Node *node = nullptr;
- name = parser.has_attribute("name") ? parser.get_attribute_value_safe("name") : parser.get_attribute_value_safe("id");
+ name = parser.has_attribute("name") ? parser.get_named_attribute_value_safe("name") : parser.get_named_attribute_value_safe("id");
if (name.is_empty()) {
name = id;
} else {
found_name = true;
}
- if ((parser.has_attribute("type") && parser.get_attribute_value("type") == "JOINT") || state.idref_joints.has(name)) {
+ if ((parser.has_attribute("type") && parser.get_named_attribute_value("type") == "JOINT") || state.idref_joints.has(name)) {
// handle a bone
NodeJoint *joint = memnew(NodeJoint);
if (parser.has_attribute("sid")) { //bones may not have sid
- joint->sid = parser.get_attribute_value("sid");
+ joint->sid = parser.get_named_attribute_value("sid");
//state.bone_map[joint->sid]=joint;
} else if (state.idref_joints.has(name)) {
joint->sid = name; //kind of a cheat but..
} else if (parser.has_attribute("name")) {
- joint->sid = parser.get_attribute_value_safe("name");
+ joint->sid = parser.get_named_attribute_value_safe("name");
}
if (!joint->sid.is_empty()) {
@@ -1490,7 +1490,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
if (section == "translate") {
Node::XForm xf;
if (parser.has_attribute("sid")) {
- xf.id = parser.get_attribute_value("sid");
+ xf.id = parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_TRANSLATE;
@@ -1501,7 +1501,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "rotate") {
Node::XForm xf;
if (parser.has_attribute("sid")) {
- xf.id = parser.get_attribute_value("sid");
+ xf.id = parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_ROTATE;
@@ -1513,7 +1513,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "scale") {
Node::XForm xf;
if (parser.has_attribute("sid")) {
- xf.id = parser.get_attribute_value("sid");
+ xf.id = parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_SCALE;
@@ -1527,7 +1527,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "matrix") {
Node::XForm xf;
if (parser.has_attribute("sid")) {
- xf.id = parser.get_attribute_value("sid");
+ xf.id = parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_MATRIX;
@@ -1544,7 +1544,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "visibility") {
Node::XForm xf;
if (parser.has_attribute("sid")) {
- xf.id = parser.get_attribute_value("sid");
+ xf.id = parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_VISIBILITY;
@@ -1609,7 +1609,7 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
}
void Collada::_parse_visual_scene(XMLParser &parser) {
- String id = parser.get_attribute_value("id");
+ String id = parser.get_named_attribute_value("id");
if (parser.is_empty()) {
return;
@@ -1619,7 +1619,7 @@ void Collada::_parse_visual_scene(XMLParser &parser) {
VisualScene &vscene = state.visual_scene_map[id];
if (parser.has_attribute("name")) {
- vscene.name = parser.get_attribute_value("name");
+ vscene.name = parser.get_named_attribute_value("name");
}
while (parser.read() == OK) {
@@ -1656,7 +1656,7 @@ void Collada::_parse_animation(XMLParser &parser) {
String id = "";
if (parser.has_attribute("id")) {
- id = parser.get_attribute_value("id");
+ id = parser.get_named_attribute_value("id");
}
String current_source;
@@ -1668,7 +1668,7 @@ void Collada::_parse_animation(XMLParser &parser) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
String name = parser.get_node_name();
if (name == "source") {
- current_source = parser.get_attribute_value("id");
+ current_source = parser.get_named_attribute_value("id");
source_param_names[current_source] = Vector<String>();
source_param_types[current_source] = Vector<String>();
@@ -1683,32 +1683,32 @@ void Collada::_parse_animation(XMLParser &parser) {
}
} else if (name == "accessor") {
if (!current_source.is_empty() && parser.has_attribute("stride")) {
- source_strides[current_source] = parser.get_attribute_value("stride").to_int();
+ source_strides[current_source] = parser.get_named_attribute_value("stride").to_int();
}
} else if (name == "sampler") {
- current_sampler = parser.get_attribute_value("id");
+ current_sampler = parser.get_named_attribute_value("id");
samplers[current_sampler] = HashMap<String, String>();
} else if (name == "param") {
if (parser.has_attribute("name")) {
- source_param_names[current_source].push_back(parser.get_attribute_value("name"));
+ source_param_names[current_source].push_back(parser.get_named_attribute_value("name"));
} else {
source_param_names[current_source].push_back("");
}
if (parser.has_attribute("type")) {
- source_param_types[current_source].push_back(parser.get_attribute_value("type"));
+ source_param_types[current_source].push_back(parser.get_named_attribute_value("type"));
} else {
source_param_types[current_source].push_back("");
}
} else if (name == "input") {
if (!current_sampler.is_empty()) {
- samplers[current_sampler][parser.get_attribute_value("semantic")] = parser.get_attribute_value("source");
+ samplers[current_sampler][parser.get_named_attribute_value("semantic")] = parser.get_named_attribute_value("source");
}
} else if (name == "channel") {
- channel_sources.push_back(parser.get_attribute_value("source"));
- channel_targets.push_back(parser.get_attribute_value("target"));
+ channel_sources.push_back(parser.get_named_attribute_value("source"));
+ channel_targets.push_back(parser.get_named_attribute_value("target"));
}
} else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "animation") {
@@ -1857,22 +1857,22 @@ void Collada::_parse_animation_clip(XMLParser &parser) {
AnimationClip clip;
if (parser.has_attribute("name")) {
- clip.name = parser.get_attribute_value("name");
+ clip.name = parser.get_named_attribute_value("name");
} else if (parser.has_attribute("id")) {
- clip.name = parser.get_attribute_value("id");
+ clip.name = parser.get_named_attribute_value("id");
}
if (parser.has_attribute("start")) {
- clip.begin = parser.get_attribute_value("start").to_float();
+ clip.begin = parser.get_named_attribute_value("start").to_float();
}
if (parser.has_attribute("end")) {
- clip.end = parser.get_attribute_value("end").to_float();
+ clip.end = parser.get_named_attribute_value("end").to_float();
}
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
String name = parser.get_node_name();
if (name == "instance_animation") {
- String url = _uri_to_id(parser.get_attribute_value("url"));
+ String url = _uri_to_id(parser.get_named_attribute_value("url"));
clip.tracks.push_back(url);
}
@@ -1894,9 +1894,9 @@ void Collada::_parse_scene(XMLParser &parser) {
String name = parser.get_node_name();
if (name == "instance_visual_scene") {
- state.root_visual_scene = _uri_to_id(parser.get_attribute_value("url"));
+ state.root_visual_scene = _uri_to_id(parser.get_named_attribute_value("url"));
} else if (name == "instance_physics_scene") {
- state.root_physics_scene = _uri_to_id(parser.get_attribute_value("url"));
+ state.root_physics_scene = _uri_to_id(parser.get_named_attribute_value("url"));
}
} else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "scene") {
@@ -1925,8 +1925,8 @@ void Collada::_parse_library(XMLParser &parser) {
} else if (name == "light") {
_parse_light(parser);
} else if (name == "geometry") {
- String id = parser.get_attribute_value("id");
- String name2 = parser.get_attribute_value_safe("name");
+ String id = parser.get_named_attribute_value("id");
+ String name2 = parser.get_named_attribute_value_safe("name");
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
if (parser.get_node_name() == "mesh") {
@@ -2346,7 +2346,7 @@ Error Collada::load(const String &p_path, int p_flags) {
{
//version
- String version = parser.get_attribute_value("version");
+ String version = parser.get_named_attribute_value("version");
state.version.major = version.get_slice(".", 0).to_int();
state.version.minor = version.get_slice(".", 1).to_int();
state.version.rev = version.get_slice(".", 2).to_int();
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index ad6d41e10c..3dd01754a3 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -206,6 +206,20 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
+ // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree)
+ // This should only match, if it's indeed a COFF file header
+ // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
+ const int first_bytes = f->get_16();
+ static const Vector<int> coff_header_machines{
+ 0x0, // IMAGE_FILE_MACHINE_UNKNOWN
+ 0x8664, // IMAGE_FILE_MACHINE_AMD64
+ 0x1c0, // IMAGE_FILE_MACHINE_ARM
+ 0x14c, // IMAGE_FILE_MACHINE_I386
+ 0x200, // IMAGE_FILE_MACHINE_IA64
+ };
+ ERR_FAIL_COND_V_MSG(coff_header_machines.find(first_bytes) != -1, ERR_FILE_CORRUPT, vformat("Couldn't read OBJ file '%s', it seems to be binary, corrupted, or empty.", p_path));
+ f->seek(0);
+
Ref<ArrayMesh> mesh;
mesh.instantiate();
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index cfe7c9c344..32421daa92 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -110,7 +110,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list
p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "suffix:px"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "suffix:px"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "suffix:px"));
p_list->push_back(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, ""));
}
@@ -164,7 +164,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
if (p_name == "atlas_coords") {
Vector2i as_vector2i = Vector2i(p_value);
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(as_vector2i, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
- ERR_FAIL_COND_V(!has_room_for_tile, false);
+ ERR_FAIL_COND_V_EDMSG(!has_room_for_tile, false, "Cannot move the tile, invalid coordinates or not enough room in the atlas for the tile and its animation frames.");
if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
tiles_set_atlas_source_editor->selection.clear();
@@ -223,7 +223,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
for (TileSelection tile : tiles) {
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_separation(tile.tile), tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
if (!has_room_for_tile) {
- ERR_PRINT("No room for tile");
+ ERR_PRINT(vformat("Cannot change the number of columns to %s for tile animation. Not enough room in the atlas to layout %s frame(s).", p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile)));
} else {
tile_set_atlas_source->set_tile_animation_columns(tile.tile, p_value);
}
@@ -234,7 +234,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
for (TileSelection tile : tiles) {
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
if (!has_room_for_tile) {
- ERR_PRINT("No room for tile");
+ ERR_PRINT(vformat("Cannot change separation between frames of the animation to %s. Not enough room in the atlas to layout %s frame(s).", p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile)));
} else {
tile_set_atlas_source->set_tile_animation_separation(tile.tile, p_value);
}
@@ -249,11 +249,16 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
return true;
} else if (p_name == "animation_frames_count") {
for (TileSelection tile : tiles) {
- bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), tile_set_atlas_source->get_tile_animation_separation(tile.tile), p_value, tile.tile);
+ int frame_count = p_value;
+ if (frame_count == 0) {
+ frame_count = 1;
+ }
+
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), tile_set_atlas_source->get_tile_animation_separation(tile.tile), frame_count, tile.tile);
if (!has_room_for_tile) {
- ERR_PRINT("No room for tile");
+ ERR_PRINT(vformat("Cannot add frames to the animation, not enough room in the atlas to layout %s frames.", frame_count));
} else {
- tile_set_atlas_source->set_tile_animation_frames_count(tile.tile, p_value);
+ tile_set_atlas_source->set_tile_animation_frames_count(tile.tile, frame_count);
}
}
notify_property_list_changed();
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index e52b87741e..8e9ff61ad0 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -423,11 +423,7 @@ bool TextServerAdvanced::_load_support_data(const String &p_filename) {
return false;
}
uint64_t len = f->get_length();
-#ifdef GDEXTENSION
PackedByteArray icu_data = f->get_buffer(len);
-#else
- PackedByteArray icu_data = f->_get_buffer(len);
-#endif
UErrorCode err = U_ZERO_ERROR;
udata_setCommonData(icu_data.ptr(), &err);
@@ -476,11 +472,7 @@ bool TextServerAdvanced::_save_support_data(const String &p_filename) const {
PackedByteArray icu_data;
icu_data.resize(U_ICUDATA_SIZE);
memcpy(icu_data.ptrw(), U_ICUDATA_ENTRY_POINT, U_ICUDATA_SIZE);
-#ifdef GDEXTENSION
f->store_buffer(icu_data);
-#else
- f->_store_buffer(icu_data);
-#endif
return true;
#else
@@ -824,29 +816,17 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
// Could not find texture to fit, create one.
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(texsize);
-#else
texsize = next_power_of_2(texsize);
-#endif
if (p_msdf) {
texsize = MIN(texsize, 2048);
} else {
texsize = MIN(texsize, 1024);
}
if (mw > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mw);
-#else
texsize = next_power_of_2(mw);
-#endif
}
if (mh > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mh);
-#else
texsize = next_power_of_2(mh);
-#endif
}
ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
@@ -949,14 +929,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con
return 0;
}
-void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+void TextServerAdvanced::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);
msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
- int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;
for (int col = 0; col < td->output->width(); ++col) {
- int x = (y % 2) ? td->output->width() - col - 1 : col;
- msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ int x = (p_y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));
msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
}
@@ -1026,14 +1006,8 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
-#ifdef GDEXTENSION
- for (int i = 0; i < h; i++) {
- _generateMTSDF_threaded(i, &td);
- }
-#else
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, SNAME("FontServerRasterizeMSDF"));
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, String("FontServerRasterizeMSDF"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
-#endif
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index e8a3a10ab8..5920ddaa50 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -72,7 +72,6 @@
#include <godot_cpp/templates/hash_map.hpp>
#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@@ -350,7 +349,7 @@ class TextServerAdvanced : public TextServerExtension {
_FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
_FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const;
_FORCE_INLINE_ void _font_clear_cache(FontAdvanced *p_font_data);
- void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+ static void _generateMTSDF_threaded(void *p_td, uint32_t p_y);
_FORCE_INLINE_ Vector2i _get_size(const FontAdvanced *p_font_data, int p_size) const {
if (p_font_data->msdf) {
diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp
index 9354d3f9b3..1406e3aaa0 100644
--- a/modules/text_server_adv/thorvg_svg_in_ot.cpp
+++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp
@@ -88,24 +88,13 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
if (!gl_state.ready) {
Ref<XMLParser> parser;
parser.instantiate();
-#ifdef GDEXTENSION
- PackedByteArray data;
- data.resize(document->svg_document_length);
- memcpy(data.ptrw(), document->svg_document, document->svg_document_length);
- parser->open_buffer(data);
-#else
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
-#endif
float aspect = 1.0f;
String xml_body;
while (parser->read() == OK) {
if (parser->has_attribute("id")) {
-#ifdef GDEXTENSION
const String &gl_name = parser->get_named_attribute_value("id");
-#else
- const String &gl_name = parser->get_attribute_value("id");
-#endif
if (gl_name.begins_with("glyph")) {
int dot_pos = gl_name.find(".");
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
@@ -117,11 +106,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
if (parser->has_attribute("viewBox")) {
-#ifdef GDEXTENSION
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
-#else
- Vector<String> vb = parser->get_attribute_value("viewBox").split(" ");
-#endif
if (vb.size() == 4) {
aspect = vb[2].to_float() / vb[3].to_float();
@@ -129,19 +114,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
}
continue;
}
-#ifdef GDEXTENSION
- if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
- xml_body = xml_body + "<" + parser->get_node_name();
- for (int i = 0; i < parser->get_attribute_count(); i++) {
- xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\"";
- }
- xml_body = xml_body + ">";
- } else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
- xml_body = xml_body + parser->get_node_data();
- } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
- xml_body = xml_body + "</" + parser->get_node_name() + ">";
- }
-#else
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
xml_body += vformat("<%s", parser->get_node_name());
for (int i = 0; i < parser->get_attribute_count(); i++) {
@@ -153,7 +125,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
xml_body += vformat("</%s>", parser->get_node_name());
}
-#endif
}
String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
@@ -175,11 +146,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
new_h = (new_w / aspect);
}
-#ifdef GDEXTENSION
gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
-#else
- gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body;
-#endif
picture = tvg::Picture::gen();
result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 034f88e387..ece34f56d6 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -248,11 +248,7 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
// Could not find texture to fit, create one.
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(texsize);
-#else
texsize = next_power_of_2(texsize);
-#endif
if (p_msdf) {
texsize = MIN(texsize, 2048);
@@ -260,18 +256,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
texsize = MIN(texsize, 1024);
}
if (mw > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mw);
-#else
texsize = next_power_of_2(mw);
-#endif
}
if (mh > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mh);
-#else
texsize = next_power_of_2(mh);
-#endif
}
ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
@@ -374,14 +362,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con
return 0;
}
-void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);
msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
- int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;
for (int col = 0; col < td->output->width(); ++col) {
- int x = (y % 2) ? td->output->width() - col - 1 : col;
- msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ int x = (p_y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));
msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
}
@@ -451,14 +439,8 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
-#ifdef GDEXTENSION
- for (int i = 0; i < h; i++) {
- _generateMTSDF_threaded(i, &td);
- }
-#else
- WorkerThreadPool::GroupID group_id = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, SNAME("TextServerFBRenderMSDF"));
- WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_id);
-#endif
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, String("TextServerFBRenderMSDF"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index b42f564534..9fb048a581 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -67,11 +67,11 @@
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/ref.hpp>
+#include <godot_cpp/classes/worker_thread_pool.hpp>
#include <godot_cpp/templates/hash_map.hpp>
#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@@ -303,7 +303,7 @@ class TextServerFallback : public TextServerExtension {
_FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
_FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const;
_FORCE_INLINE_ void _font_clear_cache(FontFallback *p_font_data);
- void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+ static void _generateMTSDF_threaded(void *p_td, uint32_t p_y);
_FORCE_INLINE_ Vector2i _get_size(const FontFallback *p_font_data, int p_size) const {
if (p_font_data->msdf) {
diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp
index 9354d3f9b3..1406e3aaa0 100644
--- a/modules/text_server_fb/thorvg_svg_in_ot.cpp
+++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp
@@ -88,24 +88,13 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
if (!gl_state.ready) {
Ref<XMLParser> parser;
parser.instantiate();
-#ifdef GDEXTENSION
- PackedByteArray data;
- data.resize(document->svg_document_length);
- memcpy(data.ptrw(), document->svg_document, document->svg_document_length);
- parser->open_buffer(data);
-#else
parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
-#endif
float aspect = 1.0f;
String xml_body;
while (parser->read() == OK) {
if (parser->has_attribute("id")) {
-#ifdef GDEXTENSION
const String &gl_name = parser->get_named_attribute_value("id");
-#else
- const String &gl_name = parser->get_attribute_value("id");
-#endif
if (gl_name.begins_with("glyph")) {
int dot_pos = gl_name.find(".");
int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
@@ -117,11 +106,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
}
if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
if (parser->has_attribute("viewBox")) {
-#ifdef GDEXTENSION
PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
-#else
- Vector<String> vb = parser->get_attribute_value("viewBox").split(" ");
-#endif
if (vb.size() == 4) {
aspect = vb[2].to_float() / vb[3].to_float();
@@ -129,19 +114,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
}
continue;
}
-#ifdef GDEXTENSION
- if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
- xml_body = xml_body + "<" + parser->get_node_name();
- for (int i = 0; i < parser->get_attribute_count(); i++) {
- xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\"";
- }
- xml_body = xml_body + ">";
- } else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
- xml_body = xml_body + parser->get_node_data();
- } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
- xml_body = xml_body + "</" + parser->get_node_name() + ">";
- }
-#else
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
xml_body += vformat("<%s", parser->get_node_name());
for (int i = 0; i < parser->get_attribute_count(); i++) {
@@ -153,7 +125,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
xml_body += vformat("</%s>", parser->get_node_name());
}
-#endif
}
String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
@@ -175,11 +146,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
new_h = (new_w / aspect);
}
-#ifdef GDEXTENSION
gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
-#else
- gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body;
-#endif
picture = tvg::Picture::gen();
result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 376ed89c16..942bf0a904 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -355,20 +355,20 @@ void OS_Android::_load_system_font_config() {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
in_font_node = false;
if (parser->get_node_name() == "familyset") {
- int ver = parser->has_attribute("version") ? parser->get_attribute_value("version").to_int() : 0;
+ int ver = parser->has_attribute("version") ? parser->get_named_attribute_value("version").to_int() : 0;
if (ver < 21) {
ERR_PRINT(vformat("Unsupported font config version %s", ver));
break;
}
} else if (parser->get_node_name() == "alias") {
- String name = parser->has_attribute("name") ? parser->get_attribute_value("name").strip_edges() : String();
- String to = parser->has_attribute("to") ? parser->get_attribute_value("to").strip_edges() : String();
+ String name = parser->has_attribute("name") ? parser->get_named_attribute_value("name").strip_edges() : String();
+ String to = parser->has_attribute("to") ? parser->get_named_attribute_value("to").strip_edges() : String();
if (!name.is_empty() && !to.is_empty()) {
font_aliases[name] = to;
}
} else if (parser->get_node_name() == "family") {
- fn = parser->has_attribute("name") ? parser->get_attribute_value("name").strip_edges() : String();
- String lang_code = parser->has_attribute("lang") ? parser->get_attribute_value("lang").strip_edges() : String();
+ fn = parser->has_attribute("name") ? parser->get_named_attribute_value("name").strip_edges() : String();
+ String lang_code = parser->has_attribute("lang") ? parser->get_named_attribute_value("lang").strip_edges() : String();
Vector<String> lang_codes = lang_code.split(",");
for (int i = 0; i < lang_codes.size(); i++) {
Vector<String> lang_code_elements = lang_codes[i].split("-");
@@ -412,9 +412,9 @@ void OS_Android::_load_system_font_config() {
}
} else if (parser->get_node_name() == "font") {
in_font_node = true;
- fb = parser->has_attribute("fallbackFor") ? parser->get_attribute_value("fallbackFor").strip_edges() : String();
- fi.weight = parser->has_attribute("weight") ? parser->get_attribute_value("weight").to_int() : 400;
- fi.italic = parser->has_attribute("style") && parser->get_attribute_value("style").strip_edges() == "italic";
+ fb = parser->has_attribute("fallbackFor") ? parser->get_named_attribute_value("fallbackFor").strip_edges() : String();
+ fi.weight = parser->has_attribute("weight") ? parser->get_named_attribute_value("weight").to_int() : 400;
+ fi.italic = parser->has_attribute("style") && parser->get_named_attribute_value("style").strip_edges() == "italic";
}
}
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 5e18b5df37..cedf4319f8 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -687,6 +687,7 @@ void Mesh::_bind_methods() {
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
@@ -1555,6 +1556,7 @@ void ArrayMesh::_recompute_aabb() {
// TODO: Need to add binding to add_surface using future MeshSurfaceData object.
void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
+ ERR_FAIL_COND(surfaces.size() == RS::MAX_MESH_SURFACES);
_create_if_empty();
Surface s;
@@ -1590,6 +1592,7 @@ void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_prim
}
void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, BitField<ArrayFormat> p_flags) {
+ ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX);
RS::SurfaceData surface;
@@ -2058,7 +2061,7 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode);
- ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces);
ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region);
ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 9542bc6179..2e8b4f93be 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1842,6 +1842,9 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
List<Variant> keys;
d.get_key_list(&keys);
for (const Variant &E : keys) {
+ // Of course keys should also be cached, after all we can't prevent users from using resources as keys, right?
+ // See also ResourceFormatSaverBinaryInstance::_find_resources (when p_variant is of type Variant::DICTIONARY)
+ _find_resources(E);
Variant v = d[E];
_find_resources(v);
}
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index e24b2af976..c3bd3d277f 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -996,6 +996,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
if (index_array_len) {
List<Variant> keys;
p_lods.get_key_list(&keys);
+ keys.sort(); // otherwise lod levels may get skipped
for (const Variant &E : keys) {
float distance = E;
ERR_CONTINUE(distance <= 0.0);
@@ -1826,6 +1827,7 @@ void RenderingServer::_bind_methods() {
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
BIND_ENUM_CONSTANT(PRIMITIVE_LINES);
diff --git a/tests/core/io/test_xml_parser.h b/tests/core/io/test_xml_parser.h
index f4e3f34be2..40cbea2dab 100644
--- a/tests/core/io/test_xml_parser.h
+++ b/tests/core/io/test_xml_parser.h
@@ -54,7 +54,7 @@ TEST_CASE("[XMLParser] End-to-end") {
CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT);
CHECK(parser.get_node_name() == "top");
CHECK(parser.has_attribute("attr"));
- CHECK(parser.get_attribute_value("attr") == "attr value");
+ CHECK(parser.get_named_attribute_value("attr") == "attr value");
CHECK(parser.read() == OK);
CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_TEXT);
diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h
index 4d9feeb4fa..b2a2ecc3bf 100644
--- a/tests/scene/test_arraymesh.h
+++ b/tests/scene/test_arraymesh.h
@@ -114,6 +114,17 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
CHECK(mesh->get_blend_shape_count() == 0);
}
+ SUBCASE("Can't add surface with incorrect number of blend shapes.") {
+ mesh->add_blend_shape(name_a);
+ mesh->add_blend_shape(name_b);
+ Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
+ Array cylinder_array{};
+ ERR_PRINT_OFF
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
+ ERR_PRINT_ON
+ CHECK(mesh->get_surface_count() == 0);
+ }
+
SUBCASE("Can't clear blend shapes after surface had been added.") {
mesh->add_blend_shape(name_a);
mesh->add_blend_shape(name_b);
@@ -121,7 +132,15 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
Array cylinder_array{};
cylinder_array.resize(Mesh::ARRAY_MAX);
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
+ Array blend_shape{};
+ blend_shape.resize(Mesh::ARRAY_MAX);
+ blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
+ blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
+ blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
+ Array blend_shapes{};
+ blend_shapes.push_back(blend_shape);
+ blend_shapes.push_back(blend_shape);
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
ERR_PRINT_OFF
mesh->clear_blend_shapes();