summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/EditorSettings.xml12
-rw-r--r--doc/classes/Joint2D.xml10
-rw-r--r--doc/classes/Joint3D.xml12
-rw-r--r--doc/classes/XRServer.xml5
-rw-r--r--editor/action_map_editor.cpp3
-rw-r--r--editor/debugger/editor_debugger_node.cpp6
-rw-r--r--editor/debugger/script_editor_debugger.cpp1
-rw-r--r--editor/editor_node.cpp12
-rw-r--r--editor/editor_node.h11
-rw-r--r--editor/editor_settings.cpp6
-rw-r--r--editor/filesystem_dock.cpp12
-rw-r--r--misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist2
-rw-r--r--modules/openxr/scene/openxr_composition_layer.cpp10
-rw-r--r--modules/openxr/scene/openxr_composition_layer.h2
-rw-r--r--modules/openxr/scene/openxr_composition_layer_cylinder.cpp10
-rw-r--r--modules/openxr/scene/openxr_composition_layer_cylinder.h2
-rw-r--r--modules/openxr/scene/openxr_composition_layer_equirect.cpp10
-rw-r--r--modules/openxr/scene/openxr_composition_layer_equirect.h2
-rw-r--r--modules/openxr/scene/openxr_composition_layer_quad.cpp10
-rw-r--r--modules/openxr/scene/openxr_composition_layer_quad.h2
-rw-r--r--platform/ios/export/export_plugin.cpp38
-rw-r--r--platform/ios/view_controller.mm12
-rw-r--r--scene/resources/material.cpp23
-rw-r--r--scene/resources/resource_format_text.cpp322
-rw-r--r--scene/resources/resource_format_text.h8
-rw-r--r--servers/xr_server.cpp4
-rw-r--r--tests/core/math/test_transform_2d.h110
27 files changed, 255 insertions, 402 deletions
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 3e3d2205f2..7ee239415f 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -925,14 +925,14 @@
<member name="run/auto_save/save_before_running" type="bool" setter="" getter="">
If [code]true[/code], saves all scenes and scripts automatically before running the project. Setting this to [code]false[/code] prevents the editor from saving if there are no changes which can speed up the project startup slightly, but it makes it possible to run a project that has unsaved changes. (Unsaved changes will not be visible in the running project.)
</member>
- <member name="run/output/always_clear_output_on_play" type="bool" setter="" getter="">
- If [code]true[/code], the editor will clear the Output panel when running the project.
+ <member name="run/bottom_panel/action_on_play" type="int" setter="" getter="">
+ The action to execute on the bottom panel when running the project.
</member>
- <member name="run/output/always_close_output_on_stop" type="bool" setter="" getter="">
- If [code]true[/code], the editor will collapse the Output panel when stopping the project.
+ <member name="run/bottom_panel/action_on_stop" type="int" setter="" getter="">
+ The action to execute on the bottom panel when stopping the project.
</member>
- <member name="run/output/always_open_output_on_play" type="bool" setter="" getter="">
- If [code]true[/code], the editor will expand the Output panel when running the project.
+ <member name="run/output/always_clear_output_on_play" type="bool" setter="" getter="">
+ If [code]true[/code], the editor will clear the Output panel when running the project.
</member>
<member name="run/output/font_size" type="int" setter="" getter="">
The size of the font in the [b]Output[/b] panel at the bottom of the editor. This setting does not impact the font size of the script editor (see [member interface/editor/code_font_size]).
diff --git a/doc/classes/Joint2D.xml b/doc/classes/Joint2D.xml
index af0a54815f..0099c76d08 100644
--- a/doc/classes/Joint2D.xml
+++ b/doc/classes/Joint2D.xml
@@ -4,7 +4,7 @@
Abstract base class for all 2D physics joints.
</brief_description>
<description>
- Abstract base class for all joints in 2D physics. 2D joints bind together two physics bodies and apply a constraint.
+ Abstract base class for all joints in 2D physics. 2D joints bind together two physics bodies ([member node_a] and [member node_b]) and apply a constraint.
</description>
<tutorials>
</tutorials>
@@ -12,7 +12,7 @@
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
- Returns the joint's [RID].
+ Returns the joint's internal [RID] from the [PhysicsServer2D].
</description>
</method>
</methods>
@@ -22,13 +22,13 @@
When set to [code]0[/code], the default value from [member ProjectSettings.physics/2d/solver/default_constraint_bias] is used.
</member>
<member name="disable_collision" type="bool" setter="set_exclude_nodes_from_collision" getter="get_exclude_nodes_from_collision" default="true">
- If [code]true[/code], [member node_a] and [member node_b] can not collide.
+ If [code]true[/code], the two bodies bound together do not collide with each other.
</member>
<member name="node_a" type="NodePath" setter="set_node_a" getter="get_node_a" default="NodePath(&quot;&quot;)">
- The first body attached to the joint. Must derive from [PhysicsBody2D].
+ Path to the first body (A) attached to the joint. The node must inherit [PhysicsBody2D].
</member>
<member name="node_b" type="NodePath" setter="set_node_b" getter="get_node_b" default="NodePath(&quot;&quot;)">
- The second body attached to the joint. Must derive from [PhysicsBody2D].
+ Path to the second body (B) attached to the joint. The node must inherit [PhysicsBody2D].
</member>
</members>
</class>
diff --git a/doc/classes/Joint3D.xml b/doc/classes/Joint3D.xml
index ea0dda881a..950129806a 100644
--- a/doc/classes/Joint3D.xml
+++ b/doc/classes/Joint3D.xml
@@ -4,7 +4,7 @@
Abstract base class for all 3D physics joints.
</brief_description>
<description>
- Abstract base class for all joints in 3D physics. 3D joints bind together two physics bodies and apply a constraint.
+ Abstract base class for all joints in 3D physics. 3D joints bind together two physics bodies ([member node_a] and [member node_b]) and apply a constraint. If only one body is defined, it is attached to a fixed [StaticBody3D] without collision shapes.
</description>
<tutorials>
<link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/2752</link>
@@ -13,19 +13,21 @@
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
- Returns the joint's [RID].
+ Returns the joint's internal [RID] from the [PhysicsServer3D].
</description>
</method>
</methods>
<members>
<member name="exclude_nodes_from_collision" type="bool" setter="set_exclude_nodes_from_collision" getter="get_exclude_nodes_from_collision" default="true">
- If [code]true[/code], the two bodies of the nodes are not able to collide with each other.
+ If [code]true[/code], the two bodies bound together do not collide with each other.
</member>
<member name="node_a" type="NodePath" setter="set_node_a" getter="get_node_a" default="NodePath(&quot;&quot;)">
- The node attached to the first side (A) of the joint.
+ Path to the first node (A) attached to the joint. The node must inherit [PhysicsBody3D].
+ If left empty and [member node_b] is set, the body is attached to a fixed [StaticBody3D] without collision shapes.
</member>
<member name="node_b" type="NodePath" setter="set_node_b" getter="get_node_b" default="NodePath(&quot;&quot;)">
- The node attached to the second side (B) of the joint.
+ Path to the second node (B) attached to the joint. The node must inherit [PhysicsBody3D].
+ If left empty and [member node_a] is set, the body is attached to a fixed [StaticBody3D] without collision shapes.
</member>
<member name="solver_priority" type="int" setter="set_solver_priority" getter="get_solver_priority" default="1">
The priority used to define which solver is executed first for multiple joints. The lower the value, the higher the priority.
diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml
index 4179ba821c..49b13986ca 100644
--- a/doc/classes/XRServer.xml
+++ b/doc/classes/XRServer.xml
@@ -135,6 +135,11 @@
Emitted when an interface is removed.
</description>
</signal>
+ <signal name="reference_frame_changed">
+ <description>
+ Emitted when the reference frame transform changes.
+ </description>
+ </signal>
<signal name="tracker_added">
<param index="0" name="tracker_name" type="StringName" />
<param index="1" name="type" type="int" />
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 3023c5907a..f70730d540 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -428,6 +428,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
// Update Tree...
TreeItem *action_item = action_tree->create_item(root);
+ ERR_FAIL_NULL(action_item);
action_item->set_meta("__action", action_info.action);
action_item->set_meta("__name", action_info.name);
@@ -604,7 +605,7 @@ ActionMapEditor::ActionMapEditor() {
action_tree->set_column_custom_minimum_width(1, 80 * EDSCALE);
action_tree->set_column_expand(2, false);
action_tree->set_column_custom_minimum_width(2, 50 * EDSCALE);
- action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited));
+ action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited), CONNECT_DEFERRED);
action_tree->connect("item_activated", callable_mp(this, &ActionMapEditor::_tree_item_activated));
action_tree->connect("button_clicked", callable_mp(this, &ActionMapEditor::_tree_button_pressed));
main_vbox->add_child(action_tree);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 2f7183b883..2a98f50a3a 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -268,11 +268,7 @@ Error EditorDebuggerNode::start(const String &p_uri) {
}
stop(true);
current_uri = p_uri;
- if (EDITOR_GET("run/output/always_open_output_on_play")) {
- EditorNode::get_bottom_panel()->make_item_visible(EditorNode::get_log());
- } else {
- EditorNode::get_bottom_panel()->make_item_visible(this);
- }
+
server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));
const Error err = server->start(p_uri);
if (err != OK) {
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 156e740509..37bb048b19 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1009,7 +1009,6 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
set_process(true);
camera_override = CameraOverride::OVERRIDE_NONE;
- tabs->set_current_tab(0);
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
_update_buttons_state();
emit_signal(SNAME("started"));
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 964061505f..0df4df36bc 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4543,17 +4543,19 @@ void EditorNode::_project_run_started() {
log->clear();
}
- if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
+ int action_on_play = EDITOR_GET("run/bottom_panel/action_on_play");
+ if (action_on_play == ACTION_ON_PLAY_OPEN_OUTPUT) {
bottom_panel->make_item_visible(log);
+ } else if (action_on_play == ACTION_ON_PLAY_OPEN_DEBUGGER) {
+ bottom_panel->make_item_visible(EditorDebuggerNode::get_singleton());
}
}
void EditorNode::_project_run_stopped() {
- if (!bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
- return;
+ int action_on_stop = EDITOR_GET("run/bottom_panel/action_on_stop");
+ if (action_on_stop == ACTION_ON_STOP_CLOSE_BUTTOM_PANEL) {
+ bottom_panel->hide_bottom_panel();
}
-
- bottom_panel->make_item_visible(log, false);
}
void EditorNode::notify_all_debug_sessions_exited() {
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 9643bc2229..5d7bd5b4f8 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -140,6 +140,17 @@ public:
SCENE_NAME_CASING_KEBAB_CASE,
};
+ enum ActionOnPlay {
+ ACTION_ON_PLAY_DO_NOTHING,
+ ACTION_ON_PLAY_OPEN_OUTPUT,
+ ACTION_ON_PLAY_OPEN_DEBUGGER,
+ };
+
+ enum ActionOnStop {
+ ACTION_ON_STOP_DO_NOTHING,
+ ACTION_ON_STOP_CLOSE_BUTTOM_PANEL,
+ };
+
struct ExecuteThreadArgs {
String path;
List<String> args;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index d7bc3502ce..737bec352d 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -818,11 +818,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Auto save
_initial_set("run/auto_save/save_before_running", true);
+ // Bottom panel
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_play", EditorNode::ACTION_ON_PLAY_OPEN_OUTPUT, "Do Nothing,Open Output,Open Debugger")
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_stop", EditorNode::ACTION_ON_STOP_DO_NOTHING, "Do Nothing,Close Bottom Panel")
+
// Output
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/font_size", 13, "8,48,1")
_initial_set("run/output/always_clear_output_on_play", true);
- _initial_set("run/output/always_open_output_on_play", true);
- _initial_set("run/output/always_close_output_on_stop", false);
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/max_lines", 10000, "100,100000,1")
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 2e88540fc4..c07667ac12 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3310,15 +3310,15 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
// Opening the system file manager is not supported on the Android and web editors.
const bool is_directory = fpath.ends_with("/");
- p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
- p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager"));
+ p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL);
+ p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal"));
if (!is_directory) {
p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL);
}
- p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL);
- p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal"));
+ p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
+ p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager"));
#endif
current_path = fpath;
@@ -3362,8 +3362,8 @@ void FileSystemDock::_tree_empty_click(const Vector2 &p_pos, MouseButton p_butto
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
// Opening the system file manager is not supported on the Android and web editors.
tree_popup->add_separator();
- tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL);
+ tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
#endif
tree_popup->set_position(tree->get_screen_position() + p_pos);
@@ -3425,8 +3425,8 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton
file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("New Resource..."), FILE_NEW_RESOURCE);
file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("New TextFile..."), FILE_NEW_TEXTFILE);
file_list_popup->add_separator();
- file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL);
+ file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
file_list_popup->set_position(files->get_screen_position() + p_pos);
file_list_popup->reset_size();
diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
index ee5f1d35ae..3d2ae6b52b 100644
--- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
+++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
@@ -54,7 +54,7 @@
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
- $interface_orientations
+ $ipad_interface_orientations
</array>
$additional_plist_content
$plist_launch_screen_name
diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp
index b02f3082ab..f69a907be9 100644
--- a/modules/openxr/scene/openxr_composition_layer.cpp
+++ b/modules/openxr/scene/openxr_composition_layer.cpp
@@ -151,6 +151,16 @@ void OpenXRCompositionLayer::update_fallback_mesh() {
should_update_fallback_mesh = true;
}
+XrPosef OpenXRCompositionLayer::get_openxr_pose() {
+ Transform3D reference_frame = XRServer::get_singleton()->get_reference_frame();
+ Transform3D transform = reference_frame.inverse() * get_transform();
+ Quaternion quat(transform.basis.orthonormalized());
+ return {
+ { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w },
+ { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z }
+ };
+}
+
bool OpenXRCompositionLayer::is_viewport_in_use(SubViewport *p_viewport) {
for (const OpenXRCompositionLayer *other_composition_layer : composition_layer_nodes) {
if (other_composition_layer != this && other_composition_layer->is_inside_tree() && other_composition_layer->get_layer_viewport() == p_viewport) {
diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h
index 6792364295..55cae27d23 100644
--- a/modules/openxr/scene/openxr_composition_layer.h
+++ b/modules/openxr/scene/openxr_composition_layer.h
@@ -77,6 +77,8 @@ protected:
void update_fallback_mesh();
+ XrPosef get_openxr_pose();
+
static Vector<OpenXRCompositionLayer *> composition_layer_nodes;
bool is_viewport_in_use(SubViewport *p_viewport);
diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp
index 728ba71006..6c8d2fc885 100644
--- a/modules/openxr/scene/openxr_composition_layer_cylinder.cpp
+++ b/modules/openxr/scene/openxr_composition_layer_cylinder.cpp
@@ -52,6 +52,7 @@ OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() {
aspect_ratio, // aspectRatio
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
+ XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerCylinder::update_transform));
}
OpenXRCompositionLayerCylinder::~OpenXRCompositionLayerCylinder() {
@@ -131,14 +132,15 @@ Ref<Mesh> OpenXRCompositionLayerCylinder::_create_fallback_mesh() {
void OpenXRCompositionLayerCylinder::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- Transform3D transform = get_transform();
- Quaternion quat(transform.basis.orthonormalized());
- composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
- composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
+ update_transform();
} break;
}
}
+void OpenXRCompositionLayerCylinder::update_transform() {
+ composition_layer.pose = get_openxr_pose();
+}
+
void OpenXRCompositionLayerCylinder::set_radius(float p_radius) {
ERR_FAIL_COND(p_radius <= 0);
radius = p_radius;
diff --git a/modules/openxr/scene/openxr_composition_layer_cylinder.h b/modules/openxr/scene/openxr_composition_layer_cylinder.h
index bb1d242267..9bd5a42d36 100644
--- a/modules/openxr/scene/openxr_composition_layer_cylinder.h
+++ b/modules/openxr/scene/openxr_composition_layer_cylinder.h
@@ -50,6 +50,8 @@ protected:
void _notification(int p_what);
+ void update_transform();
+
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.cpp b/modules/openxr/scene/openxr_composition_layer_equirect.cpp
index 14cfea8da6..b6f5d27ffe 100644
--- a/modules/openxr/scene/openxr_composition_layer_equirect.cpp
+++ b/modules/openxr/scene/openxr_composition_layer_equirect.cpp
@@ -53,6 +53,7 @@ OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() {
-lower_vertical_angle, // lowerVerticalAngle
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
+ XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerEquirect::update_transform));
}
OpenXRCompositionLayerEquirect::~OpenXRCompositionLayerEquirect() {
@@ -139,14 +140,15 @@ Ref<Mesh> OpenXRCompositionLayerEquirect::_create_fallback_mesh() {
void OpenXRCompositionLayerEquirect::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- Transform3D transform = get_transform();
- Quaternion quat(transform.basis.orthonormalized());
- composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
- composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
+ update_transform();
} break;
}
}
+void OpenXRCompositionLayerEquirect::update_transform() {
+ composition_layer.pose = get_openxr_pose();
+}
+
void OpenXRCompositionLayerEquirect::set_radius(float p_radius) {
ERR_FAIL_COND(p_radius <= 0);
radius = p_radius;
diff --git a/modules/openxr/scene/openxr_composition_layer_equirect.h b/modules/openxr/scene/openxr_composition_layer_equirect.h
index 66f8b0a91c..af6cd92cbe 100644
--- a/modules/openxr/scene/openxr_composition_layer_equirect.h
+++ b/modules/openxr/scene/openxr_composition_layer_equirect.h
@@ -51,6 +51,8 @@ protected:
void _notification(int p_what);
+ void update_transform();
+
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
diff --git a/modules/openxr/scene/openxr_composition_layer_quad.cpp b/modules/openxr/scene/openxr_composition_layer_quad.cpp
index 8c5b8ec26b..21919496d6 100644
--- a/modules/openxr/scene/openxr_composition_layer_quad.cpp
+++ b/modules/openxr/scene/openxr_composition_layer_quad.cpp
@@ -50,6 +50,7 @@ OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() {
{ (float)quad_size.x, (float)quad_size.y }, // size
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
+ XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerQuad::update_transform));
}
OpenXRCompositionLayerQuad::~OpenXRCompositionLayerQuad() {
@@ -72,14 +73,15 @@ Ref<Mesh> OpenXRCompositionLayerQuad::_create_fallback_mesh() {
void OpenXRCompositionLayerQuad::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- Transform3D transform = get_transform();
- Quaternion quat(transform.basis.orthonormalized());
- composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
- composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
+ update_transform();
} break;
}
}
+void OpenXRCompositionLayerQuad::update_transform() {
+ composition_layer.pose = get_openxr_pose();
+}
+
void OpenXRCompositionLayerQuad::set_quad_size(const Size2 &p_size) {
quad_size = p_size;
composition_layer.size = { (float)quad_size.x, (float)quad_size.y };
diff --git a/modules/openxr/scene/openxr_composition_layer_quad.h b/modules/openxr/scene/openxr_composition_layer_quad.h
index 21bb9b2d85..0c3172dbb2 100644
--- a/modules/openxr/scene/openxr_composition_layer_quad.h
+++ b/modules/openxr/scene/openxr_composition_layer_quad.h
@@ -47,6 +47,8 @@ protected:
void _notification(int p_what);
+ void update_transform();
+
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 6a452f08fa..769d97694a 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -541,6 +541,44 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
strnew += lines[i].replace("$interface_orientations", orientations);
+ } else if (lines[i].contains("$ipad_interface_orientations")) {
+ String orientations;
+ const DisplayServer::ScreenOrientation screen_orientation =
+ DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")));
+
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ // Allow both landscape orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ // Allow both portrait orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR:
+ // Allow all screen orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ }
+
+ strnew += lines[i].replace("$ipad_interface_orientations", orientations);
} else if (lines[i].contains("$camera_usage_description")) {
String description = p_preset->get("privacy/camera_usage_description");
strnew += lines[i].replace("$camera_usage_description", description) + "\n";
diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm
index 6f6c04c2c8..787e767109 100644
--- a/platform/ios/view_controller.mm
+++ b/platform/ios/view_controller.mm
@@ -258,7 +258,11 @@
case DisplayServer::SCREEN_PORTRAIT:
return UIInterfaceOrientationMaskPortrait;
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
- return UIInterfaceOrientationMaskLandscapeRight;
+ if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ return UIInterfaceOrientationMaskLandscapeLeft;
+ } else {
+ return UIInterfaceOrientationMaskLandscapeRight;
+ }
case DisplayServer::SCREEN_REVERSE_PORTRAIT:
return UIInterfaceOrientationMaskPortraitUpsideDown;
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
@@ -268,7 +272,11 @@
case DisplayServer::SCREEN_SENSOR:
return UIInterfaceOrientationMaskAll;
case DisplayServer::SCREEN_LANDSCAPE:
- return UIInterfaceOrientationMaskLandscapeLeft;
+ if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ return UIInterfaceOrientationMaskLandscapeRight;
+ } else {
+ return UIInterfaceOrientationMaskLandscapeLeft;
+ }
}
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 27da825bfe..8e49a8b56f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -822,7 +822,18 @@ uniform float distance_fade_max : hint_range(0.0, 4096.0, 0.01);
)";
}
- if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && flags[FLAG_UV1_USE_TRIPLANAR]) {
+ String msg = "MSDF is not supported on triplanar materials. Ignoring MSDF in favor of triplanar mapping.";
+ if (textures[TEXTURE_ALBEDO].is_valid()) {
+ WARN_PRINT(vformat("%s (albedo %s): " + msg, get_path(), textures[TEXTURE_ALBEDO]->get_path()));
+ } else if (!get_path().is_empty()) {
+ WARN_PRINT(vformat("%s: " + msg, get_path()));
+ } else {
+ WARN_PRINT(msg);
+ }
+ }
+
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) {
code += R"(
uniform float msdf_pixel_range : hint_range(1.0, 100.0, 1.0);
uniform float msdf_outline_size : hint_range(0.0, 250.0, 1.0);
@@ -1271,7 +1282,7 @@ void vertex() {)";
code += "}\n";
- if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) {
code += R"(
float msdf_median(float r, float g, float b, float a) {
return min(max(min(r, g), min(max(r, g), b)), a);
@@ -1414,7 +1425,7 @@ void fragment() {)";
}
}
- if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF] && !flags[FLAG_UV1_USE_TRIPLANAR]) {
code += R"(
{
// Albedo Texture MSDF: Enabled
@@ -1427,11 +1438,7 @@ void fragment() {)";
if (flags[FLAG_USE_POINT_SIZE]) {
code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n";
} else {
- if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += " vec2 dest_size = vec2(1.0) / fwidth(uv1_triplanar_pos);\n";
- } else {
- code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n";
- }
+ code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n";
}
code += R"(
float px_size = max(0.5 * dot(msdf_size, dest_size), 1.0);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 2e27ac9198..7c13e623c2 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -33,9 +33,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/missing_resource.h"
-#include "core/io/resource_format_binary.h"
#include "core/object/script_language.h"
-#include "core/version.h"
// Version 2: Changed names for Basis, AABB, Vectors, etc.
// Version 3: New string ID for ext/subresources, breaks forward compat.
@@ -44,11 +42,6 @@
// For compat, save as version 3 if not using PackedVector4Array or no big PackedByteArray.
#define FORMAT_VERSION_COMPAT 3
-#define BINARY_FORMAT_VERSION 4
-
-#include "core/io/dir_access.h"
-#include "core/version.h"
-
#define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data());
///
@@ -1127,298 +1120,6 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) {
rp.userdata = this;
}
-static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len = false) {
- CharString utf8 = p_string.utf8();
- if (p_bit_on_len) {
- p_f->store_32((utf8.length() + 1) | 0x80000000);
- } else {
- p_f->store_32(utf8.length() + 1);
- }
- p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
-}
-
-Error ResourceLoaderText::save_as_binary(const String &p_path) {
- if (error) {
- return error;
- }
-
- Ref<FileAccess> wf = FileAccess::open(p_path, FileAccess::WRITE);
- if (wf.is_null()) {
- return ERR_CANT_OPEN;
- }
-
- //save header compressed
- static const uint8_t header[4] = { 'R', 'S', 'R', 'C' };
- wf->store_buffer(header, 4);
-
- wf->store_32(0); //endianness, little endian
- wf->store_32(0); //64 bits file, false for now
- wf->store_32(VERSION_MAJOR);
- wf->store_32(VERSION_MINOR);
- static const int save_format_version = BINARY_FORMAT_VERSION;
- wf->store_32(save_format_version);
-
- bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type);
- wf->store_64(0); //offset to import metadata, this is no longer used
-
- wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
-
- wf->store_64(res_uid);
-
- for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
- wf->store_32(0); // reserved
- }
-
- wf->store_32(0); //string table size, will not be in use
- uint64_t ext_res_count_pos = wf->get_position();
-
- wf->store_32(0); //zero ext resources, still parsing them
-
- //go with external resources
-
- DummyReadData dummy_read;
- VariantParser::ResourceParser rp_new;
- rp_new.ext_func = _parse_ext_resource_dummys;
- rp_new.sub_func = _parse_sub_resource_dummys;
- rp_new.userdata = &dummy_read;
-
- while (next_tag.name == "ext_resource") {
- if (!next_tag.fields.has("path")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'path' in external resource tag";
- _printerr();
- return error;
- }
-
- if (!next_tag.fields.has("type")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'type' in external resource tag";
- _printerr();
- return error;
- }
-
- if (!next_tag.fields.has("id")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'id' in external resource tag";
- _printerr();
- return error;
- }
-
- String path = next_tag.fields["path"];
- String type = next_tag.fields["type"];
- String id = next_tag.fields["id"];
- ResourceUID::ID uid = ResourceUID::INVALID_ID;
- if (next_tag.fields.has("uid")) {
- String uidt = next_tag.fields["uid"];
- uid = ResourceUID::get_singleton()->text_to_id(uidt);
- }
-
- bs_save_unicode_string(wf, type);
- bs_save_unicode_string(wf, path);
- wf->store_64(uid);
-
- int lindex = dummy_read.external_resources.size();
- Ref<DummyResource> dr;
- dr.instantiate();
- dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external
- dummy_read.external_resources[dr] = lindex;
- dummy_read.rev_external_resources[id] = dr;
-
- error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp_new);
-
- if (error) {
- _printerr();
- return error;
- }
- }
-
- // save external resource table
- wf->seek(ext_res_count_pos);
- wf->store_32(dummy_read.external_resources.size());
- wf->seek_end();
-
- //now, save resources to a separate file, for now
-
- uint64_t sub_res_count_pos = wf->get_position();
- wf->store_32(0); //zero sub resources, still parsing them
-
- String temp_file = p_path + ".temp";
- Vector<uint64_t> local_offsets;
- Vector<uint64_t> local_pointers_pos;
- {
- Ref<FileAccess> wf2 = FileAccess::open(temp_file, FileAccess::WRITE);
- if (wf2.is_null()) {
- return ERR_CANT_OPEN;
- }
-
- while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
- String type;
- String id;
- bool main_res;
-
- if (next_tag.name == "sub_resource") {
- if (!next_tag.fields.has("type")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'type' in external resource tag";
- _printerr();
- return error;
- }
-
- if (!next_tag.fields.has("id")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'id' in external resource tag";
- _printerr();
- return error;
- }
-
- type = next_tag.fields["type"];
- id = next_tag.fields["id"];
- main_res = false;
-
- if (!dummy_read.resource_map.has(id)) {
- Ref<DummyResource> dr;
- dr.instantiate();
- dr->set_scene_unique_id(id);
- dummy_read.resource_map[id] = dr;
- uint32_t im_size = dummy_read.resource_index_map.size();
- dummy_read.resource_index_map.insert(dr, im_size);
- }
-
- } else {
- type = res_type;
- String uid_text = ResourceUID::get_singleton()->id_to_text(res_uid);
- id = type + "_" + uid_text.replace("uid://", "").replace("<invalid>", "0");
- main_res = true;
- }
-
- local_offsets.push_back(wf2->get_position());
-
- bs_save_unicode_string(wf, "local://" + id);
- local_pointers_pos.push_back(wf->get_position());
- wf->store_64(0); //temp local offset
-
- bs_save_unicode_string(wf2, type);
- uint64_t propcount_ofs = wf2->get_position();
- wf2->store_32(0);
-
- int prop_count = 0;
-
- while (true) {
- String assign;
- Variant value;
-
- error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp_new);
-
- if (error) {
- if (main_res && error == ERR_FILE_EOF) {
- next_tag.name = ""; //exit
- break;
- }
-
- _printerr();
- return error;
- }
-
- if (!assign.is_empty()) {
- HashMap<StringName, int> empty_string_map; //unused
- bs_save_unicode_string(wf2, assign, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
- prop_count++;
-
- } else if (!next_tag.name.is_empty()) {
- error = OK;
- break;
- } else {
- error = ERR_FILE_CORRUPT;
- error_text = "Premature end of file while parsing [sub_resource]";
- _printerr();
- return error;
- }
- }
-
- wf2->seek(propcount_ofs);
- wf2->store_32(prop_count);
- wf2->seek_end();
- }
-
- if (next_tag.name == "node") {
- // This is a node, must save one more!
-
- if (!is_scene) {
- error_text += "found the 'node' tag on a resource file!";
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
- }
-
- Ref<PackedScene> packed_scene = _parse_node_tag(rp_new);
-
- if (!packed_scene.is_valid()) {
- return error;
- }
-
- error = OK;
- //get it here
- List<PropertyInfo> props;
- packed_scene->get_property_list(&props);
-
- String id = "PackedScene_" + ResourceUID::get_singleton()->id_to_text(res_uid).replace("uid://", "").replace("<invalid>", "0");
- bs_save_unicode_string(wf, "local://" + id);
- local_pointers_pos.push_back(wf->get_position());
- wf->store_64(0); //temp local offset
-
- local_offsets.push_back(wf2->get_position());
- bs_save_unicode_string(wf2, "PackedScene");
- uint64_t propcount_ofs = wf2->get_position();
- wf2->store_32(0);
-
- int prop_count = 0;
-
- for (const PropertyInfo &E : props) {
- if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
- continue;
- }
-
- String name = E.name;
- Variant value = packed_scene->get(name);
-
- HashMap<StringName, int> empty_string_map; //unused
- bs_save_unicode_string(wf2, name, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
- prop_count++;
- }
-
- wf2->seek(propcount_ofs);
- wf2->store_32(prop_count);
- wf2->seek_end();
- }
- }
-
- uint64_t offset_from = wf->get_position();
- wf->seek(sub_res_count_pos); //plus one because the saved one
- wf->store_32(local_offsets.size());
-
- for (int i = 0; i < local_offsets.size(); i++) {
- wf->seek(local_pointers_pos[i]);
- wf->store_64(local_offsets[i] + offset_from);
- }
-
- wf->seek_end();
-
- Vector<uint8_t> data = FileAccess::get_file_as_bytes(temp_file);
- wf->store_buffer(data.ptr(), data.size());
- {
- Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir());
- ERR_FAIL_COND_V(dar.is_null(), FAILED);
-
- dar->remove(temp_file);
- }
-
- wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end
-
- return OK;
-}
-
Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) {
if (error) {
return error;
@@ -1835,29 +1536,6 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const
ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = nullptr;
-Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) {
- Error err;
- Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err);
-
- ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'.");
-
- ResourceLoaderText loader;
- const String &path = p_src_path;
- loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
- loader.res_path = loader.local_path;
- loader.open(f);
- return loader.save_as_binary(p_dst_path);
-}
-
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
-/*****************************************************************************************************/
/*****************************************************************************************************/
String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Resource> &p_resource) {
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 41363fd975..b5542f77ba 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -87,11 +87,6 @@ class ResourceLoaderText {
Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
- // for converter
- class DummyResource : public Resource {
- public:
- };
-
struct DummyReadData {
bool no_placeholders = false;
HashMap<Ref<Resource>, int> external_resources;
@@ -133,7 +128,6 @@ public:
Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map);
Error get_classes_used(HashSet<StringName> *r_classes);
- Error save_as_binary(const String &p_path);
ResourceLoaderText();
};
@@ -152,8 +146,6 @@ public:
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override;
- static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path);
-
ResourceFormatLoaderText() { singleton = this; }
};
diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp
index 2cfe98ea1e..a4e68afee0 100644
--- a/servers/xr_server.cpp
+++ b/servers/xr_server.cpp
@@ -98,6 +98,8 @@ void XRServer::_bind_methods() {
BIND_ENUM_CONSTANT(RESET_BUT_KEEP_TILT);
BIND_ENUM_CONSTANT(DONT_RESET_ROTATION);
+ ADD_SIGNAL(MethodInfo("reference_frame_changed"));
+
ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name")));
ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name")));
@@ -213,11 +215,13 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
reference_frame = new_reference_frame.inverse();
set_render_reference_frame(reference_frame);
+ emit_signal(SNAME("reference_frame_changed"));
}
void XRServer::clear_reference_frame() {
reference_frame = Transform3D();
set_render_reference_frame(reference_frame);
+ emit_signal(SNAME("reference_frame_changed"));
}
void XRServer::_set_render_reference_frame(const Transform3D &p_reference_frame) {
diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h
index 36d27ce7a9..6d3c80e5ca 100644
--- a/tests/core/math/test_transform_2d.h
+++ b/tests/core/math/test_transform_2d.h
@@ -45,48 +45,132 @@ Transform2D identity() {
return Transform2D();
}
+TEST_CASE("[Transform2D] Default constructor") {
+ Transform2D default_constructor = Transform2D();
+ CHECK(default_constructor == Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(0, 0)));
+}
+
+TEST_CASE("[Transform2D] Copy constructor") {
+ Transform2D T = create_dummy_transform();
+ Transform2D copy_constructor = Transform2D(T);
+ CHECK(T == copy_constructor);
+}
+
+TEST_CASE("[Transform2D] Constructor from angle and position") {
+ constexpr float ROTATION = Math_PI / 4;
+ const Vector2 TRANSLATION = Vector2(20, -20);
+
+ const Transform2D test = Transform2D(ROTATION, TRANSLATION);
+ const Transform2D expected = Transform2D().rotated(ROTATION).translated(TRANSLATION);
+ CHECK(test == expected);
+}
+
+TEST_CASE("[Transform2D] Constructor from angle, scale, skew and position") {
+ constexpr float ROTATION = Math_PI / 2;
+ const Vector2 SCALE = Vector2(2, 0.5);
+ constexpr float SKEW = Math_PI / 4;
+ const Vector2 TRANSLATION = Vector2(30, 0);
+
+ const Transform2D test = Transform2D(ROTATION, SCALE, SKEW, TRANSLATION);
+ Transform2D expected = Transform2D().scaled(SCALE).rotated(ROTATION).translated(TRANSLATION);
+ expected.set_skew(SKEW);
+
+ CHECK(test.is_equal_approx(expected));
+}
+
+TEST_CASE("[Transform2D] Constructor from raw values") {
+ const Transform2D test = Transform2D(1, 2, 3, 4, 5, 6);
+ const Transform2D expected = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+ CHECK(test == expected);
+}
+
+TEST_CASE("[Transform2D] xform") {
+ const Vector2 v = Vector2(2, 3);
+ const Transform2D T = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+ const Vector2 expected = Vector2(1 * 2 + 3 * 3 + 5 * 1, 2 * 2 + 4 * 3 + 6 * 1);
+ CHECK(T.xform(v) == expected);
+}
+
+TEST_CASE("[Transform2D] Basis xform") {
+ const Vector2 v = Vector2(2, 2);
+ const Transform2D T1 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(0, 0));
+
+ // Both versions should be the same when the origin is (0,0).
+ CHECK(T1.basis_xform(v) == T1.xform(v));
+
+ const Transform2D T2 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+
+ // Each version should be different when the origin is not (0,0).
+ CHECK_FALSE(T2.basis_xform(v) == T2.xform(v));
+}
+
+TEST_CASE("[Transform2D] Affine inverse") {
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D affine_inverted = orig.affine_inverse();
+ const Transform2D affine_inverted_again = affine_inverted.affine_inverse();
+ CHECK(affine_inverted_again == orig);
+}
+
+TEST_CASE("[Transform2D] Orthonormalized") {
+ const Transform2D T = create_dummy_transform();
+ const Transform2D orthonormalized_T = T.orthonormalized();
+
+ // Check each basis has length 1.
+ CHECK(Math::is_equal_approx(orthonormalized_T[0].length_squared(), 1));
+ CHECK(Math::is_equal_approx(orthonormalized_T[1].length_squared(), 1));
+
+ const Vector2 vx = Vector2(orthonormalized_T[0].x, orthonormalized_T[1].x);
+ const Vector2 vy = Vector2(orthonormalized_T[0].y, orthonormalized_T[1].y);
+
+ // Check the basis are orthogonal.
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vx), 1));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vy), 0));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vx), 0));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vy), 1));
+}
+
TEST_CASE("[Transform2D] translation") {
- Vector2 offset = Vector2(1, 2);
+ const Vector2 offset = Vector2(1, 2);
// Both versions should give the same result applied to identity.
CHECK(identity().translated(offset) == identity().translated_local(offset));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D T = identity().translated(offset);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D T = identity().translated(offset);
CHECK(orig.translated(offset) == T * orig);
CHECK(orig.translated_local(offset) == orig * T);
}
TEST_CASE("[Transform2D] scaling") {
- Vector2 scaling = Vector2(1, 2);
+ const Vector2 scaling = Vector2(1, 2);
// Both versions should give the same result applied to identity.
CHECK(identity().scaled(scaling) == identity().scaled_local(scaling));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D S = identity().scaled(scaling);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D S = identity().scaled(scaling);
CHECK(orig.scaled(scaling) == S * orig);
CHECK(orig.scaled_local(scaling) == orig * S);
}
TEST_CASE("[Transform2D] rotation") {
- real_t phi = 1.0;
+ constexpr real_t phi = 1.0;
// Both versions should give the same result applied to identity.
CHECK(identity().rotated(phi) == identity().rotated_local(phi));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D R = identity().rotated(phi);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D R = identity().rotated(phi);
CHECK(orig.rotated(phi) == R * orig);
CHECK(orig.rotated_local(phi) == orig * R);
}
TEST_CASE("[Transform2D] Interpolation") {
- Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8));
- Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4));
+ const Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8));
+ const Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4));
Transform2D interpolated = Transform2D().interpolate_with(rotate_scale_skew_pos, 0.5);
CHECK(interpolated.get_origin().is_equal_approx(rotate_scale_skew_pos_halfway.get_origin()));
CHECK(interpolated.get_rotation() == doctest::Approx(rotate_scale_skew_pos_halfway.get_rotation()));
@@ -98,8 +182,8 @@ TEST_CASE("[Transform2D] Interpolation") {
}
TEST_CASE("[Transform2D] Finite number checks") {
- const Vector2 x(0, 1);
- const Vector2 infinite(NAN, NAN);
+ const Vector2 x = Vector2(0, 1);
+ const Vector2 infinite = Vector2(NAN, NAN);
CHECK_MESSAGE(
Transform2D(x, x, x).is_finite(),