Merge pull request #7127 from BastiaanOlij/ios_meters

Core motion implementation for iPhone (Accelerometer/Gyro/Magnetometer support)
This commit is contained in:
Rémi Verschelde 2017-01-15 00:08:46 +01:00 committed by GitHub
commit d4eb8ec884
9 changed files with 123 additions and 6 deletions

View File

@ -70,6 +70,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(_MD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); ClassDB::bind_method(_MD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
ClassDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
ClassDB::bind_method(_MD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); ClassDB::bind_method(_MD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
ClassDB::bind_method(_MD("get_gravity"),&Input::get_gravity);
ClassDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer); ClassDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer);
ClassDB::bind_method(_MD("get_magnetometer"),&Input::get_magnetometer); ClassDB::bind_method(_MD("get_magnetometer"),&Input::get_magnetometer);
ClassDB::bind_method(_MD("get_gyroscope"),&Input::get_gyroscope); ClassDB::bind_method(_MD("get_gyroscope"),&Input::get_gyroscope);

View File

@ -82,6 +82,7 @@ public:
virtual void warp_mouse_pos(const Vector2& p_to)=0; virtual void warp_mouse_pos(const Vector2& p_to)=0;
virtual Vector3 get_gravity() const=0;
virtual Vector3 get_accelerometer() const=0; virtual Vector3 get_accelerometer() const=0;
virtual Vector3 get_magnetometer() const=0; virtual Vector3 get_magnetometer() const=0;
virtual Vector3 get_gyroscope() const=0; virtual Vector3 get_gyroscope() const=0;

View File

