From 4d8234d50ec3b477e08ce72a03b26c6fb4249551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:43:57 +0200 Subject: [PATCH] [iOS] Reintegrate camera module to the main repo. --- doc/classes/CameraServer.xml | 2 +- .../editor_export_platform_apple_embedded.cpp | 71 ++++++++-- .../editor_export_platform_apple_embedded.h | 2 +- .../Info.plist | 40 ++++++ .../ios-arm64/empty | 1 + .../ios-arm64_x86_64-simulator/empty | 1 + .../Info.plist | 40 ++++++ .../ios-arm64/empty | 1 + .../ios-arm64_x86_64-simulator/empty | 1 + .../Info.plist | 39 +++++ .../xros-arm64-simulator/empty | 1 + .../xros-arm64/empty | 1 + .../Info.plist | 39 +++++ .../xros-arm64-simulator/empty | 1 + .../xros-arm64/empty | 1 + modules/camera/SCsub | 10 +- .../camera/{camera_macos.h => camera_apple.h} | 8 +- .../{camera_macos.mm => camera_apple.mm} | 133 +++++++++++++----- modules/camera/config.py | 9 +- modules/camera/register_types.cpp | 4 +- platform/ios/SCsub | 3 + .../doc_classes/EditorExportPlatformIOS.xml | 3 + platform/visionos/SCsub | 3 + .../EditorExportPlatformVisionOS.xml | 3 + platform_methods.py | 109 ++++++++++---- 25 files changed, 441 insertions(+), 85 deletions(-) create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/Info.plist create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64_x86_64-simulator/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/Info.plist create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64_x86_64-simulator/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/Info.plist create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64-simulator/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/Info.plist create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64-simulator/empty create mode 100644 misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64/empty rename modules/camera/{camera_macos.h => camera_apple.h} (93%) rename modules/camera/{camera_macos.mm => camera_apple.mm} (77%) diff --git a/doc/classes/CameraServer.xml b/doc/classes/CameraServer.xml index 3b16768a3b0..7de268c8571 100644 --- a/doc/classes/CameraServer.xml +++ b/doc/classes/CameraServer.xml @@ -6,7 +6,7 @@ The [CameraServer] keeps track of different cameras accessible in Godot. These are external cameras such as webcams or the cameras on your phone. It is notably used to provide AR modules with a video feed from the camera. - [b]Note:[/b] This class is currently only implemented on Linux, Android, macOS, and iOS. On other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required. + [b]Note:[/b] This class is currently only implemented on Linux, Android, macOS, and iOS. On other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, enable [member EditorExportPlatformIOS.modules/camera]. diff --git a/editor/export/editor_export_platform_apple_embedded.cpp b/editor/export/editor_export_platform_apple_embedded.cpp index 2f9db30283e..458e22e9793 100644 --- a/editor/export/editor_export_platform_apple_embedded.cpp +++ b/editor/export/editor_export_platform_apple_embedded.cpp @@ -283,6 +283,8 @@ void EditorExportPlatformAppleEmbedded::get_export_options(List *r r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "modules/camera"), false)); + Vector found_plugins = get_plugins(get_platform_name()); for (int i = 0; i < found_plugins.size(); i++) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false)); @@ -1422,7 +1424,7 @@ Vector EditorExportPlatformAppleEmbedded::_get_preset_architectures(cons return enabled_archs; } -Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Ref &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector &r_exported_assets, bool p_debug) { +Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Ref &p_preset, AppleEmbeddedConfigData &p_config_data, const String &p_dest_dir, const Vector &p_module_libs, Vector &r_exported_assets, bool p_debug) { String plugin_definition_cpp_code; String plugin_initialization_cpp_code; String plugin_deinitialization_cpp_code; @@ -1449,7 +1451,7 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re String plugin_binary_result_file = plugin.binary.get_file(); // We shouldn't embed .xcframework that contains static libraries. // Static libraries are not embedded anyway. - err = _copy_asset(p_preset, dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); + err = _copy_asset(p_preset, p_dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); // Adding dependencies. @@ -1553,6 +1555,23 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re plugin_deinitialization_cpp_code += "\t" + deinitialization_method; } + for (const String &lib_name : p_module_libs) { + String definition_comment = "// Module: " + lib_name + "\n"; + String initialization_method = "register_" + lib_name + "_external_module();\n"; + String deinitialization_method = "unregister_" + lib_name + "_external_module();\n"; + + plugin_definition_cpp_code += definition_comment + + "extern void " + initialization_method + + "extern void " + deinitialization_method + "\n"; + + plugin_initialization_cpp_code += "\t" + initialization_method; + plugin_deinitialization_cpp_code += "\t" + deinitialization_method; + + String binary_name = p_dest_dir.get_file().get_basename(); + AppleEmbeddedExportAsset exported_asset = { binary_name + "_" + lib_name + ".xcframework", true, false }; + r_exported_assets.push_back(exported_asset); + } + // Updating `Info.plist` { for (const KeyValue &E : plist_values) { @@ -1570,15 +1589,15 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re // Export files { // Export linked plugin dependency - err = _export_additional_assets(p_preset, dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); + err = _export_additional_assets(p_preset, p_dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); // Export embedded plugin dependency - err = _export_additional_assets(p_preset, dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); + err = _export_additional_assets(p_preset, p_dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); // Export plugin files - err = _export_additional_assets(p_preset, dest_dir, plugin_files, false, false, r_exported_assets); + err = _export_additional_assets(p_preset, p_dest_dir, plugin_files, false, false, r_exported_assets); ERR_FAIL_COND_V(err != OK, err); } @@ -1765,6 +1784,10 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref module_libs; + if (p_preset->get("modules/camera").operator bool()) { + module_libs.push_back("camera"); + } print_line("Static framework: " + library_to_use); String pkg_name; @@ -1774,7 +1797,7 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref files_to_parse; const String project_file = "godot_apple_embedded.xcodeproj/project.pbxproj"; @@ -1821,7 +1844,7 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Refget("modules/camera").operator bool()) { + String description = p_preset->get("privacy/camera_usage_description"); + if (description.is_empty()) { + valid = false; + err += TTR("Camera module enabled, but camera usage description is not set.") + "\n"; + } + } + const String &additional_plist_content = p_preset->get("application/additional_plist_content"); if (!additional_plist_content.is_empty()) { const String &plist = vformat("\n" diff --git a/editor/export/editor_export_platform_apple_embedded.h b/editor/export/editor_export_platform_apple_embedded.h index 3a346ef72bb..4536ce05d43 100644 --- a/editor/export/editor_export_platform_apple_embedded.h +++ b/editor/export/editor_export_platform_apple_embedded.h @@ -197,7 +197,7 @@ class EditorExportPlatformAppleEmbedded : public EditorExportPlatform { Error _export_additional_assets(const Ref &p_preset, const String &p_out_dir, const Vector &p_assets, bool p_is_framework, bool p_should_embed, Vector &r_exported_assets); Error _copy_asset(const Ref &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector &r_exported_assets); Error _export_additional_assets(const Ref &p_preset, const String &p_out_dir, const Vector &p_libraries, Vector &r_exported_assets); - Error _export_apple_embedded_plugins(const Ref &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector &r_exported_assets, bool p_debug); + Error _export_apple_embedded_plugins(const Ref &p_preset, AppleEmbeddedConfigData &p_config_data, const String &p_dest_dir, const Vector &p_module_libs, Vector &r_exported_assets, bool p_debug); Error _export_project_helper(const Ref &p_preset, bool p_debug, const String &p_path, BitField p_flags, bool p_oneclick); diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/Info.plist b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/Info.plist new file mode 100644 index 00000000000..5c352d09166 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64_x86_64-simulator/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64_x86_64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.debug.xcframework/ios-arm64_x86_64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/Info.plist b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/Info.plist new file mode 100644 index 00000000000..5c352d09166 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64_x86_64-simulator/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64_x86_64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.ios.release.xcframework/ios-arm64_x86_64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/Info.plist b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/Info.plist new file mode 100644 index 00000000000..25f87359e67 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/Info.plist @@ -0,0 +1,39 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + xros-arm64 + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + xros + + + LibraryIdentifier + xros-arm64-simulator + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + xros + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64-simulator/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.debug.xcframework/xros-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/Info.plist b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/Info.plist new file mode 100644 index 00000000000..25f87359e67 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/Info.plist @@ -0,0 +1,39 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + xros-arm64 + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + xros + + + LibraryIdentifier + xros-arm64-simulator + LibraryPath + libgodot_camera.a + SupportedArchitectures + + arm64 + + SupportedPlatform + xros + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64-simulator/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64/empty b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/apple_embedded_xcode/libgodot_camera.visionos.release.xcframework/xros-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/modules/camera/SCsub b/modules/camera/SCsub index 750dc7c368c..22555f2921a 100644 --- a/modules/camera/SCsub +++ b/modules/camera/SCsub @@ -6,19 +6,25 @@ Import("env_modules") env_camera = env_modules.Clone() -if env["platform"] in ["windows", "macos", "linuxbsd", "android"]: +if env["platform"] in ["windows", "macos", "linuxbsd", "android", "ios", "visionos"]: env_camera.add_source_files(env.modules_sources, "register_types.cpp") if env["platform"] == "windows": env_camera.add_source_files(env.modules_sources, "camera_win.cpp") elif env["platform"] == "macos": - env_camera.add_source_files(env.modules_sources, "camera_macos.mm") + env_camera.add_source_files(env.modules_sources, "camera_apple.mm") elif env["platform"] == "android": env_camera.add_source_files(env.modules_sources, "camera_android.cpp") env.Append(LIBS=["camera2ndk", "mediandk"]) +elif env["platform"] in ["ios", "visionos"]: + ext_module_source = ["camera_apple.mm"] + ext_camera_lib = env_camera.add_library("#bin/libgodot_camera", ext_module_source) + env.Append(LIBS_EXTERNAL=[ext_camera_lib]) + env.Append(MODULES_EXTERNAL=["_camera"]) + elif env["platform"] == "linuxbsd": env_camera.add_source_files(env.modules_sources, "camera_linux.cpp") env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp") diff --git a/modules/camera/camera_macos.h b/modules/camera/camera_apple.h similarity index 93% rename from modules/camera/camera_macos.h rename to modules/camera/camera_apple.h index 9713b66df0d..82b5e7dec53 100644 --- a/modules/camera/camera_macos.h +++ b/modules/camera/camera_apple.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* camera_macos.h */ +/* camera_apple.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -35,11 +35,11 @@ #include "servers/camera/camera_server.h" -class CameraMacOS : public CameraServer { - GDSOFTCLASS(CameraMacOS, CameraServer); +class CameraApple : public CameraServer { + GDSOFTCLASS(CameraApple, CameraServer); public: - CameraMacOS() = default; + CameraApple() = default; void update_feeds(); void set_monitoring_feeds(bool p_monitoring_feeds) override; diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_apple.mm similarity index 77% rename from modules/camera/camera_macos.mm rename to modules/camera/camera_apple.mm index 7e3b78e2daa..5943bbd686f 100644 --- a/modules/camera/camera_macos.mm +++ b/modules/camera/camera_apple.mm @@ -1,5 +1,5 @@ /**************************************************************************/ -/* camera_macos.mm */ +/* camera_apple.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -31,11 +31,14 @@ ///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!! // If you fix something here, make sure you fix it there as well! -#import "camera_macos.h" +#import "camera_apple.h" #include "servers/camera/camera_feed.h" #import +#ifdef IOS_ENABLED +#import +#endif ////////////////////////////////////////////////////////////////////////// // MyCaptureSession - This is a little helper class so we can capture our frames @@ -63,23 +66,30 @@ - (id)initForFeed:(Ref)p_feed andDevice:(AVCaptureDevice *)p_device width[1] = 0; height[1] = 0; -#ifdef APPLE_EMBEDDED_ENABLED - [p_device lockForConfiguration:&error]; - - [p_device setFocusMode:AVCaptureFocusModeLocked]; - [p_device setExposureMode:AVCaptureExposureModeLocked]; - [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked]; +#ifdef IOS_ENABLED + if ([p_device lockForConfiguration:&error]) { + if ([p_device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + [p_device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + } + if ([p_device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { + [p_device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } + if ([p_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) { + [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]; + } - [p_device unlockForConfiguration]; -#endif // APPLE_EMBEDDED_ENABLED + [p_device unlockForConfiguration]; + } +#endif // IOS_ENABLED [self beginConfiguration]; -#ifdef APPLE_EMBEDDED_ENABLED +#ifdef IOS_ENABLED self.sessionPreset = AVCaptureSessionPreset1280x720; -#endif // APPLE_EMBEDDED_ENABLED +#endif // IOS_ENABLED + + input = [[AVCaptureDeviceInput alloc] initWithDevice:p_device error:&error]; - input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error]; if (!input) { print_line("Couldn't get input device for camera"); [self commitConfiguration]; @@ -219,6 +229,28 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM // set our texture... feed->set_ycbcr_images(img[0], img[1]); +#ifdef IOS_ENABLED + UIInterfaceOrientation orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation; + + Transform2D display_transform; + switch (orientation) { + case UIInterfaceOrientationPortrait: { + display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0); + } break; + case UIInterfaceOrientationLandscapeRight: { + display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0); + } break; + case UIInterfaceOrientationLandscapeLeft: { + display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0); + } break; + default: { + display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0); + } break; + } + + feed->set_transform(display_transform); +#endif // IOS_ENABLED + // and unlock CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); } @@ -226,10 +258,10 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM @end ////////////////////////////////////////////////////////////////////////// -// CameraFeedMacOS - Subclass for camera feeds in macOS +// CameraFeedApple - Subclass for camera feeds in macOS -class CameraFeedMacOS : public CameraFeed { - GDSOFTCLASS(CameraFeedMacOS, CameraFeed); +class CameraFeedApple : public CameraFeed { + GDSOFTCLASS(CameraFeedApple, CameraFeed); private: AVCaptureDevice *device; @@ -238,8 +270,8 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM public: AVCaptureDevice *get_device() const; - CameraFeedMacOS(); - ~CameraFeedMacOS(); + CameraFeedApple(); + ~CameraFeedApple(); void set_device(AVCaptureDevice *p_device); @@ -247,22 +279,23 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM void deactivate_feed() override; }; -AVCaptureDevice *CameraFeedMacOS::get_device() const { +AVCaptureDevice *CameraFeedApple::get_device() const { return device; } -CameraFeedMacOS::CameraFeedMacOS() { +CameraFeedApple::CameraFeedApple() { device = nullptr; capture_session = nullptr; + transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */ } -CameraFeedMacOS::~CameraFeedMacOS() { +CameraFeedApple::~CameraFeedApple() { if (is_active()) { deactivate_feed(); } } -void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) { +void CameraFeedApple::set_device(AVCaptureDevice *p_device) { device = p_device; // get some info @@ -276,14 +309,14 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM }; } -bool CameraFeedMacOS::activate_feed() { +bool CameraFeedApple::activate_feed() { if (capture_session) { // Already recording. return true; } // Start camera capture, check permission. - if (@available(macOS 10.14, *)) { + if (@available(macOS 10.14, iOS 14.0, visionOS 1.0, *)) { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status == AVAuthorizationStatusAuthorized) { capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device]; @@ -311,7 +344,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM } } -void CameraFeedMacOS::deactivate_feed() { +void CameraFeedApple::deactivate_feed() { // end camera capture if we have one if (capture_session) { [capture_session cleanup]; @@ -324,7 +357,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM // when devices are connected/disconnected @interface MyDeviceNotifications : NSObject { - CameraMacOS *camera_server; + CameraApple *camera_server; } @end @@ -335,7 +368,7 @@ - (void)devices_changed:(NSNotification *)notification { camera_server->update_feeds(); } -- (id)initForServer:(CameraMacOS *)p_server { +- (id)initForServer:(CameraApple *)p_server { if (self = [super init]) { camera_server = p_server; @@ -356,13 +389,31 @@ - (void)dealloc { MyDeviceNotifications *device_notifications = nil; ////////////////////////////////////////////////////////////////////////// -// CameraMacOS - Subclass for our camera server on macOS +// CameraApple - Subclass for our camera server on macOS -void CameraMacOS::update_feeds() { +void CameraApple::update_feeds() { NSArray *devices = nullptr; +#ifdef APPLE_EMBEDDED_ENABLED + { + NSMutableArray *deviceTypes = [NSMutableArray array]; + if (@available(iOS 14.0, visionOS 2.1, *)) { + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + } +#ifdef IOS_ENABLED + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera]; + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera]; + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera]; + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualWideCamera]; + [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTripleCamera]; +#endif // IOS_ENABLED + AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; + devices = session.devices; + } +#else // APPLE_EMBEDDED_ENABLED #if defined(__x86_64__) if (@available(macOS 10.15, *)) { -#endif +#endif // __x86_64__ AVCaptureDeviceDiscoverySession *session; if (@available(macOS 14.0, *)) { session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeContinuityCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; @@ -374,11 +425,12 @@ - (void)dealloc { } else { devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; } -#endif +#endif // __x86_64__ +#endif // APPLE_EMBEDDED_ENABLED // Deactivate feeds that are gone before removing them. for (int i = feeds.size() - 1; i >= 0; i--) { - Ref feed = (Ref)feeds[i]; + Ref feed = (Ref)feeds[i]; if (feed.is_null()) { continue; } @@ -394,7 +446,7 @@ - (void)dealloc { for (AVCaptureDevice *device in devices) { bool found = false; for (int i = 0; i < feeds.size() && !found; i++) { - Ref feed = (Ref)feeds[i]; + Ref feed = (Ref)feeds[i]; if (feed.is_null()) { continue; } @@ -404,7 +456,7 @@ - (void)dealloc { }; if (!found) { - Ref newfeed; + Ref newfeed; newfeed.instantiate(); newfeed->set_device(device); @@ -414,7 +466,7 @@ - (void)dealloc { emit_signal(SNAME(CameraServer::feeds_updated_signal_name)); } -void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) { +void CameraApple::set_monitoring_feeds(bool p_monitoring_feeds) { if (p_monitoring_feeds == monitoring_feeds) { return; } @@ -431,3 +483,14 @@ - (void)dealloc { device_notifications = nil; } } + +#ifdef APPLE_EMBEDDED_ENABLED + +void register_camera_external_module() { + CameraServer::make_default(); +} + +void unregister_camera_external_module() { +} + +#endif // APPLE_EMBEDDED_ENABLED diff --git a/modules/camera/config.py b/modules/camera/config.py index d699d4e456c..7bcf7655927 100644 --- a/modules/camera/config.py +++ b/modules/camera/config.py @@ -3,7 +3,14 @@ def can_build(env, platform): if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"): return False - return platform == "macos" or platform == "windows" or platform == "linuxbsd" or platform == "android" + return ( + platform == "macos" + or platform == "windows" + or platform == "linuxbsd" + or platform == "android" + or platform == "ios" + or platform == "visionos" + ) def configure(env): diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp index c56ddfe862e..5f4969de17c 100644 --- a/modules/camera/register_types.cpp +++ b/modules/camera/register_types.cpp @@ -37,7 +37,7 @@ #include "camera_win.h" #endif #if defined(MACOS_ENABLED) -#include "camera_macos.h" +#include "camera_apple.h" #endif #if defined(ANDROID_ENABLED) #include "camera_android.h" @@ -55,7 +55,7 @@ void initialize_camera_module(ModuleInitializationLevel p_level) { CameraServer::make_default(); #endif #if defined(MACOS_ENABLED) - CameraServer::make_default(); + CameraServer::make_default(); #endif #if defined(ANDROID_ENABLED) CameraServer::make_default(); diff --git a/platform/ios/SCsub b/platform/ios/SCsub index a313442e2ae..92f6c1714bb 100644 --- a/platform/ios/SCsub +++ b/platform/ios/SCsub @@ -22,6 +22,9 @@ ios_lib = env_ios.add_library("ios", ios_lib) # (iOS) Enable module support env_ios.Append(CCFLAGS=["-fmodules", "-fcxx-modules"]) +if "LIBS_EXTERNAL" in env_ios: + env_ios.Depends(ios_lib, env_ios["LIBS_EXTERNAL"]) + combine_command = env_ios.CommandNoCache( "#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], env.Run(combine_libs_apple_embedded) ) diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index c9723a52b24..d2be73aea3d 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -269,6 +269,9 @@ Spotlight icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + If [code]true[/code], [CameraServer] module is added to the exported project. + The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. diff --git a/platform/visionos/SCsub b/platform/visionos/SCsub index d3da9bb04a4..8f64654d8bd 100644 --- a/platform/visionos/SCsub +++ b/platform/visionos/SCsub @@ -21,6 +21,9 @@ visionos_lib = env_visionos.add_library("visionos", visionos_lib) # Enable module support env_visionos.Append(CCFLAGS=["-fmodules", "-fcxx-modules"]) +if "LIBS_EXTERNAL" in env_visionos: + env_visionos.Depends(visionos_lib, env_visionos["LIBS_EXTERNAL"]) + combine_command = env_visionos.Command( "#bin/libgodot" + env_visionos["LIBSUFFIX"], [visionos_lib] + env_visionos["LIBS"], combine_libs_apple_embedded ) diff --git a/platform/visionos/doc_classes/EditorExportPlatformVisionOS.xml b/platform/visionos/doc_classes/EditorExportPlatformVisionOS.xml index 0297bed0576..b61c87cd18f 100644 --- a/platform/visionos/doc_classes/EditorExportPlatformVisionOS.xml +++ b/platform/visionos/doc_classes/EditorExportPlatformVisionOS.xml @@ -121,6 +121,9 @@ Base application icon used to generate other icons, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + + If [code]true[/code], [CameraServer] module is added to the exported project. + The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. diff --git a/platform_methods.py b/platform_methods.py index 2b5aaa61b8f..7a702c08b04 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -172,60 +172,115 @@ def combine_libs_apple_embedded(target, source, env): ) -def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env): +def lipo_and_copy_apple_embedded( + platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, module_prefix, app_dir, env +): bin_dir = env.Dir("#bin").abspath - # Template bundle. - app_prefix = "godot." + platform - rel_prefix = "libgodot." + platform + "." + "template_release" - dbg_prefix = "libgodot." + platform + "." + "template_debug" - if env.dev_build: - app_prefix += ".dev" - rel_prefix += ".dev" - dbg_prefix += ".dev" - if env["precision"] == "double": - app_prefix += ".double" - rel_prefix += ".double" - dbg_prefix += ".double" - # Lipo template libraries. # # env.extra_suffix contains ".simulator" when building for simulator, # but it's undesired when calling lipo() extra_suffix = env.extra_suffix.replace(".simulator", "") - rel_target_bin = lipo(bin_dir + "/" + rel_prefix, extra_suffix + ".a") - dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, extra_suffix + ".a") - rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + extra_suffix + ".a") - dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + extra_suffix + ".a") + rel_target_bin = lipo(bin_dir + "/libgodot" + module_prefix + "." + rel_prefix, extra_suffix + ".a") + dbg_target_bin = lipo(bin_dir + "/libgodot" + module_prefix + "." + dbg_prefix, extra_suffix + ".a") + rel_target_bin_sim = lipo( + bin_dir + "/libgodot" + module_prefix + "." + rel_prefix, ".simulator" + extra_suffix + ".a" + ) + dbg_target_bin_sim = lipo( + bin_dir + "/libgodot" + module_prefix + "." + dbg_prefix, ".simulator" + extra_suffix + ".a" + ) # Assemble Xcode project bundle. - app_dir = env.Dir("#bin/" + platform + "_xcode").abspath - templ = env.Dir("#misc/dist/apple_embedded_xcode").abspath - if os.path.exists(app_dir): - shutil.rmtree(app_dir) - shutil.copytree(templ, app_dir) if rel_target_bin != "": print(f' Copying "{platform}" release framework') shutil.copy( - rel_target_bin, app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir + "/libgodot.a" + rel_target_bin, + app_dir + + "/libgodot" + + module_prefix + + "." + + platform + + ".release.xcframework/" + + framework_dir + + "/libgodot" + + module_prefix + + ".a", ) if dbg_target_bin != "": print(f' Copying "{platform}" debug framework') shutil.copy( - dbg_target_bin, app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir + "/libgodot.a" + dbg_target_bin, + app_dir + + "/libgodot" + + module_prefix + + "." + + platform + + ".debug.xcframework/" + + framework_dir + + "/libgodot" + + module_prefix + + ".a", ) if rel_target_bin_sim != "": print(f' Copying "{platform}" (simulator) release framework') shutil.copy( rel_target_bin_sim, - app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir_sim + "/libgodot.a", + app_dir + + "/libgodot" + + module_prefix + + "." + + platform + + ".release.xcframework/" + + framework_dir_sim + + "/libgodot" + + module_prefix + + ".a", ) if dbg_target_bin_sim != "": print(f' Copying "{platform}" (simulator) debug framework') shutil.copy( dbg_target_bin_sim, - app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir_sim + "/libgodot.a", + app_dir + + "/libgodot" + + module_prefix + + "." + + platform + + ".debug.xcframework/" + + framework_dir_sim + + "/libgodot" + + module_prefix + + ".a", ) + +def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env): + # Template bundle. + extra_suffix = env.extra_suffix.replace(".simulator", "") + app_prefix = "godot." + platform + rel_prefix = platform + "." + "template_release" + dbg_prefix = platform + "." + "template_debug" + if env.dev_build: + app_prefix += ".dev" + rel_prefix += ".dev" + dbg_prefix += ".dev" + if env["precision"] == "double": + app_prefix += ".double" + rel_prefix += ".double" + dbg_prefix += ".double" + + app_dir = env.Dir("#bin/" + platform + "_xcode").abspath + templ = env.Dir("#misc/dist/apple_embedded_xcode").abspath + if os.path.exists(app_dir): + shutil.rmtree(app_dir) + shutil.copytree(templ, app_dir) + + lipo_and_copy_apple_embedded(platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, "", app_dir, env) + if "MODULES_EXTERNAL" in env: + for mod in env["MODULES_EXTERNAL"]: + lipo_and_copy_apple_embedded( + platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, mod, app_dir, env + ) + # Remove other platform xcframeworks for entry in os.listdir(app_dir): if (entry.startswith("libgodot.") or entry.startswith("libgodot_")) and entry.endswith(".xcframework"):