summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript.cpp34
-rw-r--r--modules/gdscript/gdscript.h6
-rw-r--r--modules/gdscript/gdscript_function.h1
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp18
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.h8
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h1
-rw-r--r--modules/gdscript/gdscript_utility_callable.cpp15
-rw-r--r--modules/gdscript/gdscript_utility_callable.h1
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp2
-rw-r--r--modules/gdscript/gdscript_utility_functions.h2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/argument_count.gd102
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/argument_count.out27
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml2
-rw-r--r--modules/gltf/gltf_document.cpp444
-rw-r--r--modules/gltf/gltf_document.h26
-rw-r--r--modules/gltf/gltf_template_convert.h12
-rw-r--r--modules/gltf/structures/gltf_animation.h2
-rw-r--r--modules/interactive_music/audio_stream_interactive.cpp2
-rw-r--r--modules/minimp3/doc_classes/ResourceImporterMP3.xml4
-rw-r--r--modules/mono/csharp_script.cpp51
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs23
-rw-r--r--modules/mono/managed_callable.cpp4
-rw-r--r--modules/mono/managed_callable.h1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
-rw-r--r--modules/multiplayer/scene_replication_interface.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml19
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp11
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_depth_extension.h4
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_provider.h4
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp20
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h8
-rw-r--r--modules/openxr/openxr_api.cpp32
-rw-r--r--modules/openxr/openxr_api.h9
-rw-r--r--modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml2
-rw-r--r--modules/websocket/packet_buffer.h2
42 files changed, 754 insertions, 164 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 94aa077014..8e74de4242 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -354,6 +354,21 @@ bool GDScript::has_static_method(const StringName &p_method) const {
return member_functions.has(p_method) && member_functions[p_method]->is_static();
}
+int GDScript::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
+ if (!E) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+}
+
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
@@ -1916,6 +1931,25 @@ bool GDScriptInstance::has_method(const StringName &p_method) const {
return false;
}
+int GDScriptInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method);
+ if (E) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+ }
+ sptr = sptr->_base;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
if (unlikely(p_method == SNAME("_ready"))) {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 7c471c285b..fd5ad837f9 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -312,6 +312,9 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
virtual bool has_method(const StringName &p_method) const override;
virtual bool has_static_method(const StringName &p_method) const override;
+
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
+
virtual MethodInfo get_method_info(const StringName &p_method) const override;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
@@ -376,6 +379,9 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
+
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
+
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 177c68533e..002fc159fa 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -511,6 +511,7 @@ public:
_FORCE_INLINE_ GDScript *get_script() const { return _script; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ MethodInfo get_method_info() const { return method_info; }
+ _FORCE_INLINE_ int get_argument_count() const { return _argument_count; }
_FORCE_INLINE_ Variant get_rpc_config() const { return rpc_config; }
_FORCE_INLINE_ int get_max_stack_size() const { return _stack_size; }
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index f6fa17c84f..626ef6ccb0 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -78,6 +78,15 @@ StringName GDScriptLambdaCallable::get_method() const {
return function->get_name();
}
+int GDScriptLambdaCallable::get_argument_count(bool &r_is_valid) const {
+ if (function == nullptr) {
+ r_is_valid = false;
+ return 0;
+ }
+ r_is_valid = true;
+ return function->get_argument_count();
+}
+
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
int captures_amount = captures.size();
@@ -189,6 +198,15 @@ ObjectID GDScriptLambdaSelfCallable::get_object() const {
return object->get_instance_id();
}
+int GDScriptLambdaSelfCallable::get_argument_count(bool &r_is_valid) const {
+ if (function == nullptr) {
+ r_is_valid = false;
+ return 0;
+ }
+ r_is_valid = true;
+ return function->get_argument_count();
+}
+
void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 2c5d01aa16..45c0235913 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -59,6 +59,7 @@ public:
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
StringName get_method() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaCallable(GDScriptLambdaCallable &) = delete;
@@ -86,6 +87,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaSelfCallable(GDScriptLambdaSelfCallable &) = delete;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 8d98c0b11c..d706f4e9a3 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -856,7 +856,7 @@ void GDScriptParser::parse_extends() {
}
}
-template <class T>
+template <typename T>
void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static) {
advance();
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 583b60bf16..ea67f1eaff 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -774,7 +774,7 @@ public:
bool has_function(const StringName &p_name) const {
return has_member(p_name) && members[members_indices[p_name]].type == Member::FUNCTION;
}
- template <class T>
+ template <typename T>
void add_member(T *p_member_node) {
members_indices[p_member_node->identifier->name] = members.size();
members.push_back(Member(p_member_node));
@@ -1167,7 +1167,7 @@ public:
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
- template <class T>
+ template <typename T>
void add_local(T *p_local, FunctionNode *p_source_function) {
locals_indices[p_local->identifier->name] = locals.size();
locals.push_back(Local(p_local, p_source_function));
@@ -1426,7 +1426,7 @@ private:
void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
void reset_extents(Node *p_node, Node *p_from);
- template <class T>
+ template <typename T>
T *alloc_node() {
T *node = memnew(T);
@@ -1473,7 +1473,7 @@ private:
void parse_class_name();
void parse_extends();
void parse_class_body(bool p_is_multiline);
- template <class T>
+ template <typename T>
void parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static = false);
SignalNode *parse_signal(bool p_is_static);
EnumNode *parse_enum(bool p_is_static);
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index df014d3cfe..3139371eb5 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -68,6 +68,10 @@ StringName GDScriptRPCCallable::get_method() const {
return method;
}
+int GDScriptRPCCallable::get_argument_count(bool &r_is_valid) const {
+ return object->get_method_argument_count(method, &r_is_valid);
+}
+
void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error);
}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
index 66052157be..2ca6290951 100644
--- a/modules/gdscript/gdscript_rpc_callable.h
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -52,6 +52,7 @@ public:
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
StringName get_method() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
diff --git a/modules/gdscript/gdscript_utility_callable.cpp b/modules/gdscript/gdscript_utility_callable.cpp
index 7708a18044..edd7e05b22 100644
--- a/modules/gdscript/gdscript_utility_callable.cpp
+++ b/modules/gdscript/gdscript_utility_callable.cpp
@@ -80,6 +80,21 @@ ObjectID GDScriptUtilityCallable::get_object() const {
return ObjectID();
}
+int GDScriptUtilityCallable::get_argument_count(bool &r_is_valid) const {
+ switch (type) {
+ case TYPE_INVALID:
+ r_is_valid = false;
+ return 0;
+ case TYPE_GLOBAL:
+ r_is_valid = true;
+ return Variant::get_utility_function_argument_count(function_name);
+ case TYPE_GDSCRIPT:
+ r_is_valid = true;
+ return GDScriptUtilityFunctions::get_function_argument_count(function_name);
+ }
+ ERR_FAIL_V_MSG(0, "Invalid type.");
+}
+
void GDScriptUtilityCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
switch (type) {
case TYPE_INVALID:
diff --git a/modules/gdscript/gdscript_utility_callable.h b/modules/gdscript/gdscript_utility_callable.h
index 675bc4ddd9..c5736e815f 100644
--- a/modules/gdscript/gdscript_utility_callable.h
+++ b/modules/gdscript/gdscript_utility_callable.h
@@ -57,6 +57,7 @@ public:
bool is_valid() const override;
StringName get_method() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptUtilityCallable(const StringName &p_function_name);
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index f8cb460e40..e5b0f55df8 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -759,7 +759,7 @@ Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringN
return info->info.arguments[p_arg].type;
}
-int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function) {
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
ERR_FAIL_NULL_V(info, 0);
return info->info.arguments.size();
diff --git a/modules/gdscript/gdscript_utility_functions.h b/modules/gdscript/gdscript_utility_functions.h
index 40e9379a3a..1c4e4452c8 100644
--- a/modules/gdscript/gdscript_utility_functions.h
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -46,7 +46,7 @@ public:
static Variant::Type get_function_return_type(const StringName &p_function);
static StringName get_function_return_class(const StringName &p_function);
static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
- static int get_function_argument_count(const StringName &p_function, int p_arg);
+ static int get_function_argument_count(const StringName &p_function);
static bool is_function_vararg(const StringName &p_function);
static bool is_function_constant(const StringName &p_function);
diff --git a/modules/gdscript/tests/scripts/runtime/features/argument_count.gd b/modules/gdscript/tests/scripts/runtime/features/argument_count.gd
new file mode 100644
index 0000000000..c67ce25cbe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/argument_count.gd
@@ -0,0 +1,102 @@
+extends Node
+
+func my_func_1(_foo, _bar):
+ pass
+
+func my_func_2(_foo, _bar, _baz):
+ pass
+
+static func my_static_func_1(_foo, _bar):
+ pass
+
+static func my_static_func_2(_foo, _bar, _baz):
+ pass
+
+@rpc
+func my_rpc_func_1(_foo, _bar):
+ pass
+
+@rpc
+func my_rpc_func_2(_foo, _bar, _baz):
+ pass
+
+func test():
+ # Test built-in methods.
+ var builtin_callable_1 : Callable = add_to_group
+ print(builtin_callable_1.get_argument_count()) # Should print 2.
+ var builtin_callable_2 : Callable = find_child
+ print(builtin_callable_2.get_argument_count()) # Should print 3.
+
+ # Test built-in vararg methods.
+ var builtin_vararg_callable_1 : Callable = call_thread_safe
+ print(builtin_vararg_callable_1.get_argument_count()) # Should print 1.
+ var builtin_vararg_callable_2 : Callable = rpc_id
+ print(builtin_vararg_callable_2.get_argument_count()) # Should print 2.
+
+ # Test plain methods.
+ var callable_1 : Callable = my_func_1
+ print(callable_1.get_argument_count()) # Should print 2.
+ var callable_2 : Callable = my_func_2
+ print(callable_2.get_argument_count()) # Should print 3.
+
+ # Test static methods.
+ var static_callable_1 : Callable = my_static_func_1
+ print(static_callable_1.get_argument_count()) # Should print 2.
+ var static_callable_2 : Callable = my_static_func_2
+ print(static_callable_2.get_argument_count()) # Should print 3.
+
+ # Test rpc methods.
+ var rpc_callable_1 : Callable = my_rpc_func_1
+ print(rpc_callable_1.get_argument_count()) # Should print 2.
+ var rpc_callable_2 : Callable = my_rpc_func_2
+ print(rpc_callable_2.get_argument_count()) # Should print 3.
+
+ # Test lambdas.
+ var lambda_callable_1 : Callable = func(_foo, _bar): pass
+ print(lambda_callable_1.get_argument_count()) # Should print 2.
+ var lambda_callable_2 : Callable = func(_foo, _bar, _baz): pass
+ print(lambda_callable_2.get_argument_count()) # Should print 3.
+
+ # Test lambas with self.
+ var lambda_self_callable_1 : Callable = func(_foo, _bar): return self
+ print(lambda_self_callable_1.get_argument_count()) # Should print 2.
+ var lambda_self_callable_2 : Callable = func(_foo, _bar, _baz): return self
+ print(lambda_self_callable_2.get_argument_count()) # Should print 3.
+
+ # Test bind.
+ var bind_callable_1 : Callable = my_func_2.bind(1)
+ print(bind_callable_1.get_argument_count()) # Should print 2.
+ var bind_callable_2 : Callable = my_func_2.bind(1, 2)
+ print(bind_callable_2.get_argument_count()) # Should print 1.
+
+ # Test unbind.
+ var unbind_callable_1 : Callable = my_func_2.unbind(1)
+ print(unbind_callable_1.get_argument_count()) # Should print 4.
+ var unbind_callable_2 : Callable = my_func_2.unbind(2)
+ print(unbind_callable_2.get_argument_count()) # Should print 5.
+
+ # Test variant callables.
+ var string_tmp := String()
+ var variant_callable_1 : Callable = string_tmp.replace
+ print(variant_callable_1.get_argument_count()) # Should print 2.
+ var variant_callable_2 : Callable = string_tmp.rsplit
+ print(variant_callable_2.get_argument_count()) # Should print 3.
+
+ # Test variant vararg callables.
+ var callable_tmp := Callable()
+ var variant_vararg_callable_1 : Callable = callable_tmp.call
+ print(variant_vararg_callable_1.get_argument_count()) # Should print 0.
+ var variant_vararg_callable_2 : Callable = callable_tmp.rpc_id
+ print(variant_vararg_callable_2.get_argument_count()) # Should print 1.
+
+ # Test global methods.
+ var global_callable_1 = is_equal_approx
+ print(global_callable_1.get_argument_count()) # Should print 2.
+ var global_callable_2 = inverse_lerp
+ print(global_callable_2.get_argument_count()) # Should print 3.
+
+ # Test GDScript methods.
+ var gdscript_callable_1 = char
+ print(gdscript_callable_1.get_argument_count()) # Should print 1.
+ var gdscript_callable_2 = is_instance_of
+ print(gdscript_callable_2.get_argument_count()) # Should print 2.
diff --git a/modules/gdscript/tests/scripts/runtime/features/argument_count.out b/modules/gdscript/tests/scripts/runtime/features/argument_count.out
new file mode 100644
index 0000000000..42c4ece37d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/argument_count.out
@@ -0,0 +1,27 @@
+GDTEST_OK
+2
+3
+1
+2
+2
+3
+2
+3
+2
+3
+2
+3
+2
+3
+2
+1
+4
+5
+2
+3
+0
+1
+2
+3
+1
+2
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index 8234ed9eac..b4c3db7618 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFMesh represents an GLTF mesh.
+ GLTFMesh represents a GLTF mesh.
</brief_description>
<description>
GLTFMesh handles 3D mesh data imported from GLTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index 50789a0ebf..9ad7c0f4c6 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFTexture represents a texture in an GLTF file.
+ GLTFTexture represents a texture in a GLTF file.
</brief_description>
<description>
</description>
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index b53be7f855..6e7ca370dd 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -884,42 +884,40 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) {
d["componentType"] = accessor->component_type;
d["count"] = accessor->count;
d["type"] = _get_accessor_type_name(accessor->type);
- d["byteOffset"] = accessor->byte_offset;
d["normalized"] = accessor->normalized;
d["max"] = accessor->max;
d["min"] = accessor->min;
- d["bufferView"] = accessor->buffer_view; //optional because it may be sparse...
-
- // Dictionary s;
- // s["count"] = accessor->sparse_count;
- // ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR);
-
- // s["indices"] = accessor->sparse_accessors;
- // ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR);
-
- // Dictionary si;
+ if (accessor->buffer_view != -1) {
+ // bufferView may be omitted to zero-initialize the buffer. When this happens, byteOffset MUST also be omitted.
+ d["byteOffset"] = accessor->byte_offset;
+ d["bufferView"] = accessor->buffer_view;
+ }
- // si["bufferView"] = accessor->sparse_indices_buffer_view;
+ if (accessor->sparse_count > 0) {
+ Dictionary s;
+ s["count"] = accessor->sparse_count;
- // ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR);
- // si["componentType"] = accessor->sparse_indices_component_type;
+ Dictionary si;
+ si["bufferView"] = accessor->sparse_indices_buffer_view;
+ si["componentType"] = accessor->sparse_indices_component_type;
+ if (accessor->sparse_indices_byte_offset != -1) {
+ si["byteOffset"] = accessor->sparse_indices_byte_offset;
+ }
+ ERR_FAIL_COND_V(!si.has("bufferView") || !si.has("componentType"), ERR_PARSE_ERROR);
+ s["indices"] = si;
- // if (si.has("byteOffset")) {
- // si["byteOffset"] = accessor->sparse_indices_byte_offset;
- // }
+ Dictionary sv;
+ sv["bufferView"] = accessor->sparse_values_buffer_view;
+ if (accessor->sparse_values_byte_offset != -1) {
+ sv["byteOffset"] = accessor->sparse_values_byte_offset;
+ }
+ ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR);
+ s["values"] = sv;
- // ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR);
- // s["indices"] = si;
- // Dictionary sv;
+ ERR_FAIL_COND_V(!s.has("count") || !s.has("indices") || !s.has("values"), ERR_PARSE_ERROR);
+ d["sparse"] = s;
+ }
- // sv["bufferView"] = accessor->sparse_values_buffer_view;
- // if (sv.has("byteOffset")) {
- // sv["byteOffset"] = accessor->sparse_values_byte_offset;
- // }
- // ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR);
- // s["values"] = sv;
- // ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR);
- // d["sparse"] = s;
accessors.push_back(d);
}
@@ -1026,8 +1024,6 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
}
if (d.has("sparse")) {
- //eeh..
-
const Dictionary &s = d["sparse"];
ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR);
@@ -1143,7 +1139,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
const uint32_t offset = bv->byte_offset = p_byte_offset;
Vector<uint8_t> &gltf_buffer = p_state->buffers.write[0];
- int stride = _get_component_type_size(p_component_type);
+ int stride = component_count * component_size;
if (p_for_vertex && stride % 4) {
stride += 4 - (stride % 4); //according to spec must be multiple of 4
}
@@ -1152,13 +1148,14 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
print_verbose("glTF: encoding accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length));
- const int buffer_end = (stride * (p_count - 1)) + _get_component_type_size(p_component_type);
+ const int buffer_end = (stride * (p_count - 1)) + component_size;
// TODO define bv->byte_stride
bv->byte_offset = gltf_buffer.size();
if (p_for_vertex_indices) {
bv->indices = true;
} else if (p_for_vertex) {
bv->vertex_attributes = true;
+ bv->byte_stride = stride;
}
switch (p_component_type) {
@@ -1300,6 +1297,11 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA);
ERR_FAIL_COND_V((int)(offset + buffer_end) > gltf_buffer.size(), ERR_INVALID_DATA);
+ int pad_bytes = (4 - gltf_buffer.size()) & 3;
+ for (int i = 0; i < pad_bytes; i++) {
+ gltf_buffer.push_back(0);
+ }
+
r_accessor = bv->buffer = p_state->buffer_views.size();
p_state->buffer_views.push_back(bv);
return OK;
@@ -1519,8 +1521,12 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
type_max.resize(element_count);
Vector<double> type_min;
type_min.resize(element_count);
+ int max_index = 0;
for (int i = 0; i < p_attribs.size(); i++) {
attribs.write[i] = p_attribs[i];
+ if (p_attribs[i] > max_index) {
+ max_index = p_attribs[i];
+ }
if (i == 0) {
for (int32_t type_i = 0; type_i < element_count; type_i++) {
type_max.write[type_i] = attribs[(i * element_count) + type_i];
@@ -1539,7 +1545,12 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
GLTFBufferIndex buffer_view_i;
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_SCALAR;
- const int component_type = GLTFDocument::COMPONENT_TYPE_INT;
+ int component_type;
+ if (max_index > 65535 || p_for_vertex) {
+ component_type = GLTFDocument::COMPONENT_TYPE_INT;
+ } else {
+ component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+ }
accessor->max = type_max;
accessor->min = type_min;
@@ -1557,7 +1568,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
return p_state->accessors.size() - 1;
}
-Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector<int> &p_packed_vertex_ids) {
const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<int> ret;
@@ -1566,17 +1577,23 @@ Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> p_state, const
}
const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size();
+ int ret_size = attribs.size();
+ if (!p_packed_vertex_ids.is_empty()) {
+ ERR_FAIL_COND_V(p_packed_vertex_ids[p_packed_vertex_ids.size() - 1] >= ret_size, ret);
+ ret_size = p_packed_vertex_ids.size();
+ }
ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = int(attribs_ptr[i]);
+ for (int i = 0; i < ret_size; i++) {
+ int src_i = i;
+ if (!p_packed_vertex_ids.is_empty()) {
+ src_i = p_packed_vertex_ids[i];
}
+ ret.write[i] = int(attribs_ptr[src_i]);
}
return ret;
}
-Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector<int> &p_packed_vertex_ids) {
const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<float> ret;
@@ -1585,12 +1602,18 @@ Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> p_state, c
}
const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size();
+ int ret_size = attribs.size();
+ if (!p_packed_vertex_ids.is_empty()) {
+ ERR_FAIL_COND_V(p_packed_vertex_ids[p_packed_vertex_ids.size() - 1] >= ret_size, ret);
+ ret_size = p_packed_vertex_ids.size();
+ }
ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = float(attribs_ptr[i]);
+ for (int i = 0; i < ret_size; i++) {
+ int src_i = i;
+ if (!p_packed_vertex_ids.is_empty()) {
+ src_i = p_packed_vertex_ids[i];
}
+ ret.write[i] = float(attribs_ptr[src_i]);
}
return ret;
}
@@ -1863,7 +1886,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p
return p_state->accessors.size() - 1;
}
-Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector<int> &p_packed_vertex_ids) {
const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Vector2> ret;
@@ -1873,12 +1896,18 @@ Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> p_state, c
ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret);
const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / 2;
+ int ret_size = attribs.size() / 2;
+ if (!p_packed_vertex_ids.is_empty()) {
+ ERR_FAIL_COND_V(p_packed_vertex_ids[p_packed_vertex_ids.size() - 1] >= ret_size, ret);
+ ret_size = p_packed_vertex_ids.size();
+ }
ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]);
+ for (int i = 0; i < ret_size; i++) {
+ int src_i = i;
+ if (!p_packed_vertex_ids.is_empty()) {
+ src_i = p_packed_vertex_ids[i];
}
+ ret.write[i] = Vector2(attribs_ptr[src_i * 2 + 0], attribs_ptr[src_i * 2 + 1]);
}
return ret;
}
@@ -1976,6 +2005,112 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state,
return p_state->accessors.size() - 1;
}
+GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p_state, const Vector<Vector3> p_attribs, const Vector<Vector3> p_reference_attribs, const float p_reference_multiplier, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+
+ const int element_count = 3;
+ Vector<double> attribs;
+ Vector<double> type_max;
+ Vector<double> type_min;
+ attribs.resize(p_attribs.size() * element_count);
+ type_max.resize(element_count);
+ type_min.resize(element_count);
+
+ Vector<double> changed_indices;
+ Vector<double> changed_values;
+ int max_changed_index = 0;
+
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Vector3 attrib = p_attribs[i];
+ bool is_different = false;
+ if (i < p_reference_attribs.size()) {
+ is_different = !(attrib * p_reference_multiplier).is_equal_approx(p_reference_attribs[i]);
+ if (!is_different) {
+ attrib = p_reference_attribs[i];
+ }
+ } else {
+ is_different = !(attrib * p_reference_multiplier).is_zero_approx();
+ if (!is_different) {
+ attrib = Vector3();
+ }
+ }
+ attribs.write[(i * element_count) + 0] = _filter_number(attrib.x);
+ attribs.write[(i * element_count) + 1] = _filter_number(attrib.y);
+ attribs.write[(i * element_count) + 2] = _filter_number(attrib.z);
+ if (is_different) {
+ changed_indices.push_back(i);
+ if (i > max_changed_index) {
+ max_changed_index = i;
+ }
+ changed_values.push_back(_filter_number(attrib.x));
+ changed_values.push_back(_filter_number(attrib.y));
+ changed_values.push_back(_filter_number(attrib.z));
+ }
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+ _round_min_max_components(type_min, type_max);
+
+ if (attribs.size() % element_count != 0) {
+ return -1;
+ }
+
+ Ref<GLTFAccessor> sparse_accessor;
+ sparse_accessor.instantiate();
+ int64_t size = p_state->buffers[0].size();
+ const GLTFType type = GLTFType::TYPE_VEC3;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ sparse_accessor->normalized = false;
+ sparse_accessor->count = p_attribs.size();
+ sparse_accessor->type = type;
+ sparse_accessor->component_type = component_type;
+ if (p_reference_accessor < p_state->accessors.size() && p_reference_accessor >= 0 && p_state->accessors[p_reference_accessor].is_valid()) {
+ sparse_accessor->byte_offset = p_state->accessors[p_reference_accessor]->byte_offset;
+ sparse_accessor->buffer_view = p_state->accessors[p_reference_accessor]->buffer_view;
+ }
+ sparse_accessor->max = type_max;
+ sparse_accessor->min = type_min;
+ int sparse_accessor_index_stride = max_changed_index > 65535 ? 4 : 2;
+
+ int sparse_accessor_storage_size = changed_indices.size() * (sparse_accessor_index_stride + element_count * sizeof(float));
+ int conventional_storage_size = p_attribs.size() * element_count * sizeof(float);
+
+ if (changed_indices.size() > 0 && sparse_accessor_storage_size < conventional_storage_size) {
+ // It must be worthwhile to use a sparse accessor.
+
+ GLTFBufferIndex buffer_view_i_indices = -1;
+ GLTFBufferIndex buffer_view_i_values = -1;
+ if (sparse_accessor_index_stride == 4) {
+ sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_INT;
+ } else {
+ sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+ }
+ if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) {
+ return -1;
+ }
+ // We use changed_indices.size() here, because we must pass the number of vec3 values rather than the number of components.
+ if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) {
+ return -1;
+ }
+ sparse_accessor->sparse_indices_buffer_view = buffer_view_i_indices;
+ sparse_accessor->sparse_values_buffer_view = buffer_view_i_values;
+ sparse_accessor->sparse_count = changed_indices.size();
+ } else if (changed_indices.size() > 0) {
+ GLTFBufferIndex buffer_view_i;
+ sparse_accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ sparse_accessor->buffer_view = buffer_view_i;
+ }
+ p_state->accessors.push_back(sparse_accessor);
+
+ return p_state->accessors.size() - 1;
+}
+
GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state, const Vector<Transform3D> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
@@ -2045,7 +2180,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state
return p_state->accessors.size() - 1;
}
-Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector<int> &p_packed_vertex_ids) {
const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Vector3> ret;
@@ -2055,17 +2190,23 @@ Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> p_state, c
ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret);
const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / 3;
+ int ret_size = attribs.size() / 3;
+ if (!p_packed_vertex_ids.is_empty()) {
+ ERR_FAIL_COND_V(p_packed_vertex_ids[p_packed_vertex_ids.size() - 1] >= ret_size, ret);
+ ret_size = p_packed_vertex_ids.size();
+ }
ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = Vector3(attribs_ptr[i * 3 + 0], attribs_ptr[i * 3 + 1], attribs_ptr[i * 3 + 2]);
+ for (int i = 0; i < ret_size; i++) {
+ int src_i = i;
+ if (!p_packed_vertex_ids.is_empty()) {
+ src_i = p_packed_vertex_ids[i];
}
+ ret.write[i] = Vector3(attribs_ptr[src_i * 3 + 0], attribs_ptr[src_i * 3 + 1], attribs_ptr[src_i * 3 + 2]);
}
return ret;
}
-Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector<int> &p_packed_vertex_ids) {
const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Color> ret;
@@ -2082,12 +2223,18 @@ Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, co
ERR_FAIL_COND_V(attribs.size() % vec_len != 0, ret);
const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / vec_len;
+ int ret_size = attribs.size() / vec_len;
+ if (!p_packed_vertex_ids.is_empty()) {
+ ERR_FAIL_COND_V(p_packed_vertex_ids[p_packed_vertex_ids.size() - 1] >= ret_size, ret);
+ ret_size = p_packed_vertex_ids.size();
+ }
ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = Color(attribs_ptr[i * vec_len + 0], attribs_ptr[i * vec_len + 1], attribs_ptr[i * vec_len + 2], vec_len == 4 ? attribs_ptr[i * 4 + 3] : 1.0);
+ for (int i = 0; i < ret_size; i++) {
+ int src_i = i;
+ if (!p_packed_vertex_ids.is_empty()) {
+ src_i = p_packed_vertex_ids[i];
}
+ ret.write[i] = Color(attribs_ptr[src_i * vec_len + 0], attribs_ptr[src_i * vec_len + 1], attribs_ptr[src_i * vec_len + 2], vec_len == 4 ? attribs_ptr[src_i * 4 + 3] : 1.0);
}
return ret;
}
@@ -2430,16 +2577,16 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
Vector<int32_t> mesh_indices = array[Mesh::ARRAY_INDEX];
if (mesh_indices.size()) {
if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) {
- //swap around indices, convert ccw to cw for front face
+ // Swap around indices, convert ccw to cw for front face.
const int is = mesh_indices.size();
for (int k = 0; k < is; k += 3) {
SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]);
}
}
- primitive["indices"] = _encode_accessor_as_ints(p_state, mesh_indices, true, true);
+ primitive["indices"] = _encode_accessor_as_ints(p_state, mesh_indices, false, true);
} else {
if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) {
- //generate indices because they need to be swapped for CW/CCW
+ // Generate indices because they need to be swapped for CW/CCW.
const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
Ref<SurfaceTool> st;
st.instantiate();
@@ -2455,50 +2602,77 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
generated_indices.write[k + 2] = k + 1;
}
}
- primitive["indices"] = _encode_accessor_as_ints(p_state, generated_indices, true, true);
+ primitive["indices"] = _encode_accessor_as_ints(p_state, generated_indices, false, true);
}
}
}
primitive["attributes"] = attributes;
- //blend shapes
+ // Blend shapes
print_verbose("glTF: Mesh has targets");
if (import_mesh->get_blend_shape_count()) {
ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode();
+ const float normal_tangent_sparse_rounding = 0.001;
for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) {
Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i);
Dictionary t;
Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
+ Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
Array mesh_arrays = import_mesh->get_surface_arrays(surface_i);
- if (varr.size()) {
- Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
+ if (varr.size() && varr.size() == src_varr.size()) {
if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) {
const int max_idx = src_varr.size();
for (int blend_i = 0; blend_i < max_idx; blend_i++) {
- varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i];
+ varr.write[blend_i] = varr[blend_i] - src_varr[blend_i];
+ }
+ }
+ GLTFAccessorIndex position_accessor = attributes["POSITION"];
+ if (position_accessor != -1) {
+ int new_accessor = _encode_sparse_accessor_as_vec3(p_state, varr, Vector<Vector3>(), 1.0, true, -1);
+ if (new_accessor != -1) {
+ t["POSITION"] = new_accessor;
}
}
-
- t["POSITION"] = _encode_accessor_as_vec3(p_state, varr, true);
}
Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
- if (narr.size()) {
- t["NORMAL"] = _encode_accessor_as_vec3(p_state, narr, true);
+ Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL];
+ if (narr.size() && narr.size() == src_narr.size()) {
+ if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) {
+ const int max_idx = src_narr.size();
+ for (int blend_i = 0; blend_i < max_idx; blend_i++) {
+ narr.write[blend_i] = narr[blend_i] - src_narr[blend_i];
+ }
+ }
+ GLTFAccessorIndex normal_accessor = attributes["NORMAL"];
+ if (normal_accessor != -1) {
+ int new_accessor = _encode_sparse_accessor_as_vec3(p_state, narr, Vector<Vector3>(), normal_tangent_sparse_rounding, true, -1);
+ if (new_accessor != -1) {
+ t["NORMAL"] = new_accessor;
+ }
+ }
}
Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
- if (tarr.size()) {
+ Vector<real_t> src_tarr = array[Mesh::ARRAY_TANGENT];
+ if (tarr.size() && tarr.size() == src_tarr.size()) {
const int ret_size = tarr.size() / 4;
Vector<Vector3> attribs;
attribs.resize(ret_size);
for (int i = 0; i < ret_size; i++) {
Vector3 vec3;
- vec3.x = tarr[(i * 4) + 0];
- vec3.y = tarr[(i * 4) + 1];
- vec3.z = tarr[(i * 4) + 2];
+ vec3.x = tarr[(i * 4) + 0] - src_tarr[(i * 4) + 0];
+ vec3.y = tarr[(i * 4) + 1] - src_tarr[(i * 4) + 1];
+ vec3.z = tarr[(i * 4) + 2] - src_tarr[(i * 4) + 2];
+ attribs.write[i] = vec3;
+ }
+ GLTFAccessorIndex tangent_accessor = attributes["TANGENT"];
+ if (tangent_accessor != -1) {
+ int new_accessor = _encode_sparse_accessor_as_vec3(p_state, attribs, Vector<Vector3>(), normal_tangent_sparse_rounding, true, -1);
+ if (new_accessor != -1) {
+ t["TANGENT"] = new_accessor;
+ }
}
- t["TANGENT"] = _encode_accessor_as_vec3(p_state, attribs, true);
}
targets.push_back(t);
}
@@ -2623,24 +2797,72 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
primitive = primitives2[mode];
}
+ int32_t orig_vertex_num = 0;
ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR);
- int32_t vertex_num = 0;
if (a.has("POSITION")) {
PackedVector3Array vertices = _decode_accessor_as_vec3(p_state, a["POSITION"], true);
array[Mesh::ARRAY_VERTEX] = vertices;
- vertex_num = vertices.size();
+ orig_vertex_num = vertices.size();
+ }
+ int32_t vertex_num = orig_vertex_num;
+
+ Vector<int> indices;
+ Vector<int> indices_mapping;
+ Vector<int> indices_rev_mapping;
+ Vector<int> indices_vec4_mapping;
+ if (p.has("indices")) {
+ indices = _decode_accessor_as_ints(p_state, p["indices"], false);
+ const int is = indices.size();
+
+ if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ // Swap around indices, convert ccw to cw for front face.
+
+ int *w = indices.ptrw();
+ for (int k = 0; k < is; k += 3) {
+ SWAP(w[k + 1], w[k + 2]);
+ }
+ }
+
+ const int *indices_w = indices.ptrw();
+ Vector<bool> used_indices;
+ used_indices.resize_zeroed(orig_vertex_num);
+ bool *used_w = used_indices.ptrw();
+ for (int idx_i = 0; idx_i < is; idx_i++) {
+ ERR_FAIL_INDEX_V(indices_w[idx_i], orig_vertex_num, ERR_INVALID_DATA);
+ used_w[indices_w[idx_i]] = true;
+ }
+ indices_rev_mapping.resize_zeroed(orig_vertex_num);
+ int *rev_w = indices_rev_mapping.ptrw();
+ vertex_num = 0;
+ for (int vert_i = 0; vert_i < orig_vertex_num; vert_i++) {
+ if (used_w[vert_i]) {
+ rev_w[vert_i] = indices_mapping.size();
+ indices_mapping.push_back(vert_i);
+ indices_vec4_mapping.push_back(vert_i * 4 + 0);
+ indices_vec4_mapping.push_back(vert_i * 4 + 1);
+ indices_vec4_mapping.push_back(vert_i * 4 + 2);
+ indices_vec4_mapping.push_back(vert_i * 4 + 3);
+ vertex_num++;
+ }
+ }
+ }
+ ERR_FAIL_COND_V(vertex_num <= 0, ERR_INVALID_DECLARATION);
+
+ if (a.has("POSITION")) {
+ PackedVector3Array vertices = _decode_accessor_as_vec3(p_state, a["POSITION"], true, indices_mapping);
+ array[Mesh::ARRAY_VERTEX] = vertices;
}
if (a.has("NORMAL")) {
- array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(p_state, a["NORMAL"], true);
+ array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(p_state, a["NORMAL"], true, indices_mapping);
}
if (a.has("TANGENT")) {
- array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(p_state, a["TANGENT"], true);
+ array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(p_state, a["TANGENT"], true, indices_vec4_mapping);
}
if (a.has("TEXCOORD_0")) {
- array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_0"], true);
+ array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_0"], true, indices_mapping);
}
if (a.has("TEXCOORD_1")) {
- array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_1"], true);
+ array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_1"], true, indices_mapping);
}
for (int custom_i = 0; custom_i < 3; custom_i++) {
Vector<float> cur_custom;
@@ -2651,12 +2873,12 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
int num_channels = 0;
if (a.has(gltf_texcoord_key)) {
- texcoord_first = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true);
+ texcoord_first = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true, indices_mapping);
num_channels = 2;
}
gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
if (a.has(gltf_texcoord_key)) {
- texcoord_second = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true);
+ texcoord_second = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true, indices_mapping);
num_channels = 4;
}
if (!num_channels) {
@@ -2697,15 +2919,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
}
if (a.has("COLOR_0")) {
- array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(p_state, a["COLOR_0"], true);
+ array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(p_state, a["COLOR_0"], true, indices_mapping);
has_vertex_color = true;
}
if (a.has("JOINTS_0") && !a.has("JOINTS_1")) {
- array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true);
+ PackedInt32Array joints_0 = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true, indices_vec4_mapping);
+ ERR_FAIL_COND_V(joints_0.size() != 4 * vertex_num, ERR_INVALID_DATA);
+ array[Mesh::ARRAY_BONES] = joints_0;
} else if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
- PackedInt32Array joints_0 = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true);
- PackedInt32Array joints_1 = _decode_accessor_as_ints(p_state, a["JOINTS_1"], true);
+ PackedInt32Array joints_0 = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true, indices_vec4_mapping);
+ PackedInt32Array joints_1 = _decode_accessor_as_ints(p_state, a["JOINTS_1"], true, indices_vec4_mapping);
ERR_FAIL_COND_V(joints_0.size() != joints_1.size(), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(joints_0.size() != 4 * vertex_num, ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
Vector<int> joints;
joints.resize(vertex_num * weight_8_count);
@@ -2722,8 +2947,9 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array[Mesh::ARRAY_BONES] = joints;
}
if (a.has("WEIGHTS_0") && !a.has("WEIGHTS_1")) {
- Vector<float> weights = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true);
- { //gltf does not seem to normalize the weights for some reason..
+ Vector<float> weights = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true, indices_vec4_mapping);
+ ERR_FAIL_COND_V(weights.size() != 4 * vertex_num, ERR_INVALID_DATA);
+ { // glTF does not seem to normalize the weights for some reason.
int wc = weights.size();
float *w = weights.ptrw();
@@ -2743,10 +2969,11 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
array[Mesh::ARRAY_WEIGHTS] = weights;
} else if (a.has("WEIGHTS_0") && a.has("WEIGHTS_1")) {
- Vector<float> weights_0 = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true);
- Vector<float> weights_1 = _decode_accessor_as_floats(p_state, a["WEIGHTS_1"], true);
+ Vector<float> weights_0 = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true, indices_vec4_mapping);
+ Vector<float> weights_1 = _decode_accessor_as_floats(p_state, a["WEIGHTS_1"], true, indices_vec4_mapping);
Vector<float> weights;
ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(weights_0.size() != 4 * vertex_num, ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
weights.resize(vertex_num * weight_8_count);
for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) {
@@ -2759,7 +2986,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
weights.write[vertex_i * weight_8_count + 6] = weights_1[vertex_i * JOINT_GROUP_SIZE + 2];
weights.write[vertex_i * weight_8_count + 7] = weights_1[vertex_i * JOINT_GROUP_SIZE + 3];
}
- { //gltf does not seem to normalize the weights for some reason..
+ { // glTF does not seem to normalize the weights for some reason.
int wc = weights.size();
float *w = weights.ptrw();
@@ -2788,25 +3015,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array[Mesh::ARRAY_WEIGHTS] = weights;
}
- if (p.has("indices")) {
- Vector<int> indices = _decode_accessor_as_ints(p_state, p["indices"], false);
-
- if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
- //swap around indices, convert ccw to cw for front face
-
- const int is = indices.size();
- int *w = indices.ptrw();
- for (int k = 0; k < is; k += 3) {
- SWAP(w[k + 1], w[k + 2]);
- }
+ if (!indices.is_empty()) {
+ int *w = indices.ptrw();
+ const int is = indices.size();
+ for (int ind_i = 0; ind_i < is; ind_i++) {
+ w[ind_i] = indices_rev_mapping[indices[ind_i]];
}
array[Mesh::ARRAY_INDEX] = indices;
} else if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
- //generate indices because they need to be swapped for CW/CCW
+ // Generate indices because they need to be swapped for CW/CCW.
const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
ERR_FAIL_COND_V(vertices.is_empty(), ERR_PARSE_ERROR);
- Vector<int> indices;
const int vs = vertices.size();
indices.resize(vs);
{
@@ -2870,13 +3090,11 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
Array morphs;
- //blend shapes
+ // Blend shapes
if (p.has("targets")) {
print_verbose("glTF: Mesh has targets");
const Array &targets = p["targets"];
- //ideally BLEND_SHAPE_MODE_RELATIVE since gltf2 stores in displacement
- //but it could require a larger refactor?
import_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
if (j == 0) {
@@ -2903,7 +3121,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
if (t.has("POSITION")) {
- Vector<Vector3> varr = _decode_accessor_as_vec3(p_state, t["POSITION"], true);
+ Vector<Vector3> varr = _decode_accessor_as_vec3(p_state, t["POSITION"], true, indices_mapping);
const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
const int size = src_varr.size();
ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
@@ -2925,7 +3143,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array_copy[Mesh::ARRAY_VERTEX] = varr;
}
if (t.has("NORMAL")) {
- Vector<Vector3> narr = _decode_accessor_as_vec3(p_state, t["NORMAL"], true);
+ Vector<Vector3> narr = _decode_accessor_as_vec3(p_state, t["NORMAL"], true, indices_mapping);
const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL];
int size = src_narr.size();
ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
@@ -2947,7 +3165,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array_copy[Mesh::ARRAY_NORMAL] = narr;
}
if (t.has("TANGENT")) {
- const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(p_state, t["TANGENT"], true);
+ const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(p_state, t["TANGENT"], true, indices_mapping);
const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT];
ERR_FAIL_COND_V(src_tangents.is_empty(), ERR_PARSE_ERROR);
@@ -5517,7 +5735,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, const GL
}
}
-template <class T>
+template <typename T>
struct SceneFormatImporterGLTFInterpolate {
T lerp(const T &a, const T &b, float c) const {
return a + (b - a) * c;
@@ -5567,7 +5785,7 @@ struct SceneFormatImporterGLTFInterpolate<Quaternion> {
}
};
-template <class T>
+template <typename T>
T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
ERR_FAIL_COND_V(p_values.is_empty(), T());
if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 1682e9eeb7..1001b482cd 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -57,13 +57,6 @@ public:
ARRAY_BUFFER = 34962,
ELEMENT_ARRAY_BUFFER = 34963,
- TYPE_BYTE = 5120,
- TYPE_UNSIGNED_BYTE = 5121,
- TYPE_SHORT = 5122,
- TYPE_UNSIGNED_SHORT = 5123,
- TYPE_UNSIGNED_INT = 5125,
- TYPE_FLOAT = 5126,
-
COMPONENT_TYPE_BYTE = 5120,
COMPONENT_TYPE_UNSIGNED_BYTE = 5121,
COMPONENT_TYPE_SHORT = 5122,
@@ -155,19 +148,24 @@ private:
const bool p_for_vertex);
Vector<float> _decode_accessor_as_floats(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
- const bool p_for_vertex);
+ const bool p_for_vertex,
+ const Vector<int> &p_packed_vertex_ids = Vector<int>());
Vector<int> _decode_accessor_as_ints(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
- const bool p_for_vertex);
+ const bool p_for_vertex,
+ const Vector<int> &p_packed_vertex_ids = Vector<int>());
Vector<Vector2> _decode_accessor_as_vec2(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
- const bool p_for_vertex);
+ const bool p_for_vertex,
+ const Vector<int> &p_packed_vertex_ids = Vector<int>());
Vector<Vector3> _decode_accessor_as_vec3(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
- const bool p_for_vertex);
+ const bool p_for_vertex,
+ const Vector<int> &p_packed_vertex_ids = Vector<int>());
Vector<Color> _decode_accessor_as_color(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
- const bool p_for_vertex);
+ const bool p_for_vertex,
+ const Vector<int> &p_packed_vertex_ids = Vector<int>());
Vector<Quaternion> _decode_accessor_as_quaternion(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
@@ -217,7 +215,7 @@ private:
Light3D *_generate_light(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
Node3D *_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
void _assign_node_names(Ref<GLTFState> p_state);
- template <class T>
+ template <typename T>
T _interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values,
const float p_time,
const GLTFAnimation::Interpolation p_interp);
@@ -255,6 +253,7 @@ private:
GLTFAccessorIndex _encode_accessor_as_vec3(Ref<GLTFState> p_state,
const Vector<Vector3> p_attribs,
const bool p_for_vertex);
+ GLTFAccessorIndex _encode_sparse_accessor_as_vec3(Ref<GLTFState> p_state, const Vector<Vector3> p_attribs, const Vector<Vector3> p_reference_attribs, const float p_reference_multiplier, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor);
GLTFAccessorIndex _encode_accessor_as_color(Ref<GLTFState> p_state,
const Vector<Color> p_attribs,
const bool p_for_vertex);
@@ -273,6 +272,7 @@ private:
const int p_component_type, const bool p_normalized,
const int p_byte_offset, const bool p_for_vertex,
GLTFBufferViewIndex &r_accessor, const bool p_for_indices = false);
+
Error _encode_accessors(Ref<GLTFState> p_state);
Error _encode_buffer_views(Ref<GLTFState> p_state);
Error _serialize_materials(Ref<GLTFState> p_state);
diff --git a/modules/gltf/gltf_template_convert.h b/modules/gltf/gltf_template_convert.h
index 2743cd8a9b..46f185867a 100644
--- a/modules/gltf/gltf_template_convert.h
+++ b/modules/gltf/gltf_template_convert.h
@@ -37,7 +37,7 @@
#include "core/variant/typed_array.h"
namespace GLTFTemplateConvert {
-template <class T>
+template <typename T>
static Array to_array(const Vector<T> &p_inp) {
Array ret;
for (int i = 0; i < p_inp.size(); i++) {
@@ -46,7 +46,7 @@ static Array to_array(const Vector<T> &p_inp) {
return ret;
}
-template <class T>
+template <typename T>
static TypedArray<T> to_array(const HashSet<T> &p_inp) {
TypedArray<T> ret;
typename HashSet<T>::Iterator elem = p_inp.begin();
@@ -57,7 +57,7 @@ static TypedArray<T> to_array(const HashSet<T> &p_inp) {
return ret;
}
-template <class T>
+template <typename T>
static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
r_out.clear();
for (int i = 0; i < p_inp.size(); i++) {
@@ -65,7 +65,7 @@ static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
}
}
-template <class T>
+template <typename T>
static void set_from_array(HashSet<T> &r_out, const TypedArray<T> &p_inp) {
r_out.clear();
for (int i = 0; i < p_inp.size(); i++) {
@@ -73,7 +73,7 @@ static void set_from_array(HashSet<T> &r_out, const TypedArray<T> &p_inp) {
}
}
-template <class K, class V>
+template <typename K, typename V>
static Dictionary to_dictionary(const HashMap<K, V> &p_inp) {
Dictionary ret;
for (const KeyValue<K, V> &E : p_inp) {
@@ -82,7 +82,7 @@ static Dictionary to_dictionary(const HashMap<K, V> &p_inp) {
return ret;
}
-template <class K, class V>
+template <typename K, typename V>
static void set_from_dictionary(HashMap<K, V> &r_out, const Dictionary &p_inp) {
r_out.clear();
Array keys = p_inp.keys();
diff --git a/modules/gltf/structures/gltf_animation.h b/modules/gltf/structures/gltf_animation.h
index 7f769752c2..afc9784895 100644
--- a/modules/gltf/structures/gltf_animation.h
+++ b/modules/gltf/structures/gltf_animation.h
@@ -47,7 +47,7 @@ public:
INTERP_CUBIC_SPLINE,
};
- template <class T>
+ template <typename T>
struct Channel {
Interpolation interpolation = INTERP_LINEAR;
Vector<real_t> times;
diff --git a/modules/interactive_music/audio_stream_interactive.cpp b/modules/interactive_music/audio_stream_interactive.cpp
index ddac458463..01764d66ed 100644
--- a/modules/interactive_music/audio_stream_interactive.cpp
+++ b/modules/interactive_music/audio_stream_interactive.cpp
@@ -353,7 +353,7 @@ static void _test_and_swap(T &p_elem, uint32_t p_a, uint32_t p_b) {
}
void AudioStreamInteractive::_inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b) {
- ERR_FAIL_INDEX(p_item_a, (uint32_t)clip_count);
+ ERR_FAIL_UNSIGNED_INDEX(p_item_a, (uint32_t)clip_count);
ERR_FAIL_UNSIGNED_INDEX(p_item_b, (uint32_t)clip_count);
for (int i = 0; i < clip_count; i++) {
diff --git a/modules/minimp3/doc_classes/ResourceImporterMP3.xml b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
index a84c51cf68..72868623c7 100644
--- a/modules/minimp3/doc_classes/ResourceImporterMP3.xml
+++ b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ResourceImporterMP3" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Imports a MP3 audio file for playback.
+ Imports an MP3 audio file for playback.
</brief_description>
<description>
MP3 is a lossy audio format, with worse audio quality compared to [ResourceImporterOggVorbis] at a given bitrate.
- In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using an MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
MP3 requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
</description>
<tutorials>
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 93fb5f1dc6..858d1d3e4e 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1735,6 +1735,34 @@ bool CSharpInstance::has_method(const StringName &p_method) const {
gchandle.get_intptr(), &p_method);
}
+int CSharpInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ if (!script->is_valid() || !script->valid) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ const CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (const CSharpScript::CSharpMethodInfo &E : top->methods) {
+ if (E.name == p_method) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E.method_info.arguments.size();
+ }
+ }
+
+ top = top->base_script.ptr();
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
ERR_FAIL_COND_V(!script.is_valid(), Variant());
@@ -2579,6 +2607,29 @@ bool CSharpScript::has_method(const StringName &p_method) const {
return false;
}
+int CSharpScript::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ if (!valid) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E.method_info.arguments.size();
+ }
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
if (!valid) {
return MethodInfo();
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 7821420620..06d526f494 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -278,6 +278,7 @@ public:
void get_script_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
MethodInfo get_method_info(const StringName &p_method) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
@@ -346,6 +347,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void mono_object_disposed(GCHandleIntPtr p_gchandle_to_free);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 6c34d7c29d..c7be0464b6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -12,6 +12,7 @@ namespace Godot.Bridge
public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
public delegate* unmanaged<IntPtr, int> DelegateUtils_DelegateHash;
+ public delegate* unmanaged<IntPtr, godot_bool*, int> DelegateUtils_GetArgumentCount;
public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
@@ -55,6 +56,7 @@ namespace Godot.Bridge
DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
DelegateUtils_DelegateHash = &DelegateUtils.DelegateHash,
+ DelegateUtils_GetArgumentCount = &DelegateUtils.GetArgumentCount,
DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index c680142638..ef3c9c79d4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -46,6 +46,29 @@ namespace Godot
}
[UnmanagedCallersOnly]
+ internal static unsafe int GetArgumentCount(IntPtr delegateGCHandle, godot_bool* outIsValid)
+ {
+ try
+ {
+ var @delegate = (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target;
+ int? argCount = @delegate?.Method?.GetParameters().Length;
+ if (argCount is null)
+ {
+ *outIsValid = godot_bool.False;
+ return 0;
+ }
+ *outIsValid = godot_bool.True;
+ return argCount.Value;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outIsValid = godot_bool.False;
+ return 0;
+ }
+ }
+
+ [UnmanagedCallersOnly]
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline,
godot_variant** args, int argc, godot_variant* outRet)
{
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index c55c5d8111..7c48110199 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -85,6 +85,10 @@ ObjectID ManagedCallable::get_object() const {
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
}
+int ManagedCallable::get_argument_count(bool &r_is_valid) const {
+ return GDMonoCache::managed_callbacks.DelegateUtils_GetArgumentCount(delegate_handle, &r_is_valid);
+}
+
void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 290d49be14..388c321d5d 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -56,6 +56,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
_FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 145f4cee90..5292bcd1ea 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -53,6 +53,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateHash);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, GetArgumentCount);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback);
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 46e9ab10cb..7c24f28b3a 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -78,6 +78,7 @@ struct ManagedCallbacks {
using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *);
using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
using FuncDelegateUtils_DelegateHash = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr);
+ using FuncDelegateUtils_GetArgumentCount = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr, bool *);
using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
@@ -115,6 +116,7 @@ struct ManagedCallbacks {
FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals;
FuncDelegateUtils_DelegateHash DelegateUtils_DelegateHash;
+ FuncDelegateUtils_GetArgumentCount DelegateUtils_GetArgumentCount;
FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h
index 31211bb108..cb582a2caf 100644
--- a/modules/multiplayer/scene_replication_interface.h
+++ b/modules/multiplayer/scene_replication_interface.h
@@ -112,7 +112,7 @@ private:
Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
void _free_remotes(const PeerInfo &p_info);
- template <class T>
+ template <typename T>
static T *get_id_as(const ObjectID &p_id) {
return p_id.is_valid() ? Object::cast_to<T>(ObjectDB::get_instance(p_id)) : nullptr;
}
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index ee2aa33108..9d6b197ee1 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -11,8 +11,25 @@
<methods>
<method name="_get_composition_layer" qualifiers="virtual">
<return type="int" />
+ <param index="0" name="index" type="int" />
<description>
- Returns a pointer to a [code]XrCompositionLayerBaseHeader[/code] struct to provide a composition layer. This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+ Returns a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct to provide the given composition layer.
+ This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+ </description>
+ </method>
+ <method name="_get_composition_layer_count" qualifiers="virtual">
+ <return type="int" />
+ <description>
+ Returns the number of composition layers this extension wrapper provides via [method _get_composition_layer].
+ This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+ </description>
+ </method>
+ <method name="_get_composition_layer_order" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns an integer that will be used to sort the given composition layer provided via [method _get_composition_layer]. Lower numbers will move the layer to the front of the list, and higher numbers to the end. The default projection layer has an order of [code]0[/code], so layers provided by this method should probably be above or below (but not exactly) [code]0[/code].
+ This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
</description>
</method>
<method name="_get_requested_extensions" qualifiers="virtual">
diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
index 7a16da144e..b3c94d44f0 100644
--- a/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
@@ -56,8 +56,15 @@ bool OpenXRCompositionLayerDepthExtension::is_available() {
return available;
}
-XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer() {
- // Seems this is all done in our base layer... Just in case this changes...
+int OpenXRCompositionLayerDepthExtension::get_composition_layer_count() {
+ return 0;
+}
+XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer(int p_index) {
+ // Seems this is all done in our base layer... Just in case this changes...
return nullptr;
}
+
+int OpenXRCompositionLayerDepthExtension::get_composition_layer_order(int p_index) {
+ return 0;
+}
diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.h b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h
index 50bbef4db4..1fda8844af 100644
--- a/modules/openxr/extensions/openxr_composition_layer_depth_extension.h
+++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h
@@ -43,7 +43,9 @@ public:
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
- virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+ virtual int get_composition_layer_count() override;
+ virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
+ virtual int get_composition_layer_order(int p_index) override;
private:
static OpenXRCompositionLayerDepthExtension *singleton;
diff --git a/modules/openxr/extensions/openxr_composition_layer_provider.h b/modules/openxr/extensions/openxr_composition_layer_provider.h
index d77ae06174..44f90a0e0e 100644
--- a/modules/openxr/extensions/openxr_composition_layer_provider.h
+++ b/modules/openxr/extensions/openxr_composition_layer_provider.h
@@ -38,7 +38,9 @@
// Interface for OpenXR extensions that provide a composition layer.
class OpenXRCompositionLayerProvider {
public:
- virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0;
+ virtual int get_composition_layer_count() = 0;
+ virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) = 0;
+ virtual int get_composition_layer_order(int p_index) = 0;
virtual ~OpenXRCompositionLayerProvider() {}
};
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index a9b62819b7..60a934e3a8 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -39,7 +39,9 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer");
- GDVIRTUAL_BIND(_get_composition_layer);
+ GDVIRTUAL_BIND(_get_composition_layer_count);
+ GDVIRTUAL_BIND(_get_composition_layer, "index");
+ GDVIRTUAL_BIND(_get_composition_layer_order, "index");
GDVIRTUAL_BIND(_get_suggested_tracker_names);
GDVIRTUAL_BIND(_on_register_metadata);
GDVIRTUAL_BIND(_on_before_instance_created);
@@ -140,16 +142,28 @@ PackedStringArray OpenXRExtensionWrapperExtension::get_suggested_tracker_names()
return PackedStringArray();
}
-XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer() {
+int OpenXRExtensionWrapperExtension::get_composition_layer_count() {
+ int count = 0;
+ GDVIRTUAL_CALL(_get_composition_layer_count, count);
+ return count;
+}
+
+XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer(int p_index) {
uint64_t pointer;
- if (GDVIRTUAL_CALL(_get_composition_layer, pointer)) {
+ if (GDVIRTUAL_CALL(_get_composition_layer, p_index, pointer)) {
return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer);
}
return nullptr;
}
+int OpenXRExtensionWrapperExtension::get_composition_layer_order(int p_index) {
+ int order = 0;
+ GDVIRTUAL_CALL(_get_composition_layer_order, p_index, order);
+ return order;
+}
+
void OpenXRExtensionWrapperExtension::on_register_metadata() {
GDVIRTUAL_CALL(_on_register_metadata);
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
index edcbc0139b..d3b78bf617 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -59,7 +59,9 @@ public:
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override;
- virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+ virtual int get_composition_layer_count() override;
+ virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
+ virtual int get_composition_layer_order(int p_index) override;
//TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>);
@@ -67,7 +69,9 @@ public:
GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr<void>);
- GDVIRTUAL0R(uint64_t, _get_composition_layer);
+ GDVIRTUAL0R(int, _get_composition_layer_count);
+ GDVIRTUAL1R(uint64_t, _get_composition_layer, int);
+ GDVIRTUAL1R(int, _get_composition_layer_order, int);
virtual PackedStringArray get_suggested_tracker_names() override;
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index e978c012b5..8dd017c213 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -2080,18 +2080,29 @@ void OpenXRAPI::end_frame() {
projection_views[eye].pose = views[eye].pose;
}
- Vector<const XrCompositionLayerBaseHeader *> layers_list;
+ Vector<OrderedCompositionLayer> ordered_layers_list;
+ bool projection_layer_is_first = true;
// Add composition layers from providers
for (OpenXRCompositionLayerProvider *provider : composition_layer_providers) {
- XrCompositionLayerBaseHeader *layer = provider->get_composition_layer();
- if (layer) {
- layers_list.push_back(layer);
+ for (int i = 0; i < provider->get_composition_layer_count(); i++) {
+ OrderedCompositionLayer layer = {
+ provider->get_composition_layer(i),
+ provider->get_composition_layer_order(i),
+ };
+ if (layer.composition_layer) {
+ ordered_layers_list.push_back(layer);
+ if (layer.sort_order == 0) {
+ WARN_PRINT_ONCE_ED("Composition layer returned sort order 0, it may be overwritten by projection layer.");
+ } else if (layer.sort_order < 0) {
+ projection_layer_is_first = false;
+ }
+ }
}
}
XrCompositionLayerFlags layer_flags = XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
- if (layers_list.size() > 0 || environment_blend_mode != XR_ENVIRONMENT_BLEND_MODE_OPAQUE) {
+ if (!projection_layer_is_first || environment_blend_mode != XR_ENVIRONMENT_BLEND_MODE_OPAQUE) {
layer_flags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
}
@@ -2103,7 +2114,16 @@ void OpenXRAPI::end_frame() {
view_count, // viewCount
projection_views, // views
};
- layers_list.push_back((const XrCompositionLayerBaseHeader *)&projection_layer);
+ ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 });
+
+ // Sort our layers.
+ ordered_layers_list.sort_custom<OrderedCompositionLayer>();
+
+ // Now make a list we can pass on to OpenXR.
+ Vector<const XrCompositionLayerBaseHeader *> layers_list;
+ for (OrderedCompositionLayer &ordered_layer : ordered_layers_list) {
+ layers_list.push_back(ordered_layer.composition_layer);
+ }
XrFrameEndInfo frame_end_info = {
XR_TYPE_FRAME_END_INFO, // type
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index e1a04a0796..7ec622364b 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -287,6 +287,15 @@ private:
RID get_interaction_profile_rid(XrPath p_path);
XrPath get_interaction_profile_path(RID p_interaction_profile);
+ struct OrderedCompositionLayer {
+ const XrCompositionLayerBaseHeader *composition_layer;
+ int sort_order;
+
+ _FORCE_INLINE_ bool operator()(const OrderedCompositionLayer &a, const OrderedCompositionLayer &b) const {
+ return a.sort_order < b.sort_order || (a.sort_order == b.sort_order && uint64_t(a.composition_layer) < uint64_t(b.composition_layer));
+ }
+ };
+
// state changes
bool poll_events();
bool on_state_idle();
diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
index dd6c181eae..8ae63140f5 100644
--- a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
+++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
Ogg Vorbis is a lossy audio format, with better audio quality compared to [ResourceImporterMP3] at a given bitrate.
- In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using an MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
Ogg Vorbis requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
</description>
<tutorials>
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
index 25e1b1f15a..f98ee12ef9 100644
--- a/modules/websocket/packet_buffer.h
+++ b/modules/websocket/packet_buffer.h
@@ -33,7 +33,7 @@
#include "core/templates/ring_buffer.h"
-template <class T>
+template <typename T>
class PacketBuffer {
private:
typedef struct {