@ -277,6 +277,12 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_
emit_signal("joy_connection_changed", p_idx, p_connected); emit_signal("joy_connection_changed", p_idx, p_connected);
}; };
Vector3 InputDefault::get_gravity() const{
_THREAD_SAFE_METHOD_
return gravity;
}
Vector3 InputDefault::get_accelerometer() const{ Vector3 InputDefault::get_accelerometer() const{
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
@ -423,6 +429,14 @@ void InputDefault::stop_joy_vibration(int p_device) {
joy_vibration[p_device] = vibration; joy_vibration[p_device] = vibration;
} }
void InputDefault::set_gravity(const Vector3& p_gravity) {
_THREAD_SAFE_METHOD_
gravity=p_gravity;
}
void InputDefault::set_accelerometer(const Vector3& p_accel) { void InputDefault::set_accelerometer(const Vector3& p_accel) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_

View File

@ -44,6 +44,7 @@ class InputDefault : public Input {
Set<int> joy_buttons_pressed; Set<int> joy_buttons_pressed;
Map<int,float> _joy_axis; Map<int,float> _joy_axis;
//Map<StringName,int> custom_action_press; //Map<StringName,int> custom_action_press;
Vector3 gravity;
Vector3 accelerometer; Vector3 accelerometer;
Vector3 magnetometer; Vector3 magnetometer;
Vector3 gyroscope; Vector3 gyroscope;
@ -191,6 +192,7 @@ public:
void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "");
void parse_joypad_mapping(String p_mapping, bool p_update_existing); void parse_joypad_mapping(String p_mapping, bool p_update_existing);
virtual Vector3 get_gravity() const;
virtual Vector3 get_accelerometer() const; virtual Vector3 get_accelerometer() const;
virtual Vector3 get_magnetometer() const; virtual Vector3 get_magnetometer() const;
virtual Vector3 get_gyroscope() const; virtual Vector3 get_gyroscope() const;
@ -203,6 +205,7 @@ public:
void parse_input_event(const InputEvent& p_event); void parse_input_event(const InputEvent& p_event);
void set_gravity(const Vector3& p_gravity);
void set_accelerometer(const Vector3& p_accel); void set_accelerometer(const Vector3& p_accel);
void set_magnetometer(const Vector3& p_magnetometer); void set_magnetometer(const Vector3& p_magnetometer);
void set_gyroscope(const Vector3& p_gyroscope); void set_gyroscope(const Vector3& p_gyroscope);

View File

@ -30,11 +30,13 @@
#import "gl_view.h" #import "gl_view.h"
#import "view_controller.h" #import "view_controller.h"
@interface AppDelegate : NSObject <UIApplicationDelegate, UIAccelerometerDelegate, GLViewDelegate> { // Old accelerometer approach deprecated since IOS 7.0
// Include coremotion for accelerometer, gyroscope and magnetometer access, available since IOS 4.0 but some functionality won't work for anything before IOS 5.0
#import <CoreMotion/CoreMotion.h>
@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//@property (strong, nonatomic) UIWindow *window; //@property (strong, nonatomic) UIWindow *window;
ViewController* view_controller; ViewController* view_controller;
UIAccelerationValue accel[3];
UIAccelerationValue last_accel[3];
}; };
@property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UIWindow *window;

View File

@ -84,6 +84,9 @@ extern char** gargv;
extern int iphone_main(int, int, int, char**); extern int iphone_main(int, int, int, char**);
extern void iphone_finish(); extern void iphone_finish();
CMMotionManager *motionManager;
bool motionInitialised;
static ViewController* mainViewController = nil; static ViewController* mainViewController = nil;
+ (ViewController*) getViewController + (ViewController*) getViewController
{ {
@ -193,9 +196,58 @@ static int frame_count = 0;
}; break; // no fallthrough }; break; // no fallthrough
default: { default: {
if (OSIPhone::get_singleton()) { if (OSIPhone::get_singleton()) {
OSIPhone::get_singleton()->update_accelerometer(accel[0], accel[1], accel[2]); // OSIPhone::get_singleton()->update_accelerometer(accel[0], accel[1], accel[2]);
if (motionInitialised) {
// Just using polling approach for now, we can set this up so it sends data to us in intervals, might be better.
// See Apple reference pages for more details:
// https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
// Apple splits our accelerometer date into a gravity and user movement component. We add them back together
CMAcceleration gravity = motionManager.deviceMotion.gravity;
CMAcceleration acceleration = motionManager.deviceMotion.userAcceleration;
///@TODO We don't seem to be getting data here, is my device broken or is this code incorrect?
CMMagneticField magnetic = motionManager.deviceMotion.magneticField.field;
///@TODO we can access rotationRate as a CMRotationRate variable (processed date) or CMGyroData (raw data), have to see what works best
CMRotationRate rotation = motionManager.deviceMotion.rotationRate;
// Adjust for screen orientation.
// [[UIDevice currentDevice] orientation] changes even if we've fixed our orientation which is not
// a good thing when you're trying to get your user to move the screen in all directions and want consistent output
///@TODO Using [[UIApplication sharedApplication] statusBarOrientation] is a bit of a hack. Godot obviously knows the orientation so maybe we
// can use that instead? (note that left and right seem swapped)
switch ([[UIApplication sharedApplication] statusBarOrientation]) {
case UIDeviceOrientationLandscapeLeft: {
OSIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x, gravity.z);
OSIPhone::get_singleton()->update_accelerometer(-(acceleration.y + gravity.y), (acceleration.x + gravity.x), acceleration.z + gravity.z);
OSIPhone::get_singleton()->update_magnetometer(-magnetic.y, magnetic.x, magnetic.z);
OSIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x, rotation.z);
}; break;
case UIDeviceOrientationLandscapeRight: {
OSIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x, gravity.z);
OSIPhone::get_singleton()->update_accelerometer((acceleration.y + gravity.y), -(acceleration.x + gravity.x), acceleration.z + gravity.z);
OSIPhone::get_singleton()->update_magnetometer(magnetic.y, -magnetic.x, magnetic.z);
OSIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x, rotation.z);
}; break;
case UIDeviceOrientationPortraitUpsideDown: {
OSIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y, gravity.z);
OSIPhone::get_singleton()->update_accelerometer(-(acceleration.x + gravity.x), (acceleration.y + gravity.y), acceleration.z + gravity.z);
OSIPhone::get_singleton()->update_magnetometer(-magnetic.x, magnetic.y, magnetic.z);
OSIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y, rotation.z);
}; break;
default: { // assume portrait
OSIPhone::get_singleton()->update_gravity(gravity.x, gravity.y, gravity.z);
OSIPhone::get_singleton()->update_accelerometer(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z);
OSIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y, magnetic.z);
OSIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y, rotation.z);
}; break;
};
}
bool quit_request = OSIPhone::get_singleton()->iterate(); bool quit_request = OSIPhone::get_singleton()->iterate();
}; };
@ -253,11 +305,24 @@ static int frame_count = 0;
[window makeKeyAndVisible]; [window makeKeyAndVisible];
//Configure and start accelerometer //Configure and start accelerometer
/*
Old accelerometer approach deprecated since IOS 7.0
last_accel[0] = 0; last_accel[0] = 0;
last_accel[1] = 0; last_accel[1] = 0;
last_accel[2] = 0; last_accel[2] = 0;
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)]; [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self]; [[UIAccelerometer sharedAccelerometer] setDelegate:self];
*/
if (!motionInitialised) {
motionManager = [[CMMotionManager alloc] init];
if (motionManager.deviceMotionAvailable) {
motionManager.deviceMotionUpdateInterval = 1.0/70.0;
[motionManager startDeviceMotionUpdates];
motionInitialised = YES;
};
};
//OSIPhone::screen_width = rect.size.width - rect.origin.x; //OSIPhone::screen_width = rect.size.width - rect.origin.x;
//OSIPhone::screen_height = rect.size.height - rect.origin.y; //OSIPhone::screen_height = rect.size.height - rect.origin.y;
@ -297,12 +362,23 @@ static int frame_count = 0;
- (void)applicationWillTerminate:(UIApplication*)application { - (void)applicationWillTerminate:(UIApplication*)application {
printf("********************* will terminate\n"); printf("********************* will terminate\n");
if (motionInitialised) {
///@TODO is this the right place to clean this up?
[motionManager stopDeviceMotionUpdates];
[motionManager release];
motionManager = nil;
motionInitialised = NO;
};
iphone_finish(); iphone_finish();
}; };
- (void)applicationDidEnterBackground:(UIApplication *)application - (void)applicationDidEnterBackground:(UIApplication *)application
{ {
printf("********************* did enter background\n"); printf("********************* did enter background\n");
///@TODO maybe add pause motionManager? and where would we unpause it?
if (OS::get_singleton()->get_main_loop()) if (OS::get_singleton()->get_main_loop())
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
[view_controller.view stopAnimation]; [view_controller.view stopAnimation];
@ -340,12 +416,15 @@ static int frame_count = 0;
}; };
} }
/*
Depricated since IOS 7.0
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration { - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration {
//Use a basic low-pass filter to only keep the gravity in the accelerometer values //Use a basic low-pass filter to only keep the gravity in the accelerometer values
accel[0] = acceleration.x; // * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor); accel[0] = acceleration.x; // * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor);
accel[1] = acceleration.y; // * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor); accel[1] = acceleration.y; // * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor);
accel[2] = acceleration.z; // * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor); accel[2] = acceleration.z; // * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor);
} }
*/
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
#ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED #ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED

