diff options
Diffstat (limited to 'platform/iphone')
-rw-r--r-- | platform/iphone/SCsub | 1 | ||||
-rw-r--r-- | platform/iphone/camera_ios.h | 45 | ||||
-rw-r--r-- | platform/iphone/camera_ios.mm | 436 | ||||
-rw-r--r-- | platform/iphone/detect.py | 20 | ||||
-rw-r--r-- | platform/iphone/export/export.cpp | 47 | ||||
-rw-r--r-- | platform/iphone/gl_view.mm | 2 | ||||
-rw-r--r-- | platform/iphone/icloud.mm | 2 | ||||
-rw-r--r-- | platform/iphone/os_iphone.cpp | 7 | ||||
-rw-r--r-- | platform/iphone/os_iphone.h | 3 |
9 files changed, 541 insertions, 22 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index fa1b124561..85ba56165b 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -14,6 +14,7 @@ iphone_lib = [ 'in_app_store.mm', 'icloud.mm', 'ios.mm', + 'camera_ios.mm', ] env_ios = env.Clone() diff --git a/platform/iphone/camera_ios.h b/platform/iphone/camera_ios.h new file mode 100644 index 0000000000..ceabdba6a3 --- /dev/null +++ b/platform/iphone/camera_ios.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* camera_ios.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 CAMERAIOS_H +#define CAMERAIOS_H + +#include "servers/camera_server.h" + +class CameraIOS : public CameraServer { +private: +public: + CameraIOS(); + ~CameraIOS(); + + void update_feeds(); +}; + +#endif /* CAMERAIOS_H */
\ No newline at end of file diff --git a/platform/iphone/camera_ios.mm b/platform/iphone/camera_ios.mm new file mode 100644 index 0000000000..029ce6debf --- /dev/null +++ b/platform/iphone/camera_ios.mm @@ -0,0 +1,436 @@ +/*************************************************************************/ +/* camera_ios.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +///@TODO this is a near duplicate of CameraOSX, we should find a way to combine those to minimise code duplication!!!! +// If you fix something here, make sure you fix it there as wel! + +#include "camera_ios.h" +#include "servers/camera/camera_feed.h" + +#import <AVFoundation/AVFoundation.h> +#import <UIKit/UIKit.h> + +////////////////////////////////////////////////////////////////////////// +// MyCaptureSession - This is a little helper class so we can capture our frames + +@interface MyCaptureSession : AVCaptureSession <AVCaptureVideoDataOutputSampleBufferDelegate> { + Ref<CameraFeed> feed; + size_t width[2]; + size_t height[2]; + PoolVector<uint8_t> img_data[2]; + + AVCaptureDeviceInput *input; + AVCaptureVideoDataOutput *output; +} + +@end + +@implementation MyCaptureSession + +- (id)initForFeed:(Ref<CameraFeed>)p_feed andDevice:(AVCaptureDevice *)p_device { + if (self = [super init]) { + NSError *error; + feed = p_feed; + width[0] = 0; + height[0] = 0; + width[1] = 0; + height[1] = 0; + + // prepare our device + [p_device lockForConfiguration:&error]; + + [p_device setFocusMode:AVCaptureFocusModeLocked]; + [p_device setExposureMode:AVCaptureExposureModeLocked]; + [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked]; + + [p_device unlockForConfiguration]; + + [self beginConfiguration]; + + // setup our capture + self.sessionPreset = AVCaptureSessionPreset1280x720; + + input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error]; + if (!input) { + print_line("Couldn't get input device for camera"); + } else { + [self addInput:input]; + } + + output = [AVCaptureVideoDataOutput new]; + if (!output) { + print_line("Couldn't get output device for camera"); + } else { + NSDictionary *settings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) }; + output.videoSettings = settings; + + // discard if the data output queue is blocked (as we process the still image) + [output setAlwaysDiscardsLateVideoFrames:YES]; + + // now set ourselves as the delegate to receive new frames. Note that we're doing this on the main thread at the moment, we may need to change this.. + [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + + [self addOutput:output]; + } + + [self commitConfiguration]; + + // kick off our session.. + [self startRunning]; + }; + return self; +} + +- (void)cleanup { + // stop running + [self stopRunning]; + + // cleanup + [self beginConfiguration]; + + if (input) { + [self removeInput:input]; + // don't release this + input = nil; + } + + if (output) { + [self removeOutput:output]; + [output setSampleBufferDelegate:nil queue:NULL]; + [output release]; + output = nil; + } + + [self commitConfiguration]; +} + +- (void)dealloc { + // bye bye + [super dealloc]; +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { + // This gets called every time our camera has a new image for us to process. + // May need to investigate in a way to throttle this if we get more images then we're rendering frames.. + + // For now, version 1, we're just doing the bare minimum to make this work... + + CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + // int width = CVPixelBufferGetWidth(pixelBuffer); + // int height = CVPixelBufferGetHeight(pixelBuffer); + + // It says that we need to lock this on the documentation pages but it's not in the samples + // need to lock our base address so we can access our pixel buffers, better safe then sorry? + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + + // get our buffers + unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + if (dataY == NULL) { + print_line("Couldn't access Y pixel buffer data"); + } else if (dataCbCr == NULL) { + print_line("Couldn't access CbCr pixel buffer data"); + } else { + UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + Ref<Image> img[2]; + + { + // do Y + int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + int _bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + + if ((width[0] != new_width) || (height[0] != new_height)) { + // printf("Camera Y plane %i, %i - %i\n", new_width, new_height, bytes_per_row); + + width[0] = new_width; + height[0] = new_height; + img_data[0].resize(new_width * new_height); + } + + PoolVector<uint8_t>::Write w = img_data[0].write(); + memcpy(w.ptr(), dataY, new_width * new_height); + + img[0].instance(); + img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); + } + + { + // do CbCr + int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); + int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); + int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + + if ((width[1] != new_width) || (height[1] != new_height)) { + // printf("Camera CbCr plane %i, %i - %i\n", new_width, new_height, bytes_per_row); + + width[1] = new_width; + height[1] = new_height; + img_data[1].resize(2 * new_width * new_height); + } + + PoolVector<uint8_t>::Write w = img_data[1].write(); + memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height); + + ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion + img[1].instance(); + img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); + } + + // set our texture... + feed->set_YCbCr_imgs(img[0], img[1]); + + // update our matrix to match the orientation, note, before changing anything + // here, be aware that the project orientation settings must match your xcode + // settings or this will go wrong! + 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; + } + + //TODO: this is correct for the camera on the back, I have a feeling this needs to be inversed for the camera on the front! + feed->set_transform(display_transform); + } + + // and unlock + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); +} + +@end + +////////////////////////////////////////////////////////////////////////// +// CameraFeedIOS - Subclass for camera feeds in iOS + +class CameraFeedIOS : public CameraFeed { +private: + AVCaptureDevice *device; + MyCaptureSession *capture_session; + +public: + bool get_is_arkit() const; + AVCaptureDevice *get_device() const; + + CameraFeedIOS(); + ~CameraFeedIOS(); + + void set_device(AVCaptureDevice *p_device); + + bool activate_feed(); + void deactivate_feed(); +}; + +AVCaptureDevice *CameraFeedIOS::get_device() const { + return device; +}; + +CameraFeedIOS::CameraFeedIOS() { + capture_session = NULL; + device = NULL; + transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */ +}; + +void CameraFeedIOS::set_device(AVCaptureDevice *p_device) { + device = p_device; + [device retain]; + + // get some info + NSString *device_name = p_device.localizedName; + name = device_name.UTF8String; + position = CameraFeed::FEED_UNSPECIFIED; + if ([p_device position] == AVCaptureDevicePositionBack) { + position = CameraFeed::FEED_BACK; + } else if ([p_device position] == AVCaptureDevicePositionFront) { + position = CameraFeed::FEED_FRONT; + }; +}; + +CameraFeedIOS::~CameraFeedIOS() { + if (capture_session != NULL) { + [capture_session release]; + capture_session = NULL; + }; + + if (device != NULL) { + [device release]; + device = NULL; + }; +}; + +bool CameraFeedIOS::activate_feed() { + if (capture_session) { + // already recording! + } else { + // start camera capture + capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device]; + }; + + return true; +}; + +void CameraFeedIOS::deactivate_feed() { + // end camera capture if we have one + if (capture_session) { + [capture_session cleanup]; + [capture_session release]; + capture_session = NULL; + }; +}; + +////////////////////////////////////////////////////////////////////////// +// MyDeviceNotifications - This is a little helper class gets notifications +// when devices are connected/disconnected + +@interface MyDeviceNotifications : NSObject { + CameraIOS *camera_server; +} + +@end + +@implementation MyDeviceNotifications + +- (void)devices_changed:(NSNotification *)notification { + camera_server->update_feeds(); +} + +- (id)initForServer:(CameraIOS *)p_server { + if (self = [super init]) { + camera_server = p_server; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasConnectedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasDisconnectedNotification object:nil]; + }; + return self; +} + +- (void)dealloc { + // remove notifications + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil]; + + [super dealloc]; +} + +@end + +MyDeviceNotifications *device_notifications = nil; + +////////////////////////////////////////////////////////////////////////// +// CameraIOS - Subclass for our camera server on iPhone + +void CameraIOS::update_feeds() { + // this way of doing things is deprecated but still works, + // rewrite to using AVCaptureDeviceDiscoverySession + + AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeBuiltInTelephotoCamera, AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTrueDepthCamera, AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; + + // remove devices that are gone.. + for (int i = feeds.size() - 1; i >= 0; i--) { + Ref<CameraFeedIOS> feed(feeds[i]); + + if (feed.is_null()) { + // feed not managed by us + } else if (![session.devices containsObject:feed->get_device()]) { + // remove it from our array, this will also destroy it ;) + remove_feed(feed); + }; + }; + + // add new devices.. + for (AVCaptureDevice *device in session.devices) { + bool found = false; + + for (int i = 0; i < feeds.size() && !found; i++) { + Ref<CameraFeedIOS> feed(feeds[i]); + + if (feed.is_null()) { + // feed not managed by us + } else if (feed->get_device() == device) { + found = true; + }; + }; + + if (!found) { + Ref<CameraFeedIOS> newfeed; + newfeed.instance(); + newfeed->set_device(device); + add_feed(newfeed); + }; + }; +}; + +CameraIOS::CameraIOS() { + // check if we have our usage description + NSString *usage_desc = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSCameraUsageDescription"]; + if (usage_desc == NULL) { + // don't initialise if we don't get anything + print_line("No NSCameraUsageDescription key in pList, no access to cameras."); + return; + } else if (usage_desc.length == 0) { + // don't initialise if we don't get anything + print_line("Empty NSCameraUsageDescription key in pList, no access to cameras."); + return; + } + + // now we'll request access. + // If this is the first time the user will be prompted with the string (iOS will read it). + // Once a decision is made it is returned. If the user wants to change it later on they + // need to go into setting. + print_line("Requesting Camera permissions"); + + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:^(BOOL granted) { + if (granted) { + print_line("Access to cameras granted!"); + + // Find available cameras we have at this time + update_feeds(); + + // should only have one of these.... + device_notifications = [[MyDeviceNotifications alloc] initForServer:this]; + } else { + print_line("No access to cameras!"); + } + }]; +}; + +CameraIOS::~CameraIOS() { + [device_notifications release]; +}; diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index d9f710e456..f646b8b1d5 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -43,7 +43,7 @@ def configure(env): ## Build type if (env["target"].startswith("release")): - env.Append(CPPFLAGS=['-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1']) + env.Append(CPPDEFINES=['NDEBUG', ('NS_BLOCK_ASSERTIONS', 1)]) if (env["optimize"] == "speed"): #optimize for speed (default) env.Append(CCFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer']) env.Append(LINKFLAGS=['-O2']) @@ -52,11 +52,11 @@ def configure(env): env.Append(LINKFLAGS=['-Os']) if env["target"] == "release_debug": - env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) + env.Append(CPPDEFINES=['DEBUG_ENABLED']) elif (env["target"] == "debug"): env.Append(CCFLAGS=['-gdwarf-2', '-O0']) - env.Append(CPPFLAGS=['-D_DEBUG', '-DDEBUG=1', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Append(CPPDEFINES=['_DEBUG', ('DEBUG', 1), 'DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED']) if (env["use_lto"]): env.Append(CCFLAGS=['-flto']) @@ -112,8 +112,8 @@ def configure(env): elif (env["arch"] == "arm64"): detect_darwin_sdk_path('iphone', env) env.Append(CCFLAGS='-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=10.0 -isysroot $IPHONESDK'.split()) - env.Append(CPPFLAGS=['-DNEED_LONG_INT']) - env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON']) + env.Append(CPPDEFINES=['NEED_LONG_INT']) + env.Append(CPPDEFINES=['LIBYUV_DISABLE_NEON']) # Disable exceptions on non-tools (template) builds if not env['tools']: @@ -144,6 +144,7 @@ def configure(env): '-framework', 'CoreAudio', '-framework', 'CoreGraphics', '-framework', 'CoreMedia', + '-framework', 'CoreVideo', '-framework', 'CoreMotion', '-framework', 'Foundation', '-framework', 'GameController', @@ -153,19 +154,20 @@ def configure(env): '-framework', 'Security', '-framework', 'SystemConfiguration', '-framework', 'UIKit', + '-framework', 'ARKit', ]) # Feature options if env['game_center']: - env.Append(CPPFLAGS=['-DGAME_CENTER_ENABLED']) + env.Append(CPPDEFINES=['GAME_CENTER_ENABLED']) env.Append(LINKFLAGS=['-framework', 'GameKit']) if env['store_kit']: - env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED']) + env.Append(CPPDEFINES=['STOREKIT_ENABLED']) env.Append(LINKFLAGS=['-framework', 'StoreKit']) if env['icloud']: - env.Append(CPPFLAGS=['-DICLOUD_ENABLED']) + env.Append(CPPDEFINES=['ICLOUD_ENABLED']) env.Prepend(CPPPATH=['$IPHONESDK/usr/include', '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', @@ -175,4 +177,4 @@ def configure(env): env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate' env.Prepend(CPPPATH=['#platform/iphone']) - env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DCOREAUDIO_ENABLED']) + env.Append(CPPDEFINES=['IPHONE_ENABLED', 'UNIX_ENABLED', 'GLES_ENABLED', 'COREAUDIO_ENABLED']) diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index ba405ab7ae..85a45d62f8 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -95,7 +95,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Vector<ExportArchitecture> _get_supported_architectures(); Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); - void _add_assets_to_project(Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); + void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, Vector<IOSExportAsset> &r_exported_assets); Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); @@ -125,7 +125,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { first = true; continue; } - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-')) { if (r_error) { *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); } @@ -137,7 +137,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { } return false; } - if (first && c == '_') { + if (first && c == '-') { if (r_error) { *r_error = vformat(TTR("The character '%s' cannot be the first character in a Identifier segment."), String::chr(c)); } @@ -268,8 +268,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/in_app_purchases"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description"), "Godot would like to use your camera")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description"), "Godot would like to use your photos")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_left"), true)); @@ -398,6 +399,9 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } else if (lines[i].find("$camera_usage_description") != -1) { String description = p_preset->get("privacy/camera_usage_description"); strnew += lines[i].replace("$camera_usage_description", description) + "\n"; + } else if (lines[i].find("$microphone_usage_description") != -1) { + String description = p_preset->get("privacy/microphone_usage_description"); + strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; } else if (lines[i].find("$photolibrary_usage_description") != -1) { String description = p_preset->get("privacy/photolibrary_usage_description"); strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n"; @@ -564,7 +568,7 @@ Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler dirs.push_back(path); } } else { - Error err = p_handler(current_dir + "/" + path, p_userdata); + Error err = p_handler(current_dir.plus_file(path), p_userdata); if (err) { p_da->list_dir_end(); return err; @@ -656,7 +660,7 @@ struct ExportLibsData { String dest_dir; }; -void EditorExportPlatformIOS::_add_assets_to_project(Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { +void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); Vector<String> frameworks; for (int i = 0; i < export_plugins.size(); ++i) { @@ -714,7 +718,28 @@ void EditorExportPlatformIOS::_add_assets_to_project(Vector<uint8_t> &p_project_ // Note, frameworks like gamekit are always included in our project.pbxprof file // even if turned off in capabilities. - // Frameworks that are used by modules (like arkit) we may need to optionally add here. + + // We do need our ARKit framework + if ((bool)p_preset->get("capabilities/arkit")) { + String build_id = (++current_id).str(); + String ref_id = (++current_id).str(); + + if (pbx_frameworks_build.length() > 0) { + pbx_frameworks_build += ",\n"; + pbx_frameworks_refs += ",\n"; + } + + pbx_frameworks_build += build_id; + pbx_frameworks_refs += ref_id; + + Dictionary format_dict; + format_dict["build_id"] = build_id; + format_dict["ref_id"] = ref_id; + format_dict["name"] = "ARKit.framework"; + format_dict["file_path"] = "System/Library/Frameworks/ARKit.framework"; + format_dict["file_type"] = "wrapper.framework"; + pbx_files += file_info_format.format(format_dict, "$_"); + } String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); str = str.replace("$additional_pbx_files", pbx_files); @@ -763,7 +788,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir } } - String destination = destination_dir + "/" + asset.get_file(); + String destination = destination_dir.plus_file(asset.get_file()); Error err = dir_exists ? da->copy_dir(asset, destination) : da->copy(asset, destination); memdelete(da); if (err) { @@ -914,7 +939,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p }; DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); print_line("Unzipping..."); FileAccess *src_f = NULL; @@ -1045,7 +1070,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p print_line("Exporting additional assets"); Vector<IOSExportAsset> assets; _export_additional_assets(dest_dir + binary_name, libraries, assets); - _add_assets_to_project(project_file_data, assets); + _add_assets_to_project(p_preset, project_file_data, assets); String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); if (!f) { diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 1cb8d0e44e..4641b2c4ac 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -494,7 +494,7 @@ static void clear_touches() { #ifdef DEBUG_ENABLED GLenum err = glGetError(); if (err) - NSLog(@"%x error", err); + NSLog(@"DrawView: %x error", err); #endif } diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index e32618e8f6..c60db3d661 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -138,7 +138,7 @@ Variant nsobject_to_variant(NSObject *object) { //this is a type that icloud supports...but how did you submit it in the first place? //I guess this is a type that *might* show up, if you were, say, trying to make your game //compatible with existing cloud data written by another engine's version of your game - WARN_PRINT("NSDate unsupported, returning null Variant") + WARN_PRINT("NSDate unsupported, returning null Variant"); return Variant(); } else if ([object isKindOfClass:[NSNull class]] or object == nil) { return Variant(); diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 6a65cadf09..f5fce66059 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -167,6 +167,8 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p input = memnew(InputDefault); + camera_server = memnew(CameraIOS); + #ifdef GAME_CENTER_ENABLED game_center = memnew(GameCenter); Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center)); @@ -361,6 +363,11 @@ void OSIPhone::finalize() { if (main_loop) // should not happen? memdelete(main_loop); + if (camera_server) { + memdelete(camera_server); + camera_server = NULL; + } + visual_server->finish(); memdelete(visual_server); // memdelete(rasterizer); diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 017125209c..c16c29a858 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -37,6 +37,7 @@ #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" +#include "camera_ios.h" #include "game_center.h" #include "icloud.h" #include "in_app_store.h" @@ -60,6 +61,8 @@ private: AudioDriverCoreAudio audio_driver; + CameraServer *camera_server; + #ifdef GAME_CENTER_ENABLED GameCenter *game_center; #endif |