1 // Copyright (C) 2002-2008 Nikolaus Gebhardt
\r
2 // Copyright (C) 2008 Redshift Software, Inc.
\r
3 // Copyright (C) 2012 Patryk Nadrowski
\r
4 // This file is part of the "Irrlicht Engine".
\r
5 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
7 #import "CIrrDeviceiOS.h"
\r
9 #ifdef _IRR_COMPILE_WITH_IOS_DEVICE_
\r
11 #include "IFileSystem.h"
\r
13 #include "CEAGLManager.h"
\r
15 #import <UIKit/UIKit.h>
\r
16 #import <CoreMotion/CoreMotion.h>
\r
18 /* Important information */
\r
20 // The application state events and following methods: IrrlichtDevice::isWindowActive, IrrlichtDevice::isWindowFocused
\r
21 // and IrrlichtDevice::isWindowMinimized works out of box only if you'll use built-in CIrrDelegateiOS,
\r
22 // so _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_ must be enabled in this case. If you need a custom UIApplicationDelegate you must
\r
23 // handle all application events yourself.
\r
25 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
29 class CIrrDeviceiOS;
\r
32 /* CIrrDelegateiOS */
\r
34 @interface CIrrDelegateiOS : NSObject<UIApplicationDelegate>
\r
36 - (void)setDevice:(irr::CIrrDeviceiOS*)device;
\r
40 @property (strong, nonatomic) UIWindow* window;
\r
44 @implementation CIrrDelegateiOS
\r
46 irr::CIrrDeviceiOS* Device;
\r
51 - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)options
\r
57 [self performSelectorOnMainThread:@selector(runIrrlicht) withObject:nil waitUntilDone:NO];
\r
62 - (void)applicationWillTerminate:(UIApplication*)application
\r
67 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
68 ev.ApplicationEvent.EventType = irr::EAET_WILL_TERMINATE;
\r
70 Device->postEventFromUser(ev);
\r
72 Device->closeDevice();
\r
76 - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
\r
81 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
82 ev.ApplicationEvent.EventType = irr::EAET_MEMORY_WARNING;
\r
84 Device->postEventFromUser(ev);
\r
88 - (void)applicationWillResignActive:(UIApplication*)application
\r
93 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
94 ev.ApplicationEvent.EventType = irr::EAET_WILL_PAUSE;
\r
96 Device->postEventFromUser(ev);
\r
102 - (void)applicationDidEnterBackground:(UIApplication*)application
\r
107 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
108 ev.ApplicationEvent.EventType = irr::EAET_DID_PAUSE;
\r
110 Device->postEventFromUser(ev);
\r
116 - (void)applicationWillEnterForeground:(UIApplication*)application
\r
121 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
122 ev.ApplicationEvent.EventType = irr::EAET_WILL_RESUME;
\r
124 Device->postEventFromUser(ev);
\r
130 - (void)applicationDidBecomeActive:(UIApplication*)application
\r
135 ev.EventType = irr::EET_APPLICATION_EVENT;
\r
136 ev.ApplicationEvent.EventType = irr::EAET_DID_RESUME;
\r
138 Device->postEventFromUser(ev);
\r
144 - (void)runIrrlicht
\r
149 - (void)setDevice:(irr::CIrrDeviceiOS*)device
\r
170 @interface CIrrViewiOS : UIView
\r
172 - (id)initWithFrame:(CGRect)frame forDevice:(irr::CIrrDeviceiOS*)device;
\r
176 @implementation CIrrViewiOS
\r
178 irr::CIrrDeviceiOS* Device;
\r
182 - (id)initWithFrame:(CGRect)frame forDevice:(irr::CIrrDeviceiOS*)device;
\r
184 self = [super initWithFrame:frame];
\r
189 Scale = ([self respondsToSelector:@selector(setContentScaleFactor:)]) ? [[UIScreen mainScreen] scale] : 1.f;
\r
195 - (BOOL)isMultipleTouchEnabled
\r
200 - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
\r
203 ev.EventType = irr::EET_TOUCH_INPUT_EVENT;
\r
204 ev.TouchInput.Event = irr::ETIE_PRESSED_DOWN;
\r
206 for (UITouch* touch in touches)
\r
208 ev.TouchInput.ID = (size_t)touch;
\r
210 CGPoint touchPoint = [touch locationInView:self];
\r
212 ev.TouchInput.X = touchPoint.x*Scale;
\r
213 ev.TouchInput.Y = touchPoint.y*Scale;
\r
215 Device->postEventFromUser(ev);
\r
219 - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
\r
222 ev.EventType = irr::EET_TOUCH_INPUT_EVENT;
\r
223 ev.TouchInput.Event = irr::ETIE_MOVED;
\r
225 for (UITouch* touch in touches)
\r
227 ev.TouchInput.ID = (size_t)touch;
\r
229 CGPoint touchPoint = [touch locationInView:self];
\r
231 ev.TouchInput.X = touchPoint.x*Scale;
\r
232 ev.TouchInput.Y = touchPoint.y*Scale;
\r
234 Device->postEventFromUser(ev);
\r
238 - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
\r
241 ev.EventType = irr::EET_TOUCH_INPUT_EVENT;
\r
242 ev.TouchInput.Event = irr::ETIE_LEFT_UP;
\r
244 for (UITouch* touch in touches)
\r
246 ev.TouchInput.ID = (size_t)touch;
\r
248 CGPoint touchPoint = [touch locationInView:self];
\r
250 ev.TouchInput.X = touchPoint.x*Scale;
\r
251 ev.TouchInput.Y = touchPoint.y*Scale;
\r
253 Device->postEventFromUser(ev);
\r
257 - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
\r
260 ev.EventType = irr::EET_TOUCH_INPUT_EVENT;
\r
261 ev.TouchInput.Event = irr::ETIE_LEFT_UP;
\r
263 for (UITouch* touch in touches)
\r
265 ev.TouchInput.ID = (size_t)touch;
\r
267 CGPoint touchPoint = [touch locationInView:self];
\r
269 ev.TouchInput.X = touchPoint.x*Scale;
\r
270 ev.TouchInput.Y = touchPoint.y*Scale;
\r
272 Device->postEventFromUser(ev);
\r
278 /* CIrrViewEAGLiOS */
\r
280 @interface CIrrViewEAGLiOS : CIrrViewiOS
\r
284 @implementation CIrrViewEAGLiOS
\r
286 + (Class)layerClass
\r
288 return [CAEAGLLayer class];
\r
297 IVideoDriver* createOGLES1Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
299 IVideoDriver* createOGLES2Driver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
302 struct SIrrDeviceiOSDataStorage
\r
304 SIrrDeviceiOSDataStorage() : Window(0), ViewController(0), View(0), MotionManager(0), ReferenceAttitude(0)
\r
306 MotionManager = [[CMMotionManager alloc] init];
\r
310 UIViewController* ViewController;
\r
312 CMMotionManager* MotionManager;
\r
313 CMAttitude* ReferenceAttitude;
\r
316 CIrrDeviceiOS::CIrrDeviceiOS(const SIrrlichtCreationParameters& params) : CIrrDeviceStub(params), DataStorage(0), Close(false)
\r
319 setDebugName("CIrrDeviceiOS");
\r
322 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
323 CIrrDelegateiOS* delegate = [UIApplication sharedApplication].delegate;
\r
324 [delegate setDevice:this];
\r
327 DataStorage = new SIrrDeviceiOSDataStorage();
\r
329 FileSystem->changeWorkingDirectoryTo([[[NSBundle mainBundle] resourcePath] UTF8String]);
\r
332 createViewAndDriver();
\r
337 createGUIAndScene();
\r
340 CIrrDeviceiOS::~CIrrDeviceiOS()
\r
342 deactivateDeviceMotion();
\r
343 deactivateGyroscope();
\r
344 deactivateAccelerometer();
\r
346 delete static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
348 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
349 CIrrDelegateiOS* delegate = [UIApplication sharedApplication].delegate;
\r
350 [delegate setDevice:nil];
\r
354 bool CIrrDeviceiOS::run()
\r
358 const CFTimeInterval timeInSeconds = 0.000002;
\r
364 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeInSeconds, TRUE);
\r
366 while (result == kCFRunLoopRunHandledSource);
\r
372 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
373 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
376 if (motionManager.isAccelerometerActive)
\r
379 ev.EventType = irr::EET_ACCELEROMETER_EVENT;
\r
380 ev.AccelerometerEvent.X = motionManager.accelerometerData.acceleration.x;
\r
381 ev.AccelerometerEvent.Y = motionManager.accelerometerData.acceleration.y;
\r
382 ev.AccelerometerEvent.Z = motionManager.accelerometerData.acceleration.z;
\r
384 postEventFromUser(ev);
\r
388 if (motionManager.isGyroActive)
\r
391 ev.EventType = irr::EET_GYROSCOPE_EVENT;
\r
392 ev.GyroscopeEvent.X = motionManager.gyroData.rotationRate.x;
\r
393 ev.GyroscopeEvent.Y = motionManager.gyroData.rotationRate.y;
\r
394 ev.GyroscopeEvent.Z = motionManager.gyroData.rotationRate.z;
\r
396 postEventFromUser(ev);
\r
400 if (motionManager.isDeviceMotionActive)
\r
402 CMAttitude* currentAttitude = motionManager.deviceMotion.attitude;
\r
403 CMAttitude* referenceAttitude = dataStorage->ReferenceAttitude;
\r
405 if (referenceAttitude != nil)
\r
406 [currentAttitude multiplyByInverseOfAttitude: referenceAttitude];
\r
408 referenceAttitude = motionManager.deviceMotion.attitude;
\r
411 ev.EventType = irr::EET_DEVICE_MOTION_EVENT;
\r
412 ev.AccelerometerEvent.X = currentAttitude.roll;
\r
413 ev.AccelerometerEvent.Y = currentAttitude.pitch;
\r
414 ev.AccelerometerEvent.Z = currentAttitude.yaw;
\r
416 postEventFromUser(ev);
\r
423 void CIrrDeviceiOS::yield()
\r
425 struct timespec ts = {0,0};
\r
426 nanosleep(&ts, NULL);
\r
429 void CIrrDeviceiOS::sleep(u32 timeMs, bool pauseTimer=false)
\r
431 bool wasStopped = Timer ? Timer->isStopped() : true;
\r
433 struct timespec ts;
\r
434 ts.tv_sec = (time_t) (timeMs / 1000);
\r
435 ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
\r
437 if (pauseTimer && !wasStopped)
\r
440 nanosleep(&ts, NULL);
\r
442 if (pauseTimer && !wasStopped)
\r
446 void CIrrDeviceiOS::setWindowCaption(const wchar_t* text)
\r
450 bool CIrrDeviceiOS::isWindowActive() const
\r
452 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
453 CIrrDelegateiOS* delegate = [UIApplication sharedApplication].delegate;
\r
455 return [delegate isActive];
\r
461 bool CIrrDeviceiOS::isWindowFocused() const
\r
463 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
464 CIrrDelegateiOS* delegate = [UIApplication sharedApplication].delegate;
\r
466 return [delegate hasFocus];
\r
472 bool CIrrDeviceiOS::isWindowMinimized() const
\r
474 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
475 CIrrDelegateiOS* delegate = [UIApplication sharedApplication].delegate;
\r
477 return ![delegate isActive];
\r
483 void CIrrDeviceiOS::closeDevice()
\r
485 CFRunLoopStop(CFRunLoopGetMain());
\r
490 void CIrrDeviceiOS::setResizable(bool resize)
\r
494 void CIrrDeviceiOS::minimizeWindow()
\r
498 void CIrrDeviceiOS::maximizeWindow()
\r
502 void CIrrDeviceiOS::restoreWindow()
\r
506 core::position2di CIrrDeviceiOS::getWindowPosition()
\r
508 return core::position2di(0, 0);
\r
511 bool CIrrDeviceiOS::activateAccelerometer(float updateInterval)
\r
513 bool status = false;
\r
515 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
516 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
518 if (motionManager.isAccelerometerAvailable)
\r
520 if (!motionManager.isAccelerometerActive)
\r
522 motionManager.accelerometerUpdateInterval = updateInterval;
\r
523 [motionManager startAccelerometerUpdates];
\r
532 bool CIrrDeviceiOS::deactivateAccelerometer()
\r
534 bool status = false;
\r
536 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
537 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
539 if (motionManager.isAccelerometerAvailable)
\r
541 if (motionManager.isAccelerometerActive)
\r
542 [motionManager stopAccelerometerUpdates];
\r
550 bool CIrrDeviceiOS::isAccelerometerActive()
\r
552 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
554 return (dataStorage->MotionManager.isAccelerometerActive);
\r
557 bool CIrrDeviceiOS::isAccelerometerAvailable()
\r
559 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
561 return (dataStorage->MotionManager.isAccelerometerAvailable);
\r
564 bool CIrrDeviceiOS::activateGyroscope(float updateInterval)
\r
566 bool status = false;
\r
568 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
569 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
571 if (motionManager.isGyroAvailable)
\r
573 if (!motionManager.isGyroActive)
\r
575 motionManager.gyroUpdateInterval = updateInterval;
\r
576 [motionManager startGyroUpdates];
\r
585 bool CIrrDeviceiOS::deactivateGyroscope()
\r
587 bool status = false;
\r
589 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
590 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
592 if (motionManager.isGyroAvailable)
\r
594 if (motionManager.isGyroActive)
\r
595 [motionManager stopGyroUpdates];
\r
603 bool CIrrDeviceiOS::isGyroscopeActive()
\r
605 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
607 return (dataStorage->MotionManager.isGyroActive);
\r
610 bool CIrrDeviceiOS::isGyroscopeAvailable()
\r
612 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
614 return (dataStorage->MotionManager.isGyroAvailable);
\r
617 bool CIrrDeviceiOS::activateDeviceMotion(float updateInterval)
\r
619 bool status = false;
\r
621 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
622 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
624 if (motionManager.isDeviceMotionAvailable)
\r
626 if (!motionManager.isDeviceMotionActive)
\r
628 dataStorage->ReferenceAttitude = nil;
\r
630 motionManager.deviceMotionUpdateInterval = updateInterval;
\r
631 [motionManager startDeviceMotionUpdates];
\r
640 bool CIrrDeviceiOS::deactivateDeviceMotion()
\r
642 bool status = false;
\r
644 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
645 CMMotionManager* motionManager = dataStorage->MotionManager;
\r
647 if (motionManager.isDeviceMotionAvailable)
\r
649 if (motionManager.isDeviceMotionActive)
\r
651 [motionManager stopDeviceMotionUpdates];
\r
653 dataStorage->ReferenceAttitude = nil;
\r
662 bool CIrrDeviceiOS::isDeviceMotionActive()
\r
664 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
666 return (dataStorage->MotionManager.isDeviceMotionActive);
\r
669 bool CIrrDeviceiOS::isDeviceMotionAvailable()
\r
671 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
673 return (dataStorage->MotionManager.isDeviceMotionAvailable);
\r
676 E_DEVICE_TYPE CIrrDeviceiOS::getType() const
\r
681 void CIrrDeviceiOS::createWindow()
\r
683 if (CreationParams.DriverType != video::EDT_NULL)
\r
685 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
687 UIView* externalView = (__bridge UIView*)CreationParams.WindowId;
\r
689 if (externalView == nil)
\r
691 dataStorage->Window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
\r
692 dataStorage->ViewController = [[UIViewController alloc] init];
\r
693 dataStorage->Window.rootViewController = dataStorage->ViewController;
\r
695 [dataStorage->Window makeKeyAndVisible];
\r
699 dataStorage->Window = externalView.window;
\r
701 UIResponder* currentResponder = externalView.nextResponder;
\r
705 if ([currentResponder isKindOfClass:[UIViewController class]])
\r
707 dataStorage->ViewController = (UIViewController*)currentResponder;
\r
709 currentResponder = nil;
\r
711 else if ([currentResponder isKindOfClass:[UIView class]])
\r
713 currentResponder = currentResponder.nextResponder;
\r
717 currentResponder = nil;
\r
719 // Could not find view controller.
\r
720 _IRR_DEBUG_BREAK_IF(true);
\r
723 while (currentResponder != nil);
\r
728 void CIrrDeviceiOS::createViewAndDriver()
\r
730 SIrrDeviceiOSDataStorage* dataStorage = static_cast<SIrrDeviceiOSDataStorage*>(DataStorage);
\r
732 video::SExposedVideoData data;
\r
733 data.OpenGLiOS.Window = (__bridge void*)dataStorage->Window;
\r
734 data.OpenGLiOS.ViewController = (__bridge void*)dataStorage->ViewController;
\r
736 UIView* externalView = (__bridge UIView*)CreationParams.WindowId;
\r
738 CGRect resolution = (externalView == nil) ? [[UIScreen mainScreen] bounds] : externalView.bounds;
\r
740 switch (CreationParams.DriverType)
\r
742 case video::EDT_OGLES1:
\r
743 #ifdef _IRR_COMPILE_WITH_OGLES1_
\r
745 CIrrViewEAGLiOS* view = [[CIrrViewEAGLiOS alloc] initWithFrame:resolution forDevice:this];
\r
746 CreationParams.WindowSize = core::dimension2d<u32>(view.frame.size.width, view.frame.size.height);
\r
748 dataStorage->View = view;
\r
749 data.OpenGLiOS.View = (__bridge void*)view;
\r
751 ContextManager = new video::CEAGLManager();
\r
752 ContextManager->initialize(CreationParams, data);
\r
754 VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);
\r
757 os::Printer::log("Could not create OpenGL ES 1.x driver.", ELL_ERROR);
\r
760 os::Printer::log("No OpenGL ES 1.x support compiled in.", ELL_ERROR);
\r
764 case video::EDT_OGLES2:
\r
765 #ifdef _IRR_COMPILE_WITH_OGLES2_
\r
767 CIrrViewEAGLiOS* view = [[CIrrViewEAGLiOS alloc] initWithFrame:resolution forDevice:this];
\r
768 CreationParams.WindowSize = core::dimension2d<u32>(view.frame.size.width, view.frame.size.height);
\r
770 dataStorage->View = view;
\r
771 data.OpenGLiOS.View = (__bridge void*)view;
\r
773 ContextManager = new video::CEAGLManager();
\r
774 ContextManager->initialize(CreationParams, data);
\r
776 VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);
\r
779 os::Printer::log("Could not create OpenGL ES 2.x driver.", ELL_ERROR);
\r
782 os::Printer::log("No OpenGL ES 2.x support compiled in.", ELL_ERROR);
\r
786 case video::EDT_SOFTWARE:
\r
787 case video::EDT_BURNINGSVIDEO:
\r
788 case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:
\r
789 case video::EDT_DIRECT3D9:
\r
790 case video::EDT_OPENGL:
\r
791 os::Printer::log("This driver is not available in iOS. Try OpenGL ES.", ELL_ERROR);
\r
794 case video::EDT_NULL:
\r
795 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
\r
799 os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
\r
803 if (externalView == nil)
\r
804 dataStorage->ViewController.view = dataStorage->View;
\r
806 [externalView addSubview:dataStorage->View];
\r
810 #ifdef _IRR_COMPILE_WITH_IOS_BUILTIN_MAIN_
\r
811 int main(int argc, char** argv)
\r
813 int result = UIApplicationMain(argc, argv, 0, NSStringFromClass([CIrrDelegateiOS class]));
\r