View File

@ -83,6 +83,7 @@ def configure(env):
'-framework', 'CoreAudio', '-framework', 'CoreAudio',
'-framework', 'CoreGraphics', '-framework', 'CoreGraphics',
'-framework', 'CoreMedia', '-framework', 'CoreMedia',
'-framework', 'CoreMotion',
'-framework', 'Foundation', '-framework', 'Foundation',
'-framework', 'Security', '-framework', 'Security',
'-framework', 'UIKit', '-framework', 'UIKit',
@ -109,6 +110,7 @@ def configure(env):
'-framework', 'MediaPlayer', '-framework', 'MediaPlayer',
'-framework', 'AVFoundation', '-framework', 'AVFoundation',
'-framework', 'CoreMedia', '-framework', 'CoreMedia',
'-framework', 'CoreMotion',
]) ])
else: else:
env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=5.1.1', env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=5.1.1',
@ -126,6 +128,7 @@ def configure(env):
'-framework', 'MediaPlayer', '-framework', 'MediaPlayer',
'-framework', 'AVFoundation', '-framework', 'AVFoundation',
'-framework', 'CoreMedia', '-framework', 'CoreMedia',
'-framework', 'CoreMotion',
]) ])
if env['game_center'] == 'yes': if env['game_center'] == 'yes':

View File

@ -323,9 +323,14 @@ void OSIPhone::touches_cancelled() {
static const float ACCEL_RANGE = 1; static const float ACCEL_RANGE = 1;
void OSIPhone::update_gravity(float p_x, float p_y, float p_z) {
input->set_gravity(Vector3(p_x, p_y, p_z));
};
void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) { void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
input->set_accelerometer(Vector3(p_x / (float)ACCEL_RANGE, p_y / (float)ACCEL_RANGE, -p_z / (float)ACCEL_RANGE)); // Found out the Z should not be negated! Pass as is!
input->set_accelerometer(Vector3(p_x / (float)ACCEL_RANGE, p_y / (float)ACCEL_RANGE, p_z / (float)ACCEL_RANGE));
/* /*
if (p_x != last_accel.x) { if (p_x != last_accel.x) {
@ -364,7 +369,13 @@ void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
*/ */
}; };
void OSIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
input->set_magnetometer(Vector3(p_x, p_y, p_z));
};
void OSIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
input->set_gyroscope(Vector3(p_x, p_y, p_z));
};
void OSIPhone::delete_main_loop() { void OSIPhone::delete_main_loop() {

View File

@ -152,7 +152,10 @@ public:
int set_base_framebuffer(int p_fb); int set_base_framebuffer(int p_fb);
void update_gravity(float p_x, float p_y, float p_z);
void update_accelerometer(float p_x, float p_y, float p_z); void update_accelerometer(float p_x, float p_y, float p_z);
void update_magnetometer(float p_x, float p_y, float p_z);
void update_gyroscope(float p_x, float p_y, float p_z);
static OSIPhone* get_singleton(); static OSIPhone* get_singleton();