summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/windows_builds.yml2
-rw-r--r--.gitignore1
-rw-r--r--SConstruct3
-rw-r--r--core/object/object.cpp1
-rw-r--r--doc/classes/Object.xml3
-rw-r--r--doc/classes/XRInterface.xml12
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp37
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/scene_tree_dock.cpp6
-rw-r--r--main/main.cpp321
-rw-r--r--main/main.h11
-rw-r--r--methods.py608
-rw-r--r--misc/msvs/props.template21
-rw-r--r--misc/msvs/sln.template20
-rw-r--r--misc/msvs/vcxproj.filters.template30
-rw-r--r--misc/msvs/vcxproj.template42
-rwxr-xr-xmisc/scripts/black_format.sh2
-rwxr-xr-xmisc/scripts/clang_format.sh2
-rwxr-xr-xmisc/scripts/clang_tidy.sh2
-rwxr-xr-xmisc/scripts/dotnet_format.sh2
-rwxr-xr-xmisc/scripts/file_format.sh2
-rwxr-xr-xmisc/scripts/header_guards.sh2
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp2
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp10
-rw-r--r--modules/openxr/doc_classes/OpenXRAPIExtension.xml38
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml6
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp11
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h4
-rw-r--r--modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp246
-rw-r--r--modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h231
-rw-r--r--modules/openxr/openxr_api.cpp22
-rw-r--r--modules/openxr/openxr_api.h13
-rw-r--r--modules/openxr/openxr_api_extension.cpp32
-rw-r--r--modules/openxr/openxr_api_extension.h16
-rw-r--r--modules/openxr/openxr_interface.cpp17
-rw-r--r--modules/openxr/openxr_interface.h2
-rw-r--r--modules/openxr/register_types.cpp2
-rw-r--r--platform/windows/SCsub8
-rw-r--r--platform/windows/msvs.py20
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp15
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp17
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h2
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp8
-rw-r--r--thirdparty/thorvg/src/renderer/tvgCanvas.h39
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.cpp38
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.h21
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPicture.cpp14
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPicture.h21
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.h22
-rw-r--r--thirdparty/thorvg/src/renderer/tvgScene.h34
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.h26
-rw-r--r--thirdparty/thorvg/src/renderer/tvgText.h20
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh2
55 files changed, 1145 insertions, 950 deletions
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 2794c83e22..18ed92b57f 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -28,7 +28,7 @@ jobs:
target: editor
tests: true
# Skip debug symbols, they're way too big with MSVC.
- sconsflags: debug_symbols=no vsproj=yes windows_subsystem=console
+ sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console
bin: "./bin/godot.windows.editor.x86_64.exe"
- name: Template (target=template_release)
diff --git a/.gitignore b/.gitignore
index 51b6f66dca..f43b81f286 100644
--- a/.gitignore
+++ b/.gitignore
@@ -371,3 +371,4 @@ $RECYCLE.BIN/
*.msm
*.msp
*.lnk
+*.generated.props
diff --git a/SConstruct b/SConstruct
index 6a4dea2c09..f0f53ddc65 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1000,9 +1000,6 @@ if selected_platform in platform_list:
# Microsoft Visual Studio Project Generation
if env["vsproj"]:
- if os.name != "nt":
- print("Error: The `vsproj` option is only usable on Windows with Visual Studio.")
- Exit(255)
env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]]
methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"])
methods.generate_cpp_hint_file("cpp.hint")
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 3901c4835d..cc33d0ab8a 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1683,6 +1683,7 @@ void Object::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE);
BIND_CONSTANT(NOTIFICATION_PREDELETE);
+ BIND_CONSTANT(NOTIFICATION_EXTENSION_RELOADED);
BIND_ENUM_CONSTANT(CONNECT_DEFERRED);
BIND_ENUM_CONSTANT(CONNECT_PERSIST);
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 5fa43f868e..f66709cc5d 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -1056,6 +1056,9 @@
<constant name="NOTIFICATION_PREDELETE" value="1">
Notification received when the object is about to be deleted. Can act as the deconstructor of some programming languages.
</constant>
+ <constant name="NOTIFICATION_EXTENSION_RELOADED" value="2">
+ Notification received when the object finishes hot reloading. This notification is only sent for extensions classes and derived.
+ </constant>
<constant name="CONNECT_DEFERRED" value="1" enum="ConnectFlags">
Deferred connections trigger their [Callable]s on idle time (at the end of the frame), rather than instantly.
</constant>
diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml
index 00ce6e2a8b..6fb43d77d9 100644
--- a/doc/classes/XRInterface.xml
+++ b/doc/classes/XRInterface.xml
@@ -102,16 +102,18 @@
Is [code]true[/code] if this interface has been initialized.
</description>
</method>
- <method name="is_passthrough_enabled">
+ <method name="is_passthrough_enabled" is_deprecated="true">
<return type="bool" />
<description>
Is [code]true[/code] if passthrough is enabled.
+ [i]Deprecated.[/i] Check if [member environment_blend_mode] is [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND], instead.
</description>
</method>
- <method name="is_passthrough_supported">
+ <method name="is_passthrough_supported" is_deprecated="true">
<return type="bool" />
<description>
Is [code]true[/code] if this interface supports passthrough.
+ [i]Deprecated.[/i] Check that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is supported using [method get_supported_environment_blend_modes], instead.
</description>
</method>
<method name="set_environment_blend_mode">
@@ -144,17 +146,19 @@
[b]Note:[/b] Changing this after the interface has already been initialized can be jarring for the player, so it's recommended to recenter on the HMD with [method XRServer.center_on_hmd] (if switching to [constant XRInterface.XR_PLAY_AREA_STAGE]) or make the switch during a scene change.
</description>
</method>
- <method name="start_passthrough">
+ <method name="start_passthrough" is_deprecated="true">
<return type="bool" />
<description>
Starts passthrough, will return [code]false[/code] if passthrough couldn't be started.
[b]Note:[/b] The viewport used for XR must have a transparent background, otherwise passthrough may not properly render.
+ [i]Deprecated.[/i] Set the [member environment_blend_mode] to [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND], instead.
</description>
</method>
- <method name="stop_passthrough">
+ <method name="stop_passthrough" is_deprecated="true">
<return type="void" />
<description>
Stops passthrough.
+ [i]Deprecated.[/i] Set the [member environment_blend_mode] to [constant XRInterface.XR_ENV_BLEND_MODE_OPAQUE], instead.
</description>
</method>
<method name="supports_play_area_mode">
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 0a4dfd2e0d..5e70354d22 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2790,8 +2790,7 @@ void Node3DEditorViewport::_notification(int p_what) {
}
Transform3D t = sp->get_global_gizmo_transform();
- VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(sp);
- AABB new_aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp);
+ AABB new_aabb = _calculate_spatial_bounds(sp);
exist = true;
if (se->last_xform == t && se->aabb == new_aabb && !se->last_xform_dirty) {
@@ -2837,7 +2836,7 @@ void Node3DEditorViewport::_notification(int p_what) {
last_message = message;
}
- message_time -= get_physics_process_delta_time();
+ message_time -= get_process_delta_time();
if (message_time < 0) {
surface->queue_redraw();
}
@@ -4087,35 +4086,35 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
return world_pos + world_ray * FALLBACK_DISTANCE;
}
-AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_top_level_transform) {
+AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent) {
AABB bounds;
+ if (!p_top_level_parent) {
+ p_top_level_parent = p_parent;
+ }
+
+ if (!p_parent) {
+ return AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
+ }
+
+ Transform3D xform_to_top_level_parent_space = p_top_level_parent->get_global_transform().affine_inverse() * p_parent->get_global_transform();
+
const VisualInstance3D *visual_instance = Object::cast_to<VisualInstance3D>(p_parent);
if (visual_instance) {
bounds = visual_instance->get_aabb();
+ } else {
+ bounds = AABB();
}
+ bounds = xform_to_top_level_parent_space.xform(bounds);
for (int i = 0; i < p_parent->get_child_count(); i++) {
Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i));
if (child) {
- AABB child_bounds = _calculate_spatial_bounds(child, false);
-
- if (bounds.size == Vector3() && p_parent) {
- bounds = child_bounds;
- } else {
- bounds.merge_with(child_bounds);
- }
+ AABB child_bounds = _calculate_spatial_bounds(child, p_top_level_parent);
+ bounds.merge_with(child_bounds);
}
}
- if (bounds.size == Vector3() && !p_parent) {
- bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
- }
-
- if (!p_exclude_top_level_transform) {
- bounds = p_parent->get_transform().xform(bounds);
- }
-
return bounds;
}
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 455376b659..ed42e8e5ab 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -434,7 +434,7 @@ private:
Point2i _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const;
Vector3 _get_instance_position(const Point2 &p_pos) const;
- static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_top_level_transform = true);
+ static AABB _calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent = nullptr);
Node *_sanitize_preview_node(Node *p_node) const;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 4d998118e2..f177164ec2 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -674,10 +674,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (selection.size() == 1) {
- undo_redo->create_action(TTR("Move Node In Parent"));
+ undo_redo->create_action(TTR("Move Node in Parent"));
}
if (selection.size() > 1) {
- undo_redo->create_action(TTR("Move Nodes In Parent"));
+ undo_redo->create_action(TTR("Move Nodes in Parent"));
}
for (int i = 0; i < selection.size(); i++) {
@@ -4134,7 +4134,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root"));
ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene"));
ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
- ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show In FileSystem"));
+ ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show in FileSystem"));
ED_SHORTCUT("scene_tree/toggle_unique_name", TTR("Toggle Access as Unique Name"));
ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KeyModifierMask::SHIFT | Key::KEY_DELETE);
ED_SHORTCUT("scene_tree/delete", TTR("Delete"), Key::KEY_DELETE);
diff --git a/main/main.cpp b/main/main.cpp
index 0625874520..bc2a6107b5 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -238,6 +238,11 @@ bool profile_gpu = false;
static const String NULL_DISPLAY_DRIVER("headless");
static const String NULL_AUDIO_DRIVER("Dummy");
+// The length of the longest column in the command-line help we should align to
+// (excluding the 2-space left and right margins).
+// Currently, this is `--export-release <preset> <path>`.
+static const int OPTION_COLUMN_LENGTH = 32;
+
/* Helper methods */
bool Main::is_cmdline_tool() {
@@ -381,159 +386,241 @@ void finalize_theme_db() {
#define MAIN_PRINT(m_txt)
#endif
+/**
+ * Prints a copyright notice in the command-line help with colored text. A newline is
+ * automatically added at the end.
+ */
+void Main::print_help_copyright(const char *p_notice) {
+ OS::get_singleton()->print("\u001b[90m%s\u001b[0m\n", p_notice);
+}
+
+/**
+ * Prints a title in the command-line help with colored text. A newline is
+ * automatically added at beginning and at the end.
+ */
+void Main::print_help_title(const char *p_title) {
+ OS::get_singleton()->print("\n\u001b[1;93m%s:\u001b[0m\n", p_title);
+}
+
+/**
+ * Returns the option string with required and optional arguments colored separately from the rest of the option.
+ * This color replacement must be done *after* calling `rpad()` for the length padding to be done correctly.
+ */
+String Main::format_help_option(const char *p_option) {
+ return (String(p_option)
+ .rpad(OPTION_COLUMN_LENGTH)
+ .replace("[", "\u001b[96m[")
+ .replace("]", "]\u001b[0m")
+ .replace("<", "\u001b[95m<")
+ .replace(">", ">\u001b[0m"));
+}
+
+/**
+ * Prints an option in the command-line help with colored text. No newline is
+ * added at the end. `p_availability` denotes which build types the argument is
+ * available in. Support in release export templates implies support in debug
+ * export templates and editor. Support in debug export templates implies
+ * support in editor.
+ */
+void Main::print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability) {
+ const bool option_empty = (p_option && !p_option[0]);
+ if (!option_empty) {
+ const char *availability_badge = "";
+ switch (p_availability) {
+ case CLI_OPTION_AVAILABILITY_EDITOR:
+ availability_badge = "\u001b[1;91mE";
+ break;
+ case CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG:
+ availability_badge = "\u001b[1;94mD";
+ break;
+ case CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE:
+ availability_badge = "\u001b[1;92mR";
+ break;
+ case CLI_OPTION_AVAILABILITY_HIDDEN:
+ // Use for multiline option names (but not when the option name is empty).
+ availability_badge = " ";
+ break;
+ }
+ OS::get_singleton()->print(
+ " \u001b[92m%s %s\u001b[0m %s",
+ format_help_option(p_option).utf8().ptr(),
+ availability_badge,
+ p_description);
+ } else {
+ // Make continuation lines for descriptions faint if the option name is empty.
+ OS::get_singleton()->print(
+ " \u001b[92m%s \u001b[0m \u001b[90m%s",
+ format_help_option(p_option).utf8().ptr(),
+ p_description);
+ }
+}
+
void Main::print_help(const char *p_binary) {
- print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
- OS::get_singleton()->print("Free and open source software under the terms of the MIT license.\n");
- OS::get_singleton()->print("(c) 2014-present Godot Engine contributors.\n");
- OS::get_singleton()->print("(c) 2007-2014 Juan Linietsky, Ariel Manzur.\n");
- OS::get_singleton()->print("\n");
- OS::get_singleton()->print("Usage: %s [options] [path to scene or 'project.godot' file]\n", p_binary);
- OS::get_singleton()->print("\n");
+ print_line("\u001b[38;5;39m" + String(VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " - \u001b[4m" + String(VERSION_WEBSITE) + "\u001b[0m");
+ print_help_copyright("Free and open source software under the terms of the MIT license.");
+ print_help_copyright("(c) 2014-present Godot Engine contributors. (c) 2007-present Juan Linietsky, Ariel Manzur.");
- OS::get_singleton()->print("General options:\n");
- OS::get_singleton()->print(" -h, --help Display this help message.\n");
- OS::get_singleton()->print(" --version Display the version string.\n");
- OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n");
- OS::get_singleton()->print(" -q, --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n");
- OS::get_singleton()->print("\n");
+ print_help_title("Usage");
+ OS::get_singleton()->print(" %s \u001b[96m[options] [path to scene or \"project.godot\" file]\u001b[0m\n", p_binary);
+
+#if defined(TOOLS_ENABLED)
+ print_help_title("Option legend (this build = editor)");
+#elif defined(DEBUG_ENABLED)
+ print_help_title("Option legend (this build = debug export template)");
+#else
+ print_help_title("Option legend (this build = release export template)");
+#endif
+
+ OS::get_singleton()->print(" \u001b[1;92mR\u001b[0m Available in editor builds, debug export templates and release export templates.\n");
+#ifdef DEBUG_ENABLED
+ OS::get_singleton()->print(" \u001b[1;94mD\u001b[0m Available in editor builds and debug export templates only.\n");
+#endif
+#ifdef TOOLS_ENABLED
+ OS::get_singleton()->print(" \u001b[1;91mE\u001b[0m Only available in editor builds.\n");
+#endif
- OS::get_singleton()->print("Run options:\n");
- OS::get_singleton()->print(" --, ++ Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n");
+ print_help_title("General options");
+ print_help_option("-h, --help", "Display this help message.\n");
+ print_help_option("--version", "Display the version string.\n");
+ print_help_option("-v, --verbose", "Use verbose stdout mode.\n");
+ print_help_option("--quiet", "Quiet mode, silences stdout messages. Errors are still displayed.\n");
+
+ print_help_title("Run options");
+ print_help_option("--, ++", "Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n");
#ifdef TOOLS_ENABLED
- OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
- OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
- OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n");
+ print_help_option("-e, --editor", "Start the editor instead of running the scene.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("-p, --project-manager", "Start the project manager, even if a project is auto-detected.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--debug-server <uri>", "Start the editor debug server (<protocol>://<host/IP>[:port], e.g. tcp://127.0.0.1:6007)\n", CLI_OPTION_AVAILABILITY_EDITOR);
#if defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP)
- OS::get_singleton()->print(" --lsp-port <port> Use the specified port for the language server protocol. The port must be between 0 to 65535.\n");
+ print_help_option("--lsp-port <port>", "Use the specified port for the GDScript language server protocol. The port should be between 1025 and 49150.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif // MODULE_GDSCRIPT_ENABLED && !GDSCRIPT_NO_LSP
-#endif // TOOLS_ENABLED
- OS::get_singleton()->print(" --quit Quit after the first iteration.\n");
- OS::get_singleton()->print(" --quit-after <int> Quit after the given number of iterations. Set to 0 to disable.\n");
- OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
- OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
- OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n");
- OS::get_singleton()->print(" --main-pack <file> Path to a pack (.pck) file to load.\n");
- OS::get_singleton()->print(" --render-thread <mode> Render thread mode ['unsafe', 'safe', 'separate'].\n");
- OS::get_singleton()->print(" --remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).\n");
- OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n");
-
- OS::get_singleton()->print(" --audio-driver <driver> Audio driver [");
+#endif
+ print_help_option("--quit", "Quit after the first iteration.\n");
+ print_help_option("--quit-after <int>", "Quit after the given number of iterations. Set to 0 to disable.\n");
+ print_help_option("-l, --language <locale>", "Use a specific locale (<locale> being a two-letter code).\n");
+ print_help_option("--path <directory>", "Path to a project (<directory> must contain a \"project.godot\" file).\n");
+ print_help_option("-u, --upwards", "Scan folders upwards for project.godot file.\n");
+ print_help_option("--main-pack <file>", "Path to a pack (.pck) file to load.\n");
+ print_help_option("--render-thread <mode>", "Render thread mode (\"unsafe\", \"safe\", \"separate\").\n");
+ print_help_option("--remote-fs <address>", "Remote filesystem (<host/IP>[:<port>] address).\n");
+ print_help_option("--remote-fs-password <password>", "Password for remote filesystem.\n");
+
+ print_help_option("--audio-driver <driver>", "Audio driver [");
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (i > 0) {
OS::get_singleton()->print(", ");
}
- OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name());
+ OS::get_singleton()->print("\"%s\"", AudioDriverManager::get_driver(i)->get_name());
}
OS::get_singleton()->print("].\n");
- OS::get_singleton()->print(" --display-driver <driver> Display driver (and rendering driver) [");
+ print_help_option("--display-driver <driver>", "Display driver (and rendering driver) [");
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i > 0) {
OS::get_singleton()->print(", ");
}
- OS::get_singleton()->print("'%s' (", DisplayServer::get_create_function_name(i));
+ OS::get_singleton()->print("\"%s\" (", DisplayServer::get_create_function_name(i));
Vector<String> rd = DisplayServer::get_create_function_rendering_drivers(i);
for (int j = 0; j < rd.size(); j++) {
if (j > 0) {
OS::get_singleton()->print(", ");
}
- OS::get_singleton()->print("'%s'", rd[j].utf8().get_data());
+ OS::get_singleton()->print("\"%s\"", rd[j].utf8().get_data());
}
OS::get_singleton()->print(")");
}
OS::get_singleton()->print("].\n");
- OS::get_singleton()->print(" --audio-output-latency <ms> Override audio output latency in milliseconds (default is 15 ms).\n");
- OS::get_singleton()->print(" Lower values make sound playback more reactive but increase CPU usage, and may result in audio cracking if the CPU can't keep up.\n");
-
- OS::get_singleton()->print(" --rendering-method <renderer> Renderer name. Requires driver support.\n");
- OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
- OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n");
- OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping).\n");
- OS::get_singleton()->print(" --tablet-driver <driver> Pen tablet input driver.\n");
- OS::get_singleton()->print(" --headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n");
- OS::get_singleton()->print(" --log-file <file> Write output/error log to the specified path instead of the default location defined by the project.\n");
- OS::get_singleton()->print(" <file> path should be absolute or relative to the project directory.\n");
- OS::get_singleton()->print(" --write-movie <file> Write a video to the specified path (usually with .avi or .png extension).\n");
- OS::get_singleton()->print(" --fixed-fps is forced when enabled, but it can be used to change movie FPS.\n");
- OS::get_singleton()->print(" --disable-vsync can speed up movie writing but makes interaction more difficult.\n");
- OS::get_singleton()->print(" --quit-after can be used to specify the number of frames to write.\n");
-
- OS::get_singleton()->print("\n");
-
- OS::get_singleton()->print("Display options:\n");
- OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n");
- OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n");
- OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n");
- OS::get_singleton()->print(" -t, --always-on-top Request an always-on-top window.\n");
- OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n");
- OS::get_singleton()->print(" --position <X>,<Y> Request window position (if set, screen argument is ignored).\n");
- OS::get_singleton()->print(" --screen <N> Request window screen.\n");
- OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n");
- OS::get_singleton()->print(" --xr-mode <mode> Select XR (Extended Reality) mode ['default', 'off', 'on'].\n");
- OS::get_singleton()->print("\n");
-
- OS::get_singleton()->print("Debug options:\n");
- OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n");
- OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
- OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
- OS::get_singleton()->print(" --gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.\n");
- OS::get_singleton()->print(" --gpu-validation Enable graphics API validation layers for debugging.\n");
+ print_help_option("--audio-output-latency <ms>", "Override audio output latency in milliseconds (default is 15 ms).\n");
+ print_help_option("", "Lower values make sound playback more reactive but increase CPU usage, and may result in audio cracking if the CPU can't keep up.\n");
+
+ print_help_option("--rendering-method <renderer>", "Renderer name. Requires driver support.\n");
+ print_help_option("--rendering-driver <driver>", "Rendering driver (depends on display driver).\n");
+ print_help_option("--gpu-index <device_index>", "Use a specific GPU (run with --verbose to get a list of available devices).\n");
+ print_help_option("--text-driver <driver>", "Text driver (used for font rendering, bidirectional support and shaping).\n");
+ print_help_option("--tablet-driver <driver>", "Pen tablet input driver.\n");
+ print_help_option("--headless", "Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n");
+ print_help_option("--log-file <file>", "Write output/error log to the specified path instead of the default location defined by the project.\n");
+ print_help_option("", "<file> path should be absolute or relative to the project directory.\n");
+ print_help_option("--write-movie <file>", "Write a video to the specified path (usually with .avi or .png extension).\n");
+ print_help_option("", "--fixed-fps is forced when enabled, but it can be used to change movie FPS.\n");
+ print_help_option("", "--disable-vsync can speed up movie writing but makes interaction more difficult.\n");
+ print_help_option("", "--quit-after can be used to specify the number of frames to write.\n");
+
+ print_help_title("Display options");
+ print_help_option("-f, --fullscreen", "Request fullscreen mode.\n");
+ print_help_option("-m, --maximized", "Request a maximized window.\n");
+ print_help_option("-w, --windowed", "Request windowed mode.\n");
+ print_help_option("-t, --always-on-top", "Request an always-on-top window.\n");
+ print_help_option("--resolution <W>x<H>", "Request window resolution.\n");
+ print_help_option("--position <X>,<Y>", "Request window position.\n");
+ print_help_option("--screen <N>", "Request window screen.\n");
+ print_help_option("--single-window", "Use a single window (no separate subwindows).\n");
+ print_help_option("--xr-mode <mode>", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n");
+
+ print_help_title("Debug options");
+ print_help_option("-d, --debug", "Debug (local stdout debugger).\n");
+ print_help_option("-b, --breakpoints", "Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
+ print_help_option("--profiling", "Enable profiling in the script debugger.\n");
+ print_help_option("--gpu-profile", "Show a GPU profile of the tasks that took the most time during frame rendering.\n");
+ print_help_option("--gpu-validation", "Enable graphics API validation layers for debugging.\n");
#ifdef DEBUG_ENABLED
- OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
+ print_help_option("--gpu-abort", "Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
#endif
- OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
- OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
- OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
+ print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
+ print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
+ print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
#if defined(DEBUG_ENABLED)
- OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
- OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n");
- OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
- OS::get_singleton()->print(" --debug-avoidance Show navigation avoidance debug visuals when running the scene.\n");
- OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n");
- OS::get_singleton()->print(" --debug-canvas-item-redraw Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n");
-#endif
- OS::get_singleton()->print(" --max-fps <fps> Set a maximum number of frames per second rendered (can be used to limit power usage). A value of 0 results in unlimited framerate.\n");
- OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n");
- OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
- OS::get_singleton()->print(" --disable-vsync Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n");
- OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
- OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
- OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
- OS::get_singleton()->print(" --delta-smoothing <enable> Enable or disable frame delta smoothing ['enable', 'disable'].\n");
- OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n");
- OS::get_singleton()->print("\n");
+ print_help_option("--debug-collisions", "Show collision shapes when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
+ print_help_option("--debug-paths", "Show path lines when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
+ print_help_option("--debug-navigation", "Show navigation polygons when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
+ print_help_option("--debug-avoidance", "Show navigation avoidance debug visuals when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
+ print_help_option("--debug-stringnames", "Print all StringName allocations to stdout when the engine quits.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
+ print_help_option("--debug-canvas-item-redraw", "Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
- OS::get_singleton()->print("Standalone tools:\n");
- OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
- OS::get_singleton()->print(" --main-loop <main_loop_name> Run a MainLoop specified by its global class name.\n");
- OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
+#endif
+ print_help_option("--max-fps <fps>", "Set a maximum number of frames per second rendered (can be used to limit power usage). A value of 0 results in unlimited framerate.\n");
+ print_help_option("--frame-delay <ms>", "Simulate high CPU load (delay each frame by <ms> milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n");
+ print_help_option("--time-scale <scale>", "Force time scale (higher values are faster, 1.0 is normal speed).\n");
+ print_help_option("--disable-vsync", "Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n");
+ print_help_option("--disable-render-loop", "Disable render loop so rendering only occurs when called explicitly from script.\n");
+ print_help_option("--disable-crash-handler", "Disable crash handler when supported by the platform code.\n");
+ print_help_option("--fixed-fps <fps>", "Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
+ print_help_option("--delta-smoothing <enable>", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n");
+ print_help_option("--print-fps", "Print the frames per second to the stdout.\n");
+
+ print_help_title("Standalone tools");
+ print_help_option("-s, --script <script>", "Run a script.\n");
+ print_help_option("--main-loop <main_loop_name>", "Run a MainLoop specified by its global class name.\n");
+ print_help_option("--check-only", "Only parse for errors and quit (use with --script).\n");
#ifdef TOOLS_ENABLED
- OS::get_singleton()->print(" --export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.\n");
- OS::get_singleton()->print(" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe').\n");
- OS::get_singleton()->print(" The target directory must exist.\n");
- OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n");
- OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n");
- OS::get_singleton()->print(" --install-android-build-template Install the android build template. Used in conjunction with --export-release or --export-debug.\n");
+ print_help_option("--export-release <preset> <path>", "Export the project in release mode using the given preset and output path. The preset name should match one defined in \"export_presets.cfg\".\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("", "<path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. \"builds/game.exe\").\n");
+ print_help_option("", "The target directory must exist.\n");
+ print_help_option("--export-debug <preset> <path>", "Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--export-pack <preset> <path>", "Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--install-android-build-template", "Install the Android build template. Used in conjunction with --export-release or --export-debug.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifndef DISABLE_DEPRECATED
- OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n");
- OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n");
- OS::get_singleton()->print(" --validate-conversion-3to4 [<max_file_kb>] [<max_line_size>]\n");
- OS::get_singleton()->print(" Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n");
+ // Commands are long; split the description to a second line.
+ print_help_option("--convert-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN);
+ print_help_option(" [max_file_kb] [max_line_size]", "Converts project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--validate-conversion-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN);
+ print_help_option(" [max_file_kb] [max_line_size]", "Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif // DISABLE_DEPRECATED
- OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
- OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
+ print_help_option("--doctool [path]", "Dump the engine API reference to the given <path> (defaults to current directory) in XML format, merging if existing files are found.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--no-docbase", "Disallow dumping the base types (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifdef MODULE_GDSCRIPT_ENABLED
- OS::get_singleton()->print(" --gdscript-docs <path> Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n");
+ print_help_option("--gdscript-docs <path>", "Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
- OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
- OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
- OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
- OS::get_singleton()->print(" --dump-extension-api-with-docs Generate JSON dump of the Godot API like the previous option, but including documentation.\n");
- OS::get_singleton()->print(" --validate-extension-api <path> Validate an extension API file dumped (with one of the two previous options) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n");
- OS::get_singleton()->print(" --benchmark Benchmark the run time and print it to console.\n");
- OS::get_singleton()->print(" --benchmark-file <path> Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n");
+ print_help_option("--build-solutions", "Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--dump-gdextension-interface", "Generate a GDExtension header file \"gdextension_interface.h\" in the current folder. This file is the base file required to implement a GDExtension.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--dump-extension-api", "Generate a JSON dump of the Godot API for GDExtension bindings named \"extension_api.json\" in the current folder.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--dump-extension-api-with-docs", "Generate JSON dump of the Godot API like the previous option, but including documentation.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--validate-extension-api <path>", "Validate an extension API file dumped (with one of the two previous options) from a previous version of the engine to ensure API compatibility.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("", "If incompatibilities or errors are detected, the exit code will be non-zero.\n");
+ print_help_option("--benchmark", "Benchmark the run time and print it to console.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--benchmark-file <path>", "Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifdef TESTS_ENABLED
- OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n");
+ print_help_option("--test [--help]", "Run unit tests. Use --test --help for more information.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
#endif
OS::get_singleton()->print("\n");
diff --git a/main/main.h b/main/main.h
index cc0655cd02..09cc0feae6 100644
--- a/main/main.h
+++ b/main/main.h
@@ -39,6 +39,17 @@ template <class T>
class Vector;
class Main {
+ enum CLIOptionAvailability {
+ CLI_OPTION_AVAILABILITY_EDITOR,
+ CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG,
+ CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE,
+ CLI_OPTION_AVAILABILITY_HIDDEN,
+ };
+
+ static void print_help_copyright(const char *p_notice);
+ static void print_help_title(const char *p_title);
+ static void print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability = CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE);
+ static String format_help_option(const char *p_option);
static void print_help(const char *p_binary);
static uint64_t last_ticks;
static uint32_t hide_print_fps_attempts;
diff --git a/methods.py b/methods.py
index f36591d211..c22b1f11e4 100644
--- a/methods.py
+++ b/methods.py
@@ -774,161 +774,6 @@ def add_to_vs_project(env, sources):
env.vs_srcs += [basename + ".cpp"]
-def generate_vs_project(env, original_args, project_name="godot"):
- batch_file = find_visual_c_batch_file(env)
- filtered_args = original_args.copy()
- # Ignore the "vsproj" option to not regenerate the VS project on every build
- filtered_args.pop("vsproj", None)
- # The "platform" option is ignored because only the Windows platform is currently supported for VS projects
- filtered_args.pop("platform", None)
- # The "target" option is ignored due to the way how targets configuration is performed for VS projects (there is a separate project configuration for each target)
- filtered_args.pop("target", None)
- # The "progress" option is ignored as the current compilation progress indication doesn't work in VS
- filtered_args.pop("progress", None)
-
- if batch_file:
-
- class ModuleConfigs(Mapping):
- # This version information (Win32, x64, Debug, Release) seems to be
- # required for Visual Studio to understand that it needs to generate an NMAKE
- # project. Do not modify without knowing what you are doing.
- PLATFORMS = ["Win32", "x64"]
- PLATFORM_IDS = ["x86_32", "x86_64"]
- CONFIGURATIONS = ["editor", "template_release", "template_debug"]
- DEV_SUFFIX = ".dev" if env["dev_build"] else ""
-
- @staticmethod
- def for_every_variant(value):
- return [value for _ in range(len(ModuleConfigs.CONFIGURATIONS) * len(ModuleConfigs.PLATFORMS))]
-
- def __init__(self):
- shared_targets_array = []
- self.names = []
- self.arg_dict = {
- "variant": [],
- "runfile": shared_targets_array,
- "buildtarget": shared_targets_array,
- "cpppaths": [],
- "cppdefines": [],
- "cmdargs": [],
- }
- self.add_mode() # default
-
- def add_mode(
- self,
- name: str = "",
- includes: str = "",
- cli_args: str = "",
- defines=None,
- ):
- if defines is None:
- defines = []
- self.names.append(name)
- self.arg_dict["variant"] += [
- f'{config}{f"_[{name}]" if name else ""}|{platform}'
- for config in ModuleConfigs.CONFIGURATIONS
- for platform in ModuleConfigs.PLATFORMS
- ]
- self.arg_dict["runfile"] += [
- f'bin\\godot.windows.{config}{ModuleConfigs.DEV_SUFFIX}{".double" if env["precision"] == "double" else ""}.{plat_id}{f".{name}" if name else ""}.exe'
- for config in ModuleConfigs.CONFIGURATIONS
- for plat_id in ModuleConfigs.PLATFORM_IDS
- ]
- self.arg_dict["cpppaths"] += ModuleConfigs.for_every_variant(env["CPPPATH"] + [includes])
- self.arg_dict["cppdefines"] += ModuleConfigs.for_every_variant(list(env["CPPDEFINES"]) + defines)
- self.arg_dict["cmdargs"] += ModuleConfigs.for_every_variant(cli_args)
-
- def build_commandline(self, commands):
- configuration_getter = (
- "$(Configuration"
- + "".join([f'.Replace("{name}", "")' for name in self.names[1:]])
- + '.Replace("_[]", "")'
- + ")"
- )
-
- common_build_prefix = [
- 'cmd /V /C set "plat=$(PlatformTarget)"',
- '(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))',
- 'call "' + batch_file + '" !plat!',
- ]
-
- # Windows allows us to have spaces in paths, so we need
- # to double quote off the directory. However, the path ends
- # in a backslash, so we need to remove this, lest it escape the
- # last double quote off, confusing MSBuild
- common_build_postfix = [
- "--directory=\"$(ProjectDir.TrimEnd('\\'))\"",
- "platform=windows",
- f"target={configuration_getter}",
- "progress=no",
- ]
-
- for arg, value in filtered_args.items():
- common_build_postfix.append(f"{arg}={value}")
-
- result = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)])
- return result
-
- # Mappings interface definitions
-
- def __iter__(self) -> Iterator[str]:
- for x in self.arg_dict:
- yield x
-
- def __len__(self) -> int:
- return len(self.names)
-
- def __getitem__(self, k: str):
- return self.arg_dict[k]
-
- add_to_vs_project(env, env.core_sources)
- add_to_vs_project(env, env.drivers_sources)
- add_to_vs_project(env, env.main_sources)
- add_to_vs_project(env, env.modules_sources)
- add_to_vs_project(env, env.scene_sources)
- add_to_vs_project(env, env.servers_sources)
- if env["tests"]:
- add_to_vs_project(env, env.tests_sources)
- if env.editor_build:
- add_to_vs_project(env, env.editor_sources)
-
- for header in glob_recursive("**/*.h"):
- env.vs_incs.append(str(header))
-
- module_configs = ModuleConfigs()
-
- if env.get("module_mono_enabled"):
- mono_defines = [("GD_MONO_HOT_RELOAD",)] if env.editor_build else []
- module_configs.add_mode(
- "mono",
- cli_args="module_mono_enabled=yes",
- defines=mono_defines,
- )
-
- scons_cmd = "scons"
-
- path_to_venv = os.getenv("VIRTUAL_ENV")
- path_to_scons_exe = Path(str(path_to_venv)) / "Scripts" / "scons.exe"
- if path_to_venv and path_to_scons_exe.exists():
- scons_cmd = str(path_to_scons_exe)
-
- env["MSVSBUILDCOM"] = module_configs.build_commandline(scons_cmd)
- env["MSVSREBUILDCOM"] = module_configs.build_commandline(f"{scons_cmd} vsproj=yes")
- env["MSVSCLEANCOM"] = module_configs.build_commandline(f"{scons_cmd} --clean")
- if not env.get("MSVS"):
- env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj"
- env["MSVS"]["SOLUTIONSUFFIX"] = ".sln"
- env.MSVSProject(
- target=["#" + project_name + env["MSVSPROJECTSUFFIX"]],
- incs=env.vs_incs,
- srcs=env.vs_srcs,
- auto_build_solution=1,
- **module_configs,
- )
- else:
- print("Could not locate Visual Studio batch file to set up the build environment. Not generating VS project.")
-
-
def precious_program(env, program, sources, **args):
program = env.ProgramOriginal(program, sources, **args)
env.Precious(program)
@@ -1229,3 +1074,456 @@ def dump(env):
with open(".scons_env.json", "w") as f:
dump(env.Dictionary(), f, indent=4, default=non_serializable)
+
+
+# Custom Visual Studio project generation logic that supports any platform that has a msvs.py
+# script, so Visual Studio can be used to run scons for any platform, with the right defines per target.
+# Invoked with scons vsproj=yes
+#
+# Only platforms that opt in to vs proj generation by having a msvs.py file in the platform folder are included.
+# Platforms with a msvs.py file will be added to the solution, but only the current active platform+target+arch
+# will have a build configuration generated, because we only know what the right defines/includes/flags/etc are
+# on the active build target.
+#
+# Platforms that don't support an editor target will have a dummy editor target that won't do anything on build,
+# but will have the files and configuration for the windows editor target.
+#
+# To generate build configuration files for all platforms+targets+arch combinations, users can call
+# scons vsproj=yes
+# for each combination of platform+target+arch. This will generate the relevant vs project files but
+# skip the build process. This lets project files be quickly generated even if there are build errors.
+#
+# To generate AND build from the command line:
+# scons vsproj=yes vsproj_gen_only=yes
+def generate_vs_project(env, original_args, project_name="godot"):
+ # Augmented glob_recursive that also fills the dirs argument with traversed directories that have content.
+ def glob_recursive_2(pattern, dirs, node="."):
+ from SCons import Node
+ from SCons.Script import Glob
+
+ results = []
+ for f in Glob(str(node) + "/*", source=True):
+ if type(f) is Node.FS.Dir:
+ results += glob_recursive_2(pattern, dirs, f)
+ r = Glob(str(node) + "/" + pattern, source=True)
+ if len(r) > 0 and not str(node) in dirs:
+ d = ""
+ for part in str(node).split("\\"):
+ d += part
+ if not d in dirs:
+ dirs.append(d)
+ d += "\\"
+ results += r
+ return results
+
+ def get_bool(args, option, default):
+ from SCons.Variables.BoolVariable import _text2bool
+
+ val = args.get(option, default)
+ if val is not None:
+ try:
+ return _text2bool(val)
+ except:
+ return default
+ else:
+ return default
+
+ def format_key_value(v):
+ if type(v) in [tuple, list]:
+ return v[0] if len(v) == 1 else f"{v[0]}={v[1]}"
+ return v
+
+ filtered_args = original_args.copy()
+
+ # Ignore the "vsproj" option to not regenerate the VS project on every build
+ filtered_args.pop("vsproj", None)
+
+ # This flag allows users to regenerate the proj files but skip the building process.
+ # This lets projects be regenerated even if there are build errors.
+ filtered_args.pop("vsproj_gen_only", None)
+
+ # The "progress" option is ignored as the current compilation progress indication doesn't work in VS
+ filtered_args.pop("progress", None)
+
+ # We add these three manually because they might not be explicitly passed in, and it's important to always set them.
+ filtered_args.pop("platform", None)
+ filtered_args.pop("target", None)
+ filtered_args.pop("arch", None)
+
+ platform = env["platform"]
+ target = env["target"]
+ arch = env["arch"]
+
+ vs_configuration = {}
+ common_build_prefix = []
+ confs = []
+ for x in sorted(glob.glob("platform/*")):
+ # Only platforms that opt in to vs proj generation are included.
+ if not os.path.isdir(x) or not os.path.exists(x + "/msvs.py"):
+ continue
+ tmppath = "./" + x
+ sys.path.insert(0, tmppath)
+ import msvs
+
+ vs_plats = []
+ vs_confs = []
+ try:
+ platform_name = x[9:]
+ vs_plats = msvs.get_platforms()
+ vs_confs = msvs.get_configurations()
+ val = []
+ for plat in vs_plats:
+ val += [{"platform": plat[0], "architecture": plat[1]}]
+
+ vsconf = {"platform": platform_name, "targets": vs_confs, "arches": val}
+ confs += [vsconf]
+
+ # Save additional information about the configuration for the actively selected platform,
+ # so we can generate the platform-specific props file with all the build commands/defines/etc
+ if platform == platform_name:
+ common_build_prefix = msvs.get_build_prefix(env)
+ vs_configuration = vsconf
+ except Exception:
+ pass
+
+ sys.path.remove(tmppath)
+ sys.modules.pop("msvs")
+
+ headers = []
+ headers_dirs = []
+ for file in glob_recursive_2("*.h", headers_dirs):
+ headers.append(str(file).replace("/", "\\"))
+ for file in glob_recursive_2("*.hpp", headers_dirs):
+ headers.append(str(file).replace("/", "\\"))
+
+ sources = []
+ sources_dirs = []
+ for file in glob_recursive_2("*.cpp", sources_dirs):
+ sources.append(str(file).replace("/", "\\"))
+ for file in glob_recursive_2("*.c", sources_dirs):
+ sources.append(str(file).replace("/", "\\"))
+
+ others = []
+ others_dirs = []
+ for file in glob_recursive_2("*.natvis", others_dirs):
+ others.append(str(file).replace("/", "\\"))
+ for file in glob_recursive_2("*.glsl", others_dirs):
+ others.append(str(file).replace("/", "\\"))
+
+ skip_filters = False
+ import hashlib
+ import json
+
+ md5 = hashlib.md5(
+ json.dumps(headers + headers_dirs + sources + sources_dirs + others + others_dirs, sort_keys=True).encode(
+ "utf-8"
+ )
+ ).hexdigest()
+
+ if os.path.exists(f"{project_name}.vcxproj.filters"):
+ existing_filters = open(f"{project_name}.vcxproj.filters", "r").read()
+ match = re.search(r"(?ms)^<!-- CHECKSUM$.([0-9a-f]{32})", existing_filters)
+ if match is not None and md5 == match.group(1):
+ skip_filters = True
+
+ import uuid
+
+ # Don't regenerate the filters file if nothing has changed, so we keep the existing UUIDs.
+ if not skip_filters:
+ print(f"Regenerating {project_name}.vcxproj.filters")
+
+ filters_template = open("misc/msvs/vcxproj.filters.template", "r").read()
+ for i in range(1, 10):
+ filters_template = filters_template.replace(f"%%UUID{i}%%", str(uuid.uuid4()))
+
+ filters = ""
+
+ for d in headers_dirs:
+ filters += f'<Filter Include="Header Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
+ for d in sources_dirs:
+ filters += f'<Filter Include="Source Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
+ for d in others_dirs:
+ filters += f'<Filter Include="Other Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n'
+
+ filters_template = filters_template.replace("%%FILTERS%%", filters)
+
+ filters = ""
+ for file in headers:
+ filters += (
+ f'<ClInclude Include="{file}"><Filter>Header Files\\{os.path.dirname(file)}</Filter></ClInclude>\n'
+ )
+ filters_template = filters_template.replace("%%INCLUDES%%", filters)
+
+ filters = ""
+ for file in sources:
+ filters += (
+ f'<ClCompile Include="{file}"><Filter>Source Files\\{os.path.dirname(file)}</Filter></ClCompile>\n'
+ )
+
+ filters_template = filters_template.replace("%%COMPILES%%", filters)
+
+ filters = ""
+ for file in others:
+ filters += f'<None Include="{file}"><Filter>Other Files\\{os.path.dirname(file)}</Filter></None>\n'
+ filters_template = filters_template.replace("%%OTHERS%%", filters)
+
+ filters_template = filters_template.replace("%%HASH%%", md5)
+
+ with open(f"{project_name}.vcxproj.filters", "w") as f:
+ f.write(filters_template)
+
+ envsources = []
+
+ envsources += env.core_sources
+ envsources += env.drivers_sources
+ envsources += env.main_sources
+ envsources += env.modules_sources
+ envsources += env.scene_sources
+ envsources += env.servers_sources
+ if env.editor_build:
+ envsources += env.editor_sources
+ envsources += env.platform_sources
+
+ headers_active = []
+ sources_active = []
+ others_active = []
+ for x in envsources:
+ fname = ""
+ if type(x) == type(""):
+ fname = env.File(x).path
+ else:
+ # Some object files might get added directly as a File object and not a list.
+ try:
+ fname = env.File(x)[0].path
+ except:
+ fname = x.path
+ pass
+
+ if fname:
+ fname = fname.replace("\\\\", "/")
+ parts = os.path.splitext(fname)
+ basename = parts[0]
+ ext = parts[1]
+ idx = fname.find(env["OBJSUFFIX"])
+ if ext in [".h", ".hpp"]:
+ headers_active += [fname]
+ elif ext in [".c", ".cpp"]:
+ sources_active += [fname]
+ elif idx > 0:
+ basename = fname[:idx]
+ if os.path.isfile(basename + ".h"):
+ headers_active += [basename + ".h"]
+ elif os.path.isfile(basename + ".hpp"):
+ headers_active += [basename + ".hpp"]
+ elif basename.endswith(".gen") and os.path.isfile(basename[:-4] + ".h"):
+ headers_active += [basename[:-4] + ".h"]
+ if os.path.isfile(basename + ".c"):
+ sources_active += [basename + ".c"]
+ elif os.path.isfile(basename + ".cpp"):
+ sources_active += [basename + ".cpp"]
+ else:
+ fname = os.path.relpath(os.path.abspath(fname), env.Dir("").abspath)
+ others_active += [fname]
+
+ all_items = []
+ properties = []
+ activeItems = []
+ extraItems = []
+
+ set_headers = set(headers_active)
+ set_sources = set(sources_active)
+ set_others = set(others_active)
+ for file in headers:
+ all_items.append(f'<ClInclude Include="{file}">')
+ all_items.append(
+ f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
+ )
+ all_items.append("</ClInclude>")
+ if file in set_headers:
+ activeItems.append(file)
+
+ for file in sources:
+ all_items.append(f'<ClCompile Include="{file}">')
+ all_items.append(
+ f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
+ )
+ all_items.append("</ClCompile>")
+ if file in set_sources:
+ activeItems.append(file)
+
+ for file in others:
+ all_items.append(f'<None Include="{file}">')
+ all_items.append(
+ f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>"
+ )
+ all_items.append("</None>")
+ if file in set_others:
+ activeItems.append(file)
+
+ if vs_configuration:
+ vsconf = ""
+ for a in vs_configuration["arches"]:
+ if arch == a["architecture"]:
+ vsconf = f'{target}|{a["platform"]}'
+ break
+
+ condition = "'$(Configuration)|$(Platform)'=='" + vsconf + "'"
+ properties.append("<ActiveProjectItemList>;" + ";".join(activeItems) + ";</ActiveProjectItemList>")
+ output = f'bin\\godot{env["PROGSUFFIX"]}'
+
+ props_template = open("misc/msvs/props.template", "r").read()
+
+ props_template = props_template.replace("%%VSCONF%%", vsconf)
+ props_template = props_template.replace("%%CONDITION%%", condition)
+ props_template = props_template.replace("%%PROPERTIES%%", "\n ".join(properties))
+ props_template = props_template.replace("%%EXTRA_ITEMS%%", "\n ".join(extraItems))
+
+ props_template = props_template.replace("%%OUTPUT%%", output)
+
+ props_template = props_template.replace(
+ "%%DEFINES%%", ";".join([format_key_value(v) for v in list(env["CPPDEFINES"])])
+ )
+ props_template = props_template.replace("%%INCLUDES%%", ";".join([str(j) for j in env["CPPPATH"]]))
+ props_template = props_template.replace(
+ "%%OPTIONS%%",
+ " ".join(env["CCFLAGS"]) + " " + " ".join([x for x in env["CXXFLAGS"] if not x.startswith("$")]),
+ )
+
+ # Windows allows us to have spaces in paths, so we need
+ # to double quote off the directory. However, the path ends
+ # in a backslash, so we need to remove this, lest it escape the
+ # last double quote off, confusing MSBuild
+ common_build_postfix = [
+ "--directory=&quot;$(ProjectDir.TrimEnd(&apos;\\&apos;))&quot;",
+ "progress=no",
+ f"platform={platform}",
+ f"target={target}",
+ f"arch={arch}",
+ ]
+
+ for arg, value in filtered_args.items():
+ common_build_postfix.append(f"{arg}={value}")
+
+ cmd_rebuild = [
+ "vsproj=yes",
+ f"vsproj_name={project_name}",
+ ] + common_build_postfix
+
+ cmd_clean = [
+ "--clean",
+ ] + common_build_postfix
+
+ commands = "scons"
+ if len(common_build_prefix) == 0:
+ commands = "echo Starting SCons &amp;&amp; cmd /V /C " + commands
+ else:
+ common_build_prefix[0] = "echo Starting SCons &amp;&amp; cmd /V /C " + common_build_prefix[0]
+
+ cmd = " ^&amp; ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)])
+ props_template = props_template.replace("%%BUILD%%", cmd)
+
+ cmd = " ^&amp; ".join(common_build_prefix + [" ".join([commands] + cmd_rebuild)])
+ props_template = props_template.replace("%%REBUILD%%", cmd)
+
+ cmd = " ^&amp; ".join(common_build_prefix + [" ".join([commands] + cmd_clean)])
+ props_template = props_template.replace("%%CLEAN%%", cmd)
+
+ with open(f"{project_name}.{platform}.{target}.{arch}.generated.props", "w") as f:
+ f.write(props_template)
+
+ proj_uuid = str(uuid.uuid4())
+ sln_uuid = str(uuid.uuid4())
+
+ if os.path.exists(f"{project_name}.sln"):
+ for line in open(f"{project_name}.sln", "r").read().splitlines():
+ if line.startswith('Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")'):
+ proj_uuid = re.search(
+ r"\"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}\"$",
+ line,
+ ).group(1)
+ elif line.strip().startswith("SolutionGuid ="):
+ sln_uuid = re.search(
+ r"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}", line
+ ).group(1)
+ break
+
+ configurations = []
+ imports = []
+ properties = []
+ section1 = []
+ section2 = []
+ for conf in confs:
+ godot_platform = conf["platform"]
+ for p in conf["arches"]:
+ sln_plat = p["platform"]
+ proj_plat = sln_plat
+ godot_arch = p["architecture"]
+
+ # Redirect editor configurations for non-Windows platforms to the Windows one, so the solution has all the permutations
+ # and VS doesn't complain about missing project configurations.
+ # These configurations are disabled, so they show up but won't build.
+ if godot_platform != "windows":
+ section1 += [f"editor|{sln_plat} = editor|{proj_plat}"]
+ section2 += [
+ f"{{{proj_uuid}}}.editor|{proj_plat}.ActiveCfg = editor|{proj_plat}",
+ ]
+
+ for t in conf["targets"]:
+ godot_target = t
+
+ # Windows x86 is a special little flower that requires a project platform == Win32 but a solution platform == x86.
+ if godot_platform == "windows" and godot_target == "editor" and godot_arch == "x86_32":
+ sln_plat = "x86"
+
+ configurations += [
+ f'<ProjectConfiguration Include="{godot_target}|{proj_plat}">',
+ f" <Configuration>{godot_target}</Configuration>",
+ f" <Platform>{proj_plat}</Platform>",
+ "</ProjectConfiguration>",
+ ]
+
+ if godot_platform != "windows":
+ configurations += [
+ f'<ProjectConfiguration Include="editor|{proj_plat}">',
+ f" <Configuration>editor</Configuration>",
+ f" <Platform>{proj_plat}</Platform>",
+ "</ProjectConfiguration>",
+ ]
+
+ p = f"{project_name}.{godot_platform}.{godot_target}.{godot_arch}.generated.props"
+ imports += [
+ f'<Import Project="$(MSBuildProjectDirectory)\\{p}" Condition="Exists(\'$(MSBuildProjectDirectory)\\{p}\')"/>'
+ ]
+
+ section1 += [f"{godot_target}|{sln_plat} = {godot_target}|{sln_plat}"]
+
+ section2 += [
+ f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.ActiveCfg = {godot_target}|{proj_plat}",
+ f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.Build.0 = {godot_target}|{proj_plat}",
+ ]
+
+ section1 = sorted(section1)
+ section2 = sorted(section2)
+
+ proj_template = open("misc/msvs/vcxproj.template", "r").read()
+
+ proj_template = proj_template.replace("%%UUID%%", proj_uuid)
+ proj_template = proj_template.replace("%%CONFS%%", "\n ".join(configurations))
+ proj_template = proj_template.replace("%%IMPORTS%%", "\n ".join(imports))
+ proj_template = proj_template.replace("%%DEFAULT_ITEMS%%", "\n ".join(all_items))
+ proj_template = proj_template.replace("%%PROPERTIES%%", "\n ".join(properties))
+
+ with open(f"{project_name}.vcxproj", "w") as f:
+ f.write(proj_template)
+
+ sln_template = open("misc/msvs/sln.template", "r").read()
+ sln_template = sln_template.replace("%%NAME%%", project_name)
+ sln_template = sln_template.replace("%%UUID%%", proj_uuid)
+ sln_template = sln_template.replace("%%SLNUUID%%", sln_uuid)
+ sln_template = sln_template.replace("%%SECTION1%%", "\n ".join(section1))
+ sln_template = sln_template.replace("%%SECTION2%%", "\n ".join(section2))
+ with open(f"{project_name}.sln", "w") as f:
+ f.write(sln_template)
+
+ if get_bool(original_args, "vsproj_gen_only", True):
+ sys.exit()
diff --git a/misc/msvs/props.template b/misc/msvs/props.template
new file mode 100644
index 0000000000..9ecd49a25e
--- /dev/null
+++ b/misc/msvs/props.template
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%%VSCONF%%'">
+ <NMakeBuildCommandLine>%%BUILD%%</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>%%REBUILD%%</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine>%%CLEAN%%</NMakeCleanCommandLine>
+ <NMakeOutput>%%OUTPUT%%</NMakeOutput>
+ <NMakePreprocessorDefinitions>%%DEFINES%%</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>%%INCLUDES%%</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+ <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+ <AdditionalOptions>%%OPTIONS%%</AdditionalOptions>
+ </PropertyGroup>
+ <PropertyGroup Condition="%%CONDITION%%">
+ %%PROPERTIES%%
+ </PropertyGroup>
+ <ItemGroup Condition="%%CONDITION%%">
+ %%EXTRA_ITEMS%%
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/misc/msvs/sln.template b/misc/msvs/sln.template
new file mode 100644
index 0000000000..7d05548c6e
--- /dev/null
+++ b/misc/msvs/sln.template
@@ -0,0 +1,20 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34221.43
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%%NAME%%", "%%NAME%%.vcxproj", "{%%UUID%%}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ %%SECTION1%%
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ %%SECTION2%%
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {%%SLNUUID%%}
+ EndGlobalSection
+EndGlobal
diff --git a/misc/msvs/vcxproj.filters.template b/misc/msvs/vcxproj.filters.template
new file mode 100644
index 0000000000..d57eeee811
--- /dev/null
+++ b/misc/msvs/vcxproj.filters.template
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>%%UUID1%%</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>%%UUID2%%</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>%%UUID3%%</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Scripts">
+ <UniqueIdentifier>%%UUID4%%</UniqueIdentifier>
+ </Filter>
+ %%FILTERS%%
+ </ItemGroup>
+ <ItemGroup>
+ %%COMPILES%%
+ </ItemGroup>
+ <ItemGroup>
+ %%INCLUDES%%
+ </ItemGroup>
+ <ItemGroup>
+ %%OTHERS%%
+ </ItemGroup>
+</Project>
+<!-- CHECKSUM
+%%HASH%%
+--> \ No newline at end of file
diff --git a/misc/msvs/vcxproj.template b/misc/msvs/vcxproj.template
new file mode 100644
index 0000000000..a1cf22bfb9
--- /dev/null
+++ b/misc/msvs/vcxproj.template
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ %%CONFS%%
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{%%UUID%%}</ProjectGuid>
+ <RootNamespace>godot</RootNamespace>
+ <Keyword>MakeFileProj</Keyword>
+ <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
+ </PropertyGroup>
+ <PropertyGroup>
+ %%PROPERTIES%%
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <PlatformToolset>v143</PlatformToolset>
+ <OutDir>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
+ <LayoutDir>$(OutDir)\Layout</LayoutDir>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <ActiveProjectItemList></ActiveProjectItemList>
+ </PropertyGroup>
+ %%IMPORTS%%
+ <ItemGroup Condition="'$(IncludeListImported)'==''">
+ %%DEFAULT_ITEMS%%
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/misc/scripts/black_format.sh b/misc/scripts/black_format.sh
index 3a64284eb6..48dc14c734 100755
--- a/misc/scripts/black_format.sh
+++ b/misc/scripts/black_format.sh
@@ -20,7 +20,7 @@ fi
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
-printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
exit 1
diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh
index 40d94d4276..8b59519606 100755
--- a/misc/scripts/clang_format.sh
+++ b/misc/scripts/clang_format.sh
@@ -47,7 +47,7 @@ fi
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
-printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
exit 1
diff --git a/misc/scripts/clang_tidy.sh b/misc/scripts/clang_tidy.sh
index c4811b903c..0c6998b491 100755
--- a/misc/scripts/clang_tidy.sh
+++ b/misc/scripts/clang_tidy.sh
@@ -27,7 +27,7 @@ fi
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
-printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
exit 1
diff --git a/misc/scripts/dotnet_format.sh b/misc/scripts/dotnet_format.sh
index cac00f5cb1..e2b4ba5e43 100755
--- a/misc/scripts/dotnet_format.sh
+++ b/misc/scripts/dotnet_format.sh
@@ -31,7 +31,7 @@ fi
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
-printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
exit 1
diff --git a/misc/scripts/file_format.sh b/misc/scripts/file_format.sh
index 94a3affbd7..ad58657883 100755
--- a/misc/scripts/file_format.sh
+++ b/misc/scripts/file_format.sh
@@ -82,7 +82,7 @@ then
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
- printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+ printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
fi
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
diff --git a/misc/scripts/header_guards.sh b/misc/scripts/header_guards.sh
index ce0b3f334d..a79ccd4bee 100755
--- a/misc/scripts/header_guards.sh
+++ b/misc/scripts/header_guards.sh
@@ -81,7 +81,7 @@ fi
# A diff has been created, notify the user, clean up, and exit.
printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n"
# Perl commands replace trailing spaces with `·` and tabs with `<TAB>`.
-printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
+printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge'
printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n"
exit 1
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index 12b4ac540d..02e3a11964 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -49,11 +49,11 @@ void MultiplayerSynchronizer::_stop() {
}
#endif
root_node_cache = ObjectID();
- reset();
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->object_configuration_remove(node, this);
}
+ reset();
}
void MultiplayerSynchronizer::_start() {
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 31307e648d..35ff62dd06 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -189,9 +189,6 @@ void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) {
spawned_nodes.insert(oid);
if (_has_authority(spawner)) {
- if (tobj.net_id == 0) {
- tobj.net_id = ++last_net_id;
- }
_update_spawn_visibility(0, oid);
}
}
@@ -249,6 +246,7 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
uint32_t net_id = pending_sync_net_ids[0];
pending_sync_net_ids.pop_front();
peers_info[pending_spawn_remote].recv_sync_ids[net_id] = sync->get_instance_id();
+ sync->set_net_id(net_id);
// Try to apply spawn state (before ready).
if (pending_buffer_size > 0) {
@@ -472,9 +470,13 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpa
ERR_FAIL_COND_V(!multiplayer || !p_node || !p_spawner, ERR_BUG);
const ObjectID oid = p_node->get_instance_id();
- const TrackedNode *tnode = tracked_nodes.getptr(oid);
+ TrackedNode *tnode = tracked_nodes.getptr(oid);
ERR_FAIL_NULL_V(tnode, ERR_INVALID_PARAMETER);
+ if (tnode->net_id == 0) {
+ // Ensure the node has an ID.
+ tnode->net_id = ++last_net_id;
+ }
uint32_t nid = tnode->net_id;
ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
index e795311651..1bc3a1a3fc 100644
--- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
@@ -75,6 +75,12 @@
Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer.
</description>
</method>
+ <method name="is_environment_blend_mode_alpha_supported">
+ <return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" />
+ <description>
+ Returns [enum OpenXRAPIExtension.OpenXRAlphaBlendModeSupport] denoting if [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really support, emulated or not supported at all.
+ </description>
+ </method>
<method name="is_initialized">
<return type="bool" />
<description>
@@ -94,6 +100,20 @@
Returns [code]true[/code] if OpenXR is enabled.
</description>
</method>
+ <method name="register_composition_layer_provider">
+ <return type="void" />
+ <param index="0" name="extension" type="OpenXRExtensionWrapperExtension" />
+ <description>
+ Registers the given extension as a composition layer provider.
+ </description>
+ </method>
+ <method name="set_emulate_environment_blend_mode_alpha_blend">
+ <return type="void" />
+ <param index="0" name="enabled" type="bool" />
+ <description>
+ If set to [code]true[/code], an OpenXR extension is loaded which is capable of emulating the [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] blend mode.
+ </description>
+ </method>
<method name="transform_from_pose">
<return type="Transform3D" />
<param index="0" name="pose" type="const void*" />
@@ -101,6 +121,13 @@
Creates a [Transform3D] from an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html]XrPosef[/url].
</description>
</method>
+ <method name="unregister_composition_layer_provider">
+ <return type="void" />
+ <param index="0" name="extension" type="OpenXRExtensionWrapperExtension" />
+ <description>
+ Unregisters the given extension as a composition layer provider.
+ </description>
+ </method>
<method name="xr_result">
<return type="bool" />
<param index="0" name="result" type="int" />
@@ -111,4 +138,15 @@
</description>
</method>
</methods>
+ <constants>
+ <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE" value="0" enum="OpenXRAlphaBlendModeSupport">
+ Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] isn't supported at all.
+ </constant>
+ <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL" value="1" enum="OpenXRAlphaBlendModeSupport">
+ Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really supported.
+ </constant>
+ <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING" value="2" enum="OpenXRAlphaBlendModeSupport">
+ Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is emulated.
+ </constant>
+ </constants>
</class>
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index fa93704a0a..b923d9244d 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -9,6 +9,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="_get_composition_layer" qualifiers="virtual">
+ <return 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].
+ </description>
+ </method>
<method name="_get_requested_extensions" qualifiers="virtual">
<return type="Dictionary" />
<description>
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index 23238cabb9..5ad7a97eca 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -39,6 +39,7 @@ 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(_on_register_metadata);
GDVIRTUAL_BIND(_on_before_instance_created);
GDVIRTUAL_BIND(_on_instance_created, "instance");
@@ -128,6 +129,16 @@ void *OpenXRExtensionWrapperExtension::set_hand_joint_locations_and_get_next_poi
return nullptr;
}
+XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer() {
+ uint64_t pointer;
+
+ if (GDVIRTUAL_CALL(_get_composition_layer, pointer)) {
+ return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer);
+ }
+
+ return nullptr;
+}
+
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 6acf229e16..4d8b19f4fd 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -39,7 +39,7 @@
#include "core/os/thread_safe.h"
#include "core/variant/native_ptr.h"
-class OpenXRExtensionWrapperExtension : public Object, OpenXRExtensionWrapper {
+class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRExtensionWrapperExtension, Object);
protected:
@@ -59,6 +59,7 @@ 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;
//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>);
@@ -66,6 +67,7 @@ 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);
virtual void on_register_metadata() override;
virtual void on_before_instance_created() override;
diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp
deleted file mode 100644
index 3da0ffd9c7..0000000000
--- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/**************************************************************************/
-/* openxr_fb_passthrough_extension_wrapper.cpp */
-/**************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/**************************************************************************/
-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/**************************************************************************/
-
-#include "openxr_fb_passthrough_extension_wrapper.h"
-
-#include "core/os/os.h"
-#include "scene/main/viewport.h"
-#include "scene/main/window.h"
-
-using namespace godot;
-
-OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr;
-
-OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() {
- return singleton;
-}
-
-OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper() {
- singleton = this;
-}
-
-OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() {
- cleanup();
-}
-
-HashMap<String, bool *> OpenXRFbPassthroughExtensionWrapper::get_requested_extensions() {
- HashMap<String, bool *> request_extensions;
-
- request_extensions[XR_FB_PASSTHROUGH_EXTENSION_NAME] = &fb_passthrough_ext;
- request_extensions[XR_FB_TRIANGLE_MESH_EXTENSION_NAME] = &fb_triangle_mesh_ext;
-
- return request_extensions;
-}
-
-void OpenXRFbPassthroughExtensionWrapper::cleanup() {
- fb_passthrough_ext = false;
- fb_triangle_mesh_ext = false;
-}
-
-Viewport *OpenXRFbPassthroughExtensionWrapper::get_main_viewport() {
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
- if (!main_loop) {
- print_error("Unable to retrieve main loop");
- return nullptr;
- }
-
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
- if (!scene_tree) {
- print_error("Unable to retrieve scene tree");
- return nullptr;
- }
-
- Viewport *viewport = scene_tree->get_root()->get_viewport();
- return viewport;
-}
-
-void OpenXRFbPassthroughExtensionWrapper::on_instance_created(const XrInstance instance) {
- if (fb_passthrough_ext) {
- bool result = initialize_fb_passthrough_extension(instance);
- if (!result) {
- print_error("Failed to initialize fb_passthrough extension");
- fb_passthrough_ext = false;
- }
- }
-
- if (fb_triangle_mesh_ext) {
- bool result = initialize_fb_triangle_mesh_extension(instance);
- if (!result) {
- print_error("Failed to initialize fb_triangle_mesh extension");
- fb_triangle_mesh_ext = false;
- }
- }
-
- if (fb_passthrough_ext) {
- OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
- }
-}
-
-bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() {
- return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE;
-}
-
-bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() {
- if (passthrough_handle == XR_NULL_HANDLE) {
- return false;
- }
-
- if (is_passthrough_enabled()) {
- return true;
- }
-
- // Start the passthrough feature
- XrResult result = xrPassthroughStartFB(passthrough_handle);
- if (!is_valid_passthrough_result(result, "Failed to start passthrough")) {
- stop_passthrough();
- return false;
- }
-
- // Create the passthrough layer
- XrPassthroughLayerCreateInfoFB passthrough_layer_config = {
- XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB,
- nullptr,
- passthrough_handle,
- XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB,
- XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB,
- };
- result = xrCreatePassthroughLayerFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_layer_config, &passthrough_layer);
- if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) {
- stop_passthrough();
- return false;
- }
-
- // Check if the the viewport has transparent background
- Viewport *viewport = get_main_viewport();
- if (viewport && !viewport->has_transparent_background()) {
- print_error("Main viewport doesn't have transparent background! Passthrough may not properly render.");
- }
-
- return true;
-}
-
-void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) {
- if (fb_passthrough_ext) {
- // Create the passthrough feature and start it.
- XrPassthroughCreateInfoFB passthrough_create_info = {
- XR_TYPE_PASSTHROUGH_CREATE_INFO_FB,
- nullptr,
- 0,
- };
-
- XrResult result = xrCreatePassthroughFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_create_info, &passthrough_handle);
- if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to create passthrough")) {
- passthrough_handle = XR_NULL_HANDLE;
- return;
- }
- }
-}
-
-XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() {
- if (is_passthrough_enabled()) {
- composition_passthrough_layer.layerHandle = passthrough_layer;
- return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer;
- } else {
- return nullptr;
- }
-}
-
-void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() {
- if (!fb_passthrough_ext) {
- return;
- }
-
- XrResult result;
- if (passthrough_layer != XR_NULL_HANDLE) {
- // Destroy the layer
- result = xrDestroyPassthroughLayerFB(passthrough_layer);
- OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough layer");
- passthrough_layer = XR_NULL_HANDLE;
- }
-
- if (passthrough_handle != XR_NULL_HANDLE) {
- result = xrPassthroughPauseFB(passthrough_handle);
- OpenXRAPI::get_singleton()->xr_result(result, "Unable to stop passthrough feature");
- }
-}
-
-void OpenXRFbPassthroughExtensionWrapper::on_session_destroyed() {
- if (fb_passthrough_ext) {
- stop_passthrough();
-
- XrResult result;
- if (passthrough_handle != XR_NULL_HANDLE) {
- result = xrDestroyPassthroughFB(passthrough_handle);
- OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough feature");
- passthrough_handle = XR_NULL_HANDLE;
- }
- }
-}
-
-void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() {
- if (fb_passthrough_ext) {
- OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
- }
- cleanup();
-}
-
-bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_passthrough_extension(const XrInstance p_instance) {
- ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
-
- EXT_INIT_XR_FUNC_V(xrCreatePassthroughFB);
- EXT_INIT_XR_FUNC_V(xrDestroyPassthroughFB);
- EXT_INIT_XR_FUNC_V(xrPassthroughStartFB);
- EXT_INIT_XR_FUNC_V(xrPassthroughPauseFB);
- EXT_INIT_XR_FUNC_V(xrCreatePassthroughLayerFB);
- EXT_INIT_XR_FUNC_V(xrDestroyPassthroughLayerFB);
- EXT_INIT_XR_FUNC_V(xrPassthroughLayerPauseFB);
- EXT_INIT_XR_FUNC_V(xrPassthroughLayerResumeFB);
- EXT_INIT_XR_FUNC_V(xrPassthroughLayerSetStyleFB);
- EXT_INIT_XR_FUNC_V(xrCreateGeometryInstanceFB);
- EXT_INIT_XR_FUNC_V(xrDestroyGeometryInstanceFB);
- EXT_INIT_XR_FUNC_V(xrGeometryInstanceSetTransformFB);
-
- return true;
-}
-
-bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_triangle_mesh_extension(const XrInstance p_instance) {
- ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
-
- EXT_INIT_XR_FUNC_V(xrCreateTriangleMeshFB);
- EXT_INIT_XR_FUNC_V(xrDestroyTriangleMeshFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshGetVertexBufferFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshGetIndexBufferFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginUpdateFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshEndUpdateFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginVertexBufferUpdateFB);
- EXT_INIT_XR_FUNC_V(xrTriangleMeshEndVertexBufferUpdateFB);
-
- return true;
-}
diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h
deleted file mode 100644
index 045e424202..0000000000
--- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/**************************************************************************/
-/* openxr_fb_passthrough_extension_wrapper.h */
-/**************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/**************************************************************************/
-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/**************************************************************************/
-
-#ifndef OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
-#define OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
-
-#include "../openxr_api.h"
-#include "../util.h"
-#include "openxr_composition_layer_provider.h"
-#include "openxr_extension_wrapper.h"
-
-#include <map>
-
-class Viewport;
-
-// Wrapper for the set of Facebook XR passthrough extensions.
-class OpenXRFbPassthroughExtensionWrapper : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
-public:
- OpenXRFbPassthroughExtensionWrapper();
- ~OpenXRFbPassthroughExtensionWrapper();
-
- virtual HashMap<String, bool *> get_requested_extensions() override;
-
- void on_instance_created(const XrInstance instance) override;
-
- void on_session_created(const XrSession session) override;
-
- void on_session_destroyed() override;
-
- void on_instance_destroyed() override;
-
- XrCompositionLayerBaseHeader *get_composition_layer() override;
-
- bool is_passthrough_supported() {
- return fb_passthrough_ext;
- }
-
- bool is_passthrough_enabled();
-
- bool start_passthrough();
-
- void stop_passthrough();
-
- static OpenXRFbPassthroughExtensionWrapper *get_singleton();
-
-private:
- // Create a passthrough feature
- EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughFB,
- (XrSession), session,
- (const XrPassthroughCreateInfoFB *), create_info,
- (XrPassthroughFB *), feature_out)
-
- // Destroy a previously created passthrough feature
- EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughFB, (XrPassthroughFB), feature)
-
- //*** Passthrough feature state management functions *********
- // Start the passthrough feature
- EXT_PROTO_XRRESULT_FUNC1(xrPassthroughStartFB, (XrPassthroughFB), passthrough)
- // Pause the passthrough feature
- EXT_PROTO_XRRESULT_FUNC1(xrPassthroughPauseFB, (XrPassthroughFB), passthrough)
-
- EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughLayerFB, (XrSession), session,
- (const XrPassthroughLayerCreateInfoFB *), config,
- (XrPassthroughLayerFB *), layer_out)
-
- EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughLayerFB, (XrPassthroughLayerFB), layer)
-
- EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerPauseFB, (XrPassthroughLayerFB), layer)
- EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerResumeFB, (XrPassthroughLayerFB), layer)
-
- // Set the style of an existing passthrough layer. If the enabled feature set
- // doesn’t change, this is a lightweight operation that can be called in every
- // frame to animate the style. Changes that may incur a bigger cost:
- // - Enabling/disabling the color mapping, or changing the type of mapping
- // (monochromatic to RGBA or back).
- // - Changing `textureOpacityFactor` from 0 to non-zero or vice versa
- // - Changing `edgeColor[3]` from 0 to non-zero or vice versa
- // NOTE: For XR_FB_passthrough, all color values are treated as linear.
- EXT_PROTO_XRRESULT_FUNC2(xrPassthroughLayerSetStyleFB,
- (XrPassthroughLayerFB), layer,
- (const XrPassthroughStyleFB *), style)
-
- // Create a geometry instance to be used as a projection surface for passthrough.
- // A geometry instance assigns a triangle mesh as part of the specified layer's
- // projection surface.
- // The operation is only valid if the passthrough layer's purpose has been set to
- // `XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB`. Otherwise, the call this function will
- // result in an error. In the specified layer, Passthrough will be visible where the view
- // is covered by the user-specified geometries.
- //
- // A triangle mesh object can be instantiated multiple times - in the same or different layers'
- // projection surface. Each instantiation has its own transformation, which
- // can be updated using `xrGeometryInstanceSetTransformFB`.
- EXT_PROTO_XRRESULT_FUNC3(xrCreateGeometryInstanceFB,
- (XrSession), session,
- (const XrGeometryInstanceCreateInfoFB *), create_info,
- (XrGeometryInstanceFB *), out_geometry_instance)
-
- // Destroys a previously created geometry instance from passthrough rendering.
- // This removes the geometry instance from passthrough rendering.
- // The operation has no effect on other instances or the underlying mesh.
- EXT_PROTO_XRRESULT_FUNC1(xrDestroyGeometryInstanceFB, (XrGeometryInstanceFB), instance)
-
- // Update the transformation of a passthrough geometry instance.
- EXT_PROTO_XRRESULT_FUNC2(xrGeometryInstanceSetTransformFB,
- (XrGeometryInstanceFB), instance,
- (const XrGeometryInstanceTransformFB *), transformation)
-
- // Create a triangle mesh geometry object.
- // Depending on the behavior flags, the mesh could be created immutable (data is assigned
- // at creation and cannot be changed) or mutable (the mesh is created empty and can be updated
- // by calling begin/end update functions).
- EXT_PROTO_XRRESULT_FUNC3(xrCreateTriangleMeshFB,
- (XrSession), session,
- (const XrTriangleMeshCreateInfoFB *), create_info,
- (XrTriangleMeshFB *), out_triangle_mesh)
-
- // Destroy an `XrTriangleMeshFB` object along with its data. The mesh buffers must not be
- // accessed anymore after their parent mesh object has been destroyed.
- EXT_PROTO_XRRESULT_FUNC1(xrDestroyTriangleMeshFB, (XrTriangleMeshFB), mesh)
-
- // Retrieve a pointer to the vertex buffer. The vertex buffer is structured as an array of 3 floats
- // per vertex representing x, y, and z: `[x0, y0, z0, x1, y1, z1, ...]`. The size of the buffer is
- // `maxVertexCount * 3` floats. The application must call `xrTriangleMeshBeginUpdateFB` or
- // `xrTriangleMeshBeginVertexBufferUpdateFB` before making modifications to the vertex
- // buffer. The buffer location is guaranteed to remain constant over the lifecycle of the mesh
- // object.
- EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetVertexBufferFB,
- (XrTriangleMeshFB), mesh,
- (XrVector3f **), out_vertex_buffer)
-
- // Retrieve the index buffer that defines the topology of the triangle mesh. Each triplet of
- // consecutive elements point to three vertices in the vertex buffer and thus form a triangle. The
- // size of each element is `indexElementSize` bytes, and thus the size of the buffer is
- // `maxTriangleCount * 3 * indexElementSize` bytes. The application must call
- // `xrTriangleMeshBeginUpdateFB` before making modifications to the index buffer. The buffer
- // location is guaranteed to remain constant over the lifecycle of the mesh object.
- EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetIndexBufferFB,
- (XrTriangleMeshFB), mesh,
- (uint32_t **), out_index_buffer)
-
- // Begin updating the mesh buffer data. The application must call this function before it makes any
- // modifications to the buffers retrieved by `xrTriangleMeshGetVertexBufferFB` and
- // `xrTriangleMeshGetIndexBufferFB`. If only the vertex buffer needs to be updated,
- // `xrTriangleMeshBeginVertexBufferUpdateFB` can be used instead. To commit the
- // modifications, the application must call `xrTriangleMeshEndUpdateFB`.
- EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshBeginUpdateFB, (XrTriangleMeshFB), mesh)
-
- // Signal the API that the application has finished updating the mesh buffers after a call to
- // `xrTriangleMeshBeginUpdateFB`. `vertexCount` and `triangleCount` specify the actual
- // number of primitives that make up the mesh after the update. They must be larger than zero but
- // smaller or equal to the maximum counts defined at create time. Buffer data beyond these counts
- // is ignored.
- EXT_PROTO_XRRESULT_FUNC3(xrTriangleMeshEndUpdateFB,
- (XrTriangleMeshFB), mesh,
- (uint32_t), vertexCount,
- (uint32_t), triangle_count)
-
- // Update the vertex positions of a triangle mesh. Can only be called once the mesh topology has
- // been set using `xrTriangleMeshBeginUpdateFB`/`xrTriangleMeshEndUpdateFB`. The
- // vertex count is defined by the last invocation to `xrTriangleMeshEndUpdateFB`. Once the
- // modification is done, `xrTriangleMeshEndVertexBufferUpdateFB` must be called.
- EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshBeginVertexBufferUpdateFB,
- (XrTriangleMeshFB), mesh,
- (uint32_t *), out_vertex_count)
-
- // Signal the API that the contents of the vertex buffer data has been updated
- // after a call to `xrTriangleMeshBeginVertexBufferUpdateFB`.
- EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshEndVertexBufferUpdateFB, (XrTriangleMeshFB), mesh)
-
- bool initialize_fb_passthrough_extension(const XrInstance instance);
-
- bool initialize_fb_triangle_mesh_extension(const XrInstance instance);
-
- void cleanup();
-
- // TODO: Temporary workaround (https://github.com/GodotVR/godot_openxr/issues/138)
- // Address a bug in the passthrough api where XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB is
- // returned even when the operation is valid on Meta Quest devices.
- // The issue should be addressed on that platform in OS release v37.
- inline bool is_valid_passthrough_result(XrResult result, const char *format) {
- return OpenXRAPI::get_singleton()->xr_result(result, format) || result == XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB;
- }
-
- Viewport *get_main_viewport();
-
- static OpenXRFbPassthroughExtensionWrapper *singleton;
-
- bool fb_passthrough_ext = false; // required for any passthrough functionality
- bool fb_triangle_mesh_ext = false; // only use for projected passthrough
-
- XrPassthroughFB passthrough_handle = XR_NULL_HANDLE;
- XrPassthroughLayerFB passthrough_layer = XR_NULL_HANDLE;
-
- XrCompositionLayerPassthroughFB composition_passthrough_layer = {
- XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB,
- nullptr,
- XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT,
- XR_NULL_HANDLE,
- XR_NULL_HANDLE,
- };
-};
-
-#endif // OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 491a419525..80ddfe703f 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -56,7 +56,6 @@
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
-#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
#include "extensions/openxr_fb_update_swapchain_extension.h"
#ifdef ANDROID_ENABLED
@@ -3122,11 +3121,30 @@ bool OpenXRAPI::is_environment_blend_mode_supported(XrEnvironmentBlendMode p_ble
}
bool OpenXRAPI::set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode) {
+ if (emulate_environment_blend_mode_alpha_blend && p_blend_mode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) {
+ requested_environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;
+ environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+ return true;
+ }
// We allow setting this when not initialized and will check if it is supported when initializing.
// After OpenXR is initialized we verify we're setting a supported blend mode.
- if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) {
+ else if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) {
+ requested_environment_blend_mode = p_blend_mode;
environment_blend_mode = p_blend_mode;
return true;
}
return false;
}
+
+void OpenXRAPI::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) {
+ emulate_environment_blend_mode_alpha_blend = p_enabled;
+}
+
+OpenXRAPI::OpenXRAlphaBlendModeSupport OpenXRAPI::is_environment_blend_mode_alpha_blend_supported() {
+ if (is_environment_blend_mode_supported(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND)) {
+ return OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL;
+ } else if (emulate_environment_blend_mode_alpha_blend) {
+ return OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING;
+ }
+ return OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE;
+}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 5e5a3d4663..d39e6e0b2e 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -104,8 +104,10 @@ private:
// blend mode
XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+ XrEnvironmentBlendMode requested_environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
uint32_t num_supported_environment_blend_modes = 0;
XrEnvironmentBlendMode *supported_environment_blend_modes = nullptr;
+ bool emulate_environment_blend_mode_alpha_blend = false;
// state
XrInstance instance = XR_NULL_HANDLE;
@@ -429,7 +431,16 @@ public:
const XrEnvironmentBlendMode *get_supported_environment_blend_modes(uint32_t &count);
bool is_environment_blend_mode_supported(XrEnvironmentBlendMode p_blend_mode) const;
bool set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode);
- XrEnvironmentBlendMode get_environment_blend_mode() const { return environment_blend_mode; }
+ XrEnvironmentBlendMode get_environment_blend_mode() const { return requested_environment_blend_mode; }
+
+ enum OpenXRAlphaBlendModeSupport {
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE = 0,
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL = 1,
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING = 2,
+ };
+
+ void set_emulate_environment_blend_mode_alpha_blend(bool p_enabled);
+ OpenXRAlphaBlendModeSupport is_environment_blend_mode_alpha_blend_supported();
OpenXRAPI();
~OpenXRAPI();
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
index f0f0835f78..d9e282e218 100644
--- a/modules/openxr/openxr_api_extension.cpp
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -30,6 +30,8 @@
#include "openxr_api_extension.h"
+#include "extensions/openxr_extension_wrapper_extension.h"
+
void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance"), &OpenXRAPIExtension::get_instance);
ClassDB::bind_method(D_METHOD("get_system_id"), &OpenXRAPIExtension::get_system_id);
@@ -48,6 +50,16 @@ void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space);
ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
+
+ ClassDB::bind_method(D_METHOD("register_composition_layer_provider", "extension"), &OpenXRAPIExtension::register_composition_layer_provider);
+ ClassDB::bind_method(D_METHOD("unregister_composition_layer_provider", "extension"), &OpenXRAPIExtension::unregister_composition_layer_provider);
+
+ ClassDB::bind_method(D_METHOD("set_emulate_environment_blend_mode_alpha_blend", "enabled"), &OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend);
+ ClassDB::bind_method(D_METHOD("is_environment_blend_mode_alpha_supported"), &OpenXRAPIExtension::is_environment_blend_mode_alpha_blend_supported);
+
+ BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE);
+ BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL);
+ BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING);
}
uint64_t OpenXRAPIExtension::get_instance() {
@@ -126,5 +138,25 @@ bool OpenXRAPIExtension::can_render() {
return OpenXRAPI::get_singleton()->can_render();
}
+void OpenXRAPIExtension::register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+ OpenXRAPI::get_singleton()->register_composition_layer_provider(p_extension);
+}
+
+void OpenXRAPIExtension::unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+ OpenXRAPI::get_singleton()->unregister_composition_layer_provider(p_extension);
+}
+
+void OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+ OpenXRAPI::get_singleton()->set_emulate_environment_blend_mode_alpha_blend(p_enabled);
+}
+
+OpenXRAPIExtension::OpenXRAlphaBlendModeSupport OpenXRAPIExtension::is_environment_blend_mode_alpha_blend_supported() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE);
+ return (OpenXRAPIExtension::OpenXRAlphaBlendModeSupport)OpenXRAPI::get_singleton()->is_environment_blend_mode_alpha_blend_supported();
+}
+
OpenXRAPIExtension::OpenXRAPIExtension() {
}
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
index 98f87c7aa1..82344c1d06 100644
--- a/modules/openxr/openxr_api_extension.h
+++ b/modules/openxr/openxr_api_extension.h
@@ -38,6 +38,8 @@
#include "core/os/thread_safe.h"
#include "core/variant/native_ptr.h"
+class OpenXRExtensionWrapperExtension;
+
class OpenXRAPIExtension : public RefCounted {
GDCLASS(OpenXRAPIExtension, RefCounted);
@@ -70,7 +72,21 @@ public:
int64_t get_next_frame_time();
bool can_render();
+ void register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension);
+ void unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension);
+
+ enum OpenXRAlphaBlendModeSupport {
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE = 0,
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL = 1,
+ OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING = 2,
+ };
+
+ void set_emulate_environment_blend_mode_alpha_blend(bool p_enabled);
+ OpenXRAlphaBlendModeSupport is_environment_blend_mode_alpha_blend_supported();
+
OpenXRAPIExtension();
};
+VARIANT_ENUM_CAST(OpenXRAPIExtension::OpenXRAlphaBlendModeSupport);
+
#endif // OPENXR_API_EXTENSION_H
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index ebcd331f3d..05c53ad52f 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -1158,21 +1158,19 @@ void OpenXRInterface::end_frame() {
}
bool OpenXRInterface::is_passthrough_supported() {
- return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_supported();
+ return get_supported_environment_blend_modes().find(XR_ENV_BLEND_MODE_ALPHA_BLEND);
}
bool OpenXRInterface::is_passthrough_enabled() {
- return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_enabled();
+ return get_environment_blend_mode() == XR_ENV_BLEND_MODE_ALPHA_BLEND;
}
bool OpenXRInterface::start_passthrough() {
- return passthrough_wrapper != nullptr && passthrough_wrapper->start_passthrough();
+ return set_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND);
}
void OpenXRInterface::stop_passthrough() {
- if (passthrough_wrapper) {
- passthrough_wrapper->stop_passthrough();
- }
+ set_environment_blend_mode(XR_ENV_BLEND_MODE_OPAQUE);
}
Array OpenXRInterface::get_supported_environment_blend_modes() {
@@ -1204,6 +1202,11 @@ Array OpenXRInterface::get_supported_environment_blend_modes() {
WARN_PRINT("Unsupported blend mode found: " + String::num_int64(int64_t(env_blend_modes[i])));
}
}
+
+ if (openxr_api->is_environment_blend_mode_alpha_blend_supported() == OpenXRAPI::OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING) {
+ modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);
+ }
+
return modes;
}
@@ -1422,8 +1425,6 @@ OpenXRInterface::OpenXRInterface() {
_set_default_pos(head_transform, 1.0, 0);
_set_default_pos(transform_for_view[0], 1.0, 1);
_set_default_pos(transform_for_view[1], 1.0, 2);
-
- passthrough_wrapper = OpenXRFbPassthroughExtensionWrapper::get_singleton();
}
OpenXRInterface::~OpenXRInterface() {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index ca95fdf04d..aee9751d6b 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -32,7 +32,6 @@
#define OPENXR_INTERFACE_H
#include "action_map/openxr_action_map.h"
-#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
#include "extensions/openxr_hand_tracking_extension.h"
#include "openxr_api.h"
@@ -49,7 +48,6 @@ private:
OpenXRAPI *openxr_api = nullptr;
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
- OpenXRFbPassthroughExtensionWrapper *passthrough_wrapper = nullptr;
// At a minimum we need a tracker for our head
Ref<XRPositionalTracker> head;
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 04411a0c57..a0a93c8694 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -44,7 +44,6 @@
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
-#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
#include "extensions/openxr_hand_tracking_extension.h"
#include "extensions/openxr_htc_controller_extension.h"
#include "extensions/openxr_htc_vive_tracker_extension.h"
@@ -114,7 +113,6 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHuaweiControllerExtension));
- OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRWMRControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRML2ControllerExtension));
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 7aaf70e625..6010d4ba76 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -7,6 +7,8 @@ from pathlib import Path
from platform_methods import run_in_subprocess
import platform_windows_builders
+sources = []
+
common_win = [
"godot_windows.cpp",
"crash_handler_windows.cpp",
@@ -43,7 +45,8 @@ res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file)
-sources = common_win + res_obj
+env.add_source_files(sources, common_win)
+sources += res_obj
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
arrange_program_clean(prog)
@@ -65,6 +68,7 @@ if env["windows_subsystem"] == "gui":
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
arrange_program_clean(prog_wrap)
env_wrap.Depends(prog_wrap, prog)
+ sources += common_win_wrap + res_wrap_obj
# Microsoft Visual Studio Project Generation
if env["vsproj"]:
@@ -134,3 +138,5 @@ if not os.getenv("VCINSTALLDIR"):
env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw))
if env["windows_subsystem"] == "gui":
env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw))
+
+env.platform_sources += sources
diff --git a/platform/windows/msvs.py b/platform/windows/msvs.py
new file mode 100644
index 0000000000..2d5ebe811a
--- /dev/null
+++ b/platform/windows/msvs.py
@@ -0,0 +1,20 @@
+import methods
+
+
+# Tuples with the name of the arch that will be used in VS, mapped to our internal arch names.
+# For Windows platforms, Win32 is what VS wants. For other platforms, it can be different.
+def get_platforms():
+ return [("Win32", "x86_32"), ("x64", "x86_64")]
+
+
+def get_configurations():
+ return ["editor", "template_debug", "template_release"]
+
+
+def get_build_prefix(env):
+ batch_file = methods.find_visual_c_batch_file(env)
+ return [
+ "set &quot;plat=$(PlatformTarget)&quot;",
+ "(if &quot;$(PlatformTarget)&quot;==&quot;x64&quot; (set &quot;plat=x86_amd64&quot;))",
+ f"call &quot;{batch_file}&quot; !plat!",
+ ]
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 1988744340..0bf1ad5b06 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -862,7 +862,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.12.3 (9d79f0ccef632fd3b43b8ea02def529b6a8d2288, 2024)
+- Version: 0.12.4 (331839d49368e19ca15f35abee5ac541dbf23637, 2024)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index ef65b1500d..e50971e297 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -9,5 +9,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
-#define THORVG_VERSION_STRING "0.12.3"
+#define THORVG_VERSION_STRING "0.12.4"
#endif
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index f618a3c827..67c87ba149 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -51,8 +51,8 @@
#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
#include <cstring>
-#include <math.h>
#include <ctype.h>
+#include "tvgMath.h"
#include "tvgShape.h"
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
@@ -471,9 +471,16 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
}
case 'a':
case 'A': {
- _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
- *cur = *curCtl = {arr[5], arr[6]};
- *isQuadratic = false;
+ if (mathZero(arr[0]) || mathZero(arr[1])) {
+ Point p = {arr[5], arr[6]};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ *cur = {arr[5], arr[6]};
+ } else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) {
+ _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]);
+ *cur = *curCtl = {arr[5], arr[6]};
+ *isQuadratic = false;
+ }
break;
}
default: {
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
index 4645c4a58b..c187af38d5 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
@@ -63,7 +63,7 @@ struct SwTask : Task
return region;
}
- virtual bool dispose() = 0;
+ virtual void dispose() = 0;
virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0;
@@ -196,10 +196,9 @@ struct SwShapeTask : SwTask
shapeDelOutline(&shape, mpool, tid);
}
- bool dispose() override
+ void dispose() override
{
shapeFree(&shape);
- return true;
}
};
@@ -250,10 +249,9 @@ struct SwSceneTask : SwTask
}
}
- bool dispose() override
+ void dispose() override
{
rleFree(sceneRle);
- return true;
}
};
@@ -318,10 +316,9 @@ struct SwImageTask : SwTask
imageDelOutline(&image, mpool, tid);
}
- bool dispose() override
+ void dispose() override
{
imageFree(&image);
- return true;
}
};
@@ -703,17 +700,15 @@ ColorSpace SwRenderer::colorSpace()
}
-bool SwRenderer::dispose(RenderData data)
+void SwRenderer::dispose(RenderData data)
{
auto task = static_cast<SwTask*>(data);
- if (!task) return true;
+ if (!task) return;
task->done();
task->dispose();
if (task->pushed) task->disposed = true;
else delete(task);
-
- return true;
}
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
index 83d942388f..02359e4a39 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
@@ -43,7 +43,7 @@ public:
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;
bool postRender() override;
- bool dispose(RenderData data) override;
+ void dispose(RenderData data) override;
RenderRegion region(RenderData data) override;
RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
index 45322c07b4..d3b715eab8 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
@@ -122,7 +122,9 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
Line cur = {dash.ptCur, *to};
auto len = _lineLength(cur.pt1, cur.pt2);
- if (len < dash.curLen) {
+ if (mathZero(len)) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ } else if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@@ -179,7 +181,9 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
auto len = bezLength(cur);
- if (len < dash.curLen) {
+ if (mathZero(len)) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ } else if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.h b/thirdparty/thorvg/src/renderer/tvgCanvas.h
index 25181de47e..2d87a7e0a1 100644
--- a/thirdparty/thorvg/src/renderer/tvgCanvas.h
+++ b/thirdparty/thorvg/src/renderer/tvgCanvas.h
@@ -33,14 +33,30 @@ struct Canvas::Impl
bool refresh = false; //if all paints should be updated by force.
bool drawing = false; //on drawing condition?
- Impl(RenderMethod* pRenderer):renderer(pRenderer)
+ Impl(RenderMethod* pRenderer) : renderer(pRenderer)
{
+ renderer->ref();
}
~Impl()
{
- clear(true);
- delete(renderer);
+ //make it sure any deffered jobs
+ if (renderer) {
+ renderer->sync();
+ renderer->clear();
+ }
+
+ clearPaints();
+
+ if (renderer && (renderer->unref() == 0)) delete(renderer);
+ }
+
+ void clearPaints()
+ {
+ for (auto paint : paints) {
+ if (P(paint)->unref() == 0) delete(paint);
+ }
+ paints.clear();
}
Result push(unique_ptr<Paint> paint)
@@ -62,15 +78,8 @@ struct Canvas::Impl
if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
//Free paints
- if (free) {
- for (auto paint : paints) {
- P(paint)->unref();
- if (paint->pImpl->dispose(*renderer) && P(paint)->refCnt == 0) {
- delete(paint);
- }
- }
- paints.clear();
- }
+ if (free) clearPaints();
+
drawing = false;
return Result::Success;
@@ -94,7 +103,7 @@ struct Canvas::Impl
//Optimize Me: Can we skip the searching?
for (auto paint2 : paints) {
if (paint2 == paint) {
- paint->pImpl->update(*renderer, nullptr, clips, 255, flag);
+ paint->pImpl->update(renderer, nullptr, clips, 255, flag);
return Result::Success;
}
}
@@ -102,7 +111,7 @@ struct Canvas::Impl
//Update all retained paint nodes
} else {
for (auto paint : paints) {
- paint->pImpl->update(*renderer, nullptr, clips, 255, flag);
+ paint->pImpl->update(renderer, nullptr, clips, 255, flag);
}
}
@@ -117,7 +126,7 @@ struct Canvas::Impl
bool rendered = false;
for (auto paint : paints) {
- if (paint->pImpl->render(*renderer)) rendered = true;
+ if (paint->pImpl->render(renderer)) rendered = true;
}
if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
index 563db3b44a..13ec4183d6 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
@@ -106,7 +106,7 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform,
}
-RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const
+RenderRegion Paint::Impl::bounds(RenderMethod* renderer) const
{
RenderRegion ret;
PAINT_METHOD(ret, bounds(renderer));
@@ -114,16 +114,6 @@ RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const
}
-bool Paint::Impl::dispose(RenderMethod& renderer)
-{
- if (compData) compData->target->pImpl->dispose(renderer);
-
- bool ret;
- PAINT_METHOD(ret, dispose(renderer));
- return ret;
-}
-
-
Iterator* Paint::Impl::iterator()
{
Iterator* ret;
@@ -198,7 +188,7 @@ bool Paint::Impl::translate(float x, float y)
}
-bool Paint::Impl::render(RenderMethod& renderer)
+bool Paint::Impl::render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
@@ -210,27 +200,33 @@ bool Paint::Impl::render(RenderMethod& renderer)
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
- cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
- if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
+ cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
+ if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
compData->target->pImpl->render(renderer);
}
}
- if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
+ if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
- renderer.blend(blendMethod);
+ renderer->blend(blendMethod);
bool ret;
PAINT_METHOD(ret, render(renderer));
- if (cmp) renderer.endComposite(cmp);
+ if (cmp) renderer->endComposite(cmp);
return ret;
}
-RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
+ if (this->renderer != renderer) {
+ if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!");
+ renderer->ref();
+ this->renderer = renderer;
+ }
+
if (renderFlag & RenderUpdateFlag::Transform) {
if (!rTransform) return nullptr;
rTransform->update();
@@ -265,9 +261,9 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
if (tryFastTrack) {
RenderRegion viewport2;
if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
- viewport = renderer.viewport();
+ viewport = renderer->viewport();
viewport2.intersect(viewport);
- renderer.viewport(viewport2);
+ renderer->viewport(viewport2);
target->pImpl->ctxFlag |= ContextFlag::FastTrack;
}
}
@@ -289,7 +285,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
/* 3. Composition Post Processing */
- if (compFastTrack) renderer.viewport(viewport);
+ if (compFastTrack) renderer->viewport(viewport);
else if (childClipper) clips.pop();
return rd;
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h
index 4ec9fa7f7e..c7eb68b198 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.h
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.h
@@ -50,16 +50,15 @@ namespace tvg
Paint* paint = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
- BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
+ RenderMethod* renderer = nullptr;
+ BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
uint8_t renderFlag = RenderUpdateFlag::None;
uint8_t ctxFlag = ContextFlag::Invalid;
uint8_t id;
uint8_t opacity = 255;
- uint8_t refCnt = 0;
+ uint8_t refCnt = 0; //reference count
- Impl(Paint* pnt) : paint(pnt)
- {
- }
+ Impl(Paint* pnt) : paint(pnt) {}
~Impl()
{
@@ -68,18 +67,19 @@ namespace tvg
free(compData);
}
delete(rTransform);
+ if (renderer && (renderer->unref() == 0)) delete(renderer);
}
uint8_t ref()
{
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
- return (++refCnt);
+ return ++refCnt;
}
uint8_t unref()
{
if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
- return (--refCnt);
+ return --refCnt;
}
bool transform(const Matrix& m)
@@ -131,15 +131,14 @@ namespace tvg
return true;
}
- RenderRegion bounds(RenderMethod& renderer) const;
- bool dispose(RenderMethod& renderer);
+ RenderRegion bounds(RenderMethod* renderer) const;
Iterator* iterator();
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
- RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
- bool render(RenderMethod& renderer);
+ RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
+ bool render(RenderMethod* renderer);
Paint* duplicate();
};
}
diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp
index 7f120593f4..cfa1bccd08 100644
--- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgPicture.cpp
@@ -69,18 +69,18 @@ bool Picture::Impl::needComposition(uint8_t opacity)
}
-bool Picture::Impl::render(RenderMethod &renderer)
+bool Picture::Impl::render(RenderMethod* renderer)
{
bool ret = false;
- if (surface) return renderer.renderImage(rd);
+ if (surface) return renderer->renderImage(rd);
else if (paint) {
Compositor* cmp = nullptr;
if (needComp) {
- cmp = renderer.target(bounds(renderer), renderer.colorSpace());
- renderer.beginComposite(cmp, CompositeMethod::None, 255);
+ cmp = renderer->target(bounds(renderer), renderer->colorSpace());
+ renderer->beginComposite(cmp, CompositeMethod::None, 255);
}
ret = paint->pImpl->render(renderer);
- if (cmp) renderer.endComposite(cmp);
+ if (cmp) renderer->endComposite(cmp);
}
return ret;
}
@@ -95,9 +95,9 @@ bool Picture::Impl::size(float w, float h)
}
-RenderRegion Picture::Impl::bounds(RenderMethod& renderer)
+RenderRegion Picture::Impl::bounds(RenderMethod* renderer)
{
- if (rd) return renderer.region(rd);
+ if (rd) return renderer->region(rd);
if (paint) return paint->pImpl->bounds(renderer);
return {0, 0, 0, 0};
}
diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h
index 26a171ba66..91c16eb44e 100644
--- a/thirdparty/thorvg/src/renderer/tvgPicture.h
+++ b/thirdparty/thorvg/src/renderer/tvgPicture.h
@@ -70,9 +70,9 @@ struct Picture::Impl
RenderTransform resizeTransform(const RenderTransform* pTransform);
bool needComposition(uint8_t opacity);
- bool render(RenderMethod &renderer);
+ bool render(RenderMethod* renderer);
bool size(float w, float h);
- RenderRegion bounds(RenderMethod& renderer);
+ RenderRegion bounds(RenderMethod* renderer);
Result load(ImageLoader* ploader);
Impl(Picture* p) : picture(p)
@@ -82,24 +82,21 @@ struct Picture::Impl
~Impl()
{
LoaderMgr::retrieve(loader);
+ if (surface) {
+ if (auto renderer = PP(picture)->renderer) {
+ renderer->dispose(rd);
+ }
+ }
delete(paint);
}
- bool dispose(RenderMethod& renderer)
- {
- if (paint) paint->pImpl->dispose(renderer);
- else if (surface) renderer.dispose(rd);
- rd = nullptr;
- return true;
- }
-
- RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+ RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
auto flag = load();
if (surface) {
auto transform = resizeTransform(pTransform);
- rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag));
+ rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag));
} else if (paint) {
if (resizing) {
loader->resize(paint, w, h);
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h
index 3437f9cbff..210382efb4 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.h
+++ b/thirdparty/thorvg/src/renderer/tvgRender.h
@@ -261,7 +261,23 @@ struct RenderShape
class RenderMethod
{
+private:
+ uint32_t refCnt = 0; //reference count
+ Key key;
+
public:
+ uint32_t ref()
+ {
+ ScopedLock lock(key);
+ return (++refCnt);
+ }
+
+ uint32_t unref()
+ {
+ ScopedLock lock(key);
+ return (--refCnt);
+ }
+
virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
@@ -270,7 +286,7 @@ public:
virtual bool renderShape(RenderData data) = 0;
virtual bool renderImage(RenderData data) = 0;
virtual bool postRender() = 0;
- virtual bool dispose(RenderData data) = 0;
+ virtual void dispose(RenderData data) = 0;
virtual RenderRegion region(RenderData data) = 0;
virtual RenderRegion viewport() = 0;
virtual bool viewport(const RenderRegion& vp) = 0;
@@ -322,7 +338,7 @@ static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
}
}
-static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method)
+static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, CompositeMethod method)
{
switch(method) {
case CompositeMethod::AlphaMask:
@@ -335,7 +351,7 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
//TODO: Optimize Luma/InvLuma colorspace to Grayscale8
case CompositeMethod::LumaMask:
case CompositeMethod::InvLumaMask:
- return renderer.colorSpace();
+ return renderer->colorSpace();
default:
TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
return ColorSpace::Unsupported;
diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h
index 1a5600c231..2267a2f71b 100644
--- a/thirdparty/thorvg/src/renderer/tvgScene.h
+++ b/thirdparty/thorvg/src/renderer/tvgScene.h
@@ -59,7 +59,6 @@ struct SceneIterator : Iterator
struct Scene::Impl
{
list<Paint*> paints;
- RenderMethod* renderer = nullptr; //keep it for explicit clear
RenderData rd = nullptr;
Scene* scene = nullptr;
uint8_t opacity; //for composition
@@ -74,19 +73,10 @@ struct Scene::Impl
for (auto paint : paints) {
if (P(paint)->unref() == 0) delete(paint);
}
- }
- bool dispose(RenderMethod& renderer)
- {
- for (auto paint : paints) {
- paint->pImpl->dispose(renderer);
+ if (auto renderer = PP(scene)->renderer) {
+ renderer->dispose(rd);
}
-
- renderer.dispose(rd);
- this->renderer = nullptr;
- this->rd = nullptr;
-
- return true;
}
bool needComposition(uint8_t opacity)
@@ -111,7 +101,7 @@ struct Scene::Impl
return true;
}
- RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
+ RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
{
if ((needComp = needComposition(opacity))) {
/* Overriding opacity value. If this scene is half-translucent,
@@ -120,14 +110,12 @@ struct Scene::Impl
opacity = 255;
}
- this->renderer = &renderer;
-
if (clipper) {
Array<RenderData> rds(paints.size());
for (auto paint : paints) {
rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
}
- rd = renderer.prepare(rds, rd, transform, clips, opacity, flag);
+ rd = renderer->prepare(rds, rd, transform, clips, opacity, flag);
return rd;
} else {
for (auto paint : paints) {
@@ -137,13 +125,13 @@ struct Scene::Impl
}
}
- bool render(RenderMethod& renderer)
+ bool render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
if (needComp) {
- cmp = renderer.target(bounds(renderer), renderer.colorSpace());
- renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+ cmp = renderer->target(bounds(renderer), renderer->colorSpace());
+ renderer->beginComposite(cmp, CompositeMethod::None, opacity);
needComp = false;
}
@@ -151,12 +139,12 @@ struct Scene::Impl
if (!paint->pImpl->render(renderer)) return false;
}
- if (cmp) renderer.endComposite(cmp);
+ if (cmp) renderer->endComposite(cmp);
return true;
}
- RenderRegion bounds(RenderMethod& renderer) const
+ RenderRegion bounds(RenderMethod* renderer) const
{
if (paints.empty()) return {0, 0, 0, 0};
@@ -226,14 +214,10 @@ struct Scene::Impl
void clear(bool free)
{
- auto dispose = renderer ? true : false;
-
for (auto paint : paints) {
- if (dispose) free &= P(paint)->dispose(*renderer);
if (P(paint)->unref() == 0 && free) delete(paint);
}
paints.clear();
- renderer = nullptr;
}
Iterator* iterator()
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h
index a7f1226690..1a7a29a999 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.h
+++ b/thirdparty/thorvg/src/renderer/tvgShape.h
@@ -41,25 +41,25 @@ struct Shape::Impl
{
}
- bool dispose(RenderMethod& renderer)
+ ~Impl()
{
- renderer.dispose(rd);
- rd = nullptr;
- return true;
+ if (auto renderer = PP(shape)->renderer) {
+ renderer->dispose(rd);
+ }
}
- bool render(RenderMethod& renderer)
+ bool render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
bool ret;
if (needComp) {
- cmp = renderer.target(bounds(renderer), renderer.colorSpace());
- renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+ cmp = renderer->target(bounds(renderer), renderer->colorSpace());
+ renderer->beginComposite(cmp, CompositeMethod::None, opacity);
needComp = false;
}
- ret = renderer.renderShape(rd);
- if (cmp) renderer.endComposite(cmp);
+ ret = renderer->renderShape(rd);
+ if (cmp) renderer->endComposite(cmp);
return ret;
}
@@ -83,7 +83,7 @@ struct Shape::Impl
return true;
}
- RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+ RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
if ((needComp = needComposition(opacity))) {
/* Overriding opacity value. If this scene is half-translucent,
@@ -92,14 +92,14 @@ struct Shape::Impl
opacity = 255;
}
- rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+ rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
flag = RenderUpdateFlag::None;
return rd;
}
- RenderRegion bounds(RenderMethod& renderer)
+ RenderRegion bounds(RenderMethod* renderer)
{
- return renderer.region(rd);
+ return renderer->region(rd);
}
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h
index b9f7ef6079..f4fb12259a 100644
--- a/thirdparty/thorvg/src/renderer/tvgText.h
+++ b/thirdparty/thorvg/src/renderer/tvgText.h
@@ -35,7 +35,6 @@
struct Text::Impl
{
- RenderData rd = nullptr;
FontLoader* loader = nullptr;
Shape* paint = nullptr;
char* utf8 = nullptr;
@@ -92,12 +91,13 @@ struct Text::Impl
return Result::Success;
}
- RenderRegion bounds(RenderMethod& renderer)
+ RenderRegion bounds(RenderMethod* renderer)
{
- return renderer.region(rd);
+ if (paint) return P(paint)->bounds(renderer);
+ else return {0, 0, 0, 0};
}
- bool render(RenderMethod& renderer)
+ bool render(RenderMethod* renderer)
{
if (paint) return PP(paint)->render(renderer);
return false;
@@ -120,7 +120,7 @@ struct Text::Impl
return false;
}
- RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+ RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
if (!load()) return nullptr;
@@ -142,8 +142,7 @@ struct Text::Impl
P(static_cast<RadialGradient*>(fill))->fr *= scale;
}
}
- rd = PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper);
- return rd;
+ return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper);
}
bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
@@ -153,13 +152,6 @@ struct Text::Impl
return true;
}
- bool dispose(RenderMethod& renderer)
- {
- renderer.dispose(rd);
- this->rd = nullptr;
- return true;
- }
-
Paint* duplicate()
{
load();
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index 4f485d0921..be82de797d 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.12.3
+VERSION=0.12.4
cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/