iPhone rotation applied to a 3d object - ios

The quaternion you get from CMAttitude seems flawed. In Unity I can get the iPhone's rotation, apply it to a 3d object, and the object will rotate as you rotate the iPhone. In Xcode it seems to be different.
To set up a quick test project, follow these steps:
In Xcode, create a new project from the iOS > Game template (this gives us a 3d object to test on).
In GameViewController.h add #import <CoreMotion/CoreMotion.h> and #property (strong, nonatomic) CMMotionManager *motionManager;
In GameViewController.m remove the animation at line:46 [ship runAction: etc.. and don't allow camera control at line:55 (not necessary)
In GameViewController.m add to bottom of ViewDidLoad
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = .1;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error) {
CMQuaternion q = motion.attitude.quaternion;
ship.rotation = SCNVector4Make(q.x, q.y, q.z, q.w);
if(error){ NSLog(#"%#", error); }
} ];
To avoid annoyances, in Project > Targets > Project Name > Deployment Info > Device Orientation, allow only Portrait (or just lock your iPhone's rotation)
When I build this to my iPhone the 3d object (an airplane) doesn't follow the rotation of the phone. Pitching kind of works.
Is this intended? How am I using this wrong?

In 3D space there are different representations of rotations.
You are using the rotation property of SCNNode:
The four-component rotation vector specifies the direction
of the rotation axis in the first three components and the
angle of rotation (in radians) in the fourth.
But you are assigning the device rotation as quaternion.
Either assign the quaternion to the orientation property of SCNNode or use the yaw, pitch and roll angles of CMAttitude and assign these to the eulerAngles property of SCNNode:
The node’s orientation, expressed as pitch, yaw, and roll angles,
each in radians.

Related

Make a shape's position fixed on iOS camera preview

I am working on an iOS camera based app, in which I have to capture a first point and then I need to draw the line to the current focus point to the first captured point. MagicPlan works this way.
Here is an image:
I have tried to fix a point for first point using accelerometer values and the tilted angle of the device. But, no luck so far. And how would i draw the line to the second point from the first point?
This is the code that i have tried so far:
if (self.motionManager.deviceMotionAvailable)
{
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error) {
CATransform3D transform;
transform = CATransform3DMakeRotation(motion.attitude.pitch, 1, 0, 0);
transform = CATransform3DRotate(transform,motion.attitude.roll, 0, 1, 0);
transform = CATransform3DRotate(transform,motion.attitude.yaw, 0, 0, 1);
self.viewObject.layer.transform = transform;
}];
}
if (self.motionManager.deviceMotionActive)
{
/**
* Pulling gravity values from deviceMotion sensor
*/
CGFloat x = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.x];
CGFloat y = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.y];
CGFloat z = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.z];
CGFloat r = sqrtf(x*x + y*y + z*z);
/**
* Calculating device forward/backward title angle in degrees
*/
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f;
[self.lblTilForwardBackward setText:[#(tiltForwardBackward) stringValue]];
}
You have a lot of issues to resolve here. It isn't just a matter of adjusting for camera orientation as the height that the camera is being held at and position of the camera in the room are also changing. Even in MagicPlan, when the person turns around, the camera moves (rotates about the axis going through the person's head down to his feet).
There is quite a lot of algebra and rotation/translation matrix operations to work out. No one is going to do this for you. You'll have to figure it all out and derive it yourself (or look it up from old graphics text books).
I suggest doing something as straight forward and multi-step as possible (so you can debug each step along the way). Assume flat ground (indoor environment).
Get camera position/orientation/focal length from the first snapshot.
Figure out the touch point in real world Cartesian coordinates(start with video coordinates and translate via roll/pitch/yaw and ray traced projection to ground plane(using camera height).
From the focal length you can figure out the field of view and depth to center of field of view and using camera orientation and click distance from center of screen determine xyz offset from some origin (your feet maybe).
Determine and track camera position and orientation relative to that origin.
On second snapshot (or motion awake), figure out (center or touched point) distance from origin and exact xyz (as above).
Once you have those two points in xyz you can plot the line by taking the standard orthogonal projection onto the view plane. Clipping as needed in case original point is out of the FOV.

scenekit node rotation and movement in 3D Space

Let's say we have a SCNNode and we want to rotate, change flight direction and move in space with no gravity. The node currently is just a camera. actually it should react to gyroscope accelerometer data. i think scene update can be done in - (void)renderer:(id <SCNSceneRenderer>)aRenderer didSimulatePhysicsAtTime:(NSTimeInterval)time.
The problem I'm facing is how to utilise the gyroscope & accelerometer data for calculating flight direction, and how to calculate the rotation direction & movement. I refreshed my memory about 3D rotation matrix for each axis(x,y,z) but still missing above mentioned part to solve this problem. May be thinking very complicated and the solution is very easy.
- (void)viewDidLoad
{
SCNView *scnView = (SCNView *) self.view;
SCNScene *scene = [SCNScene scene];
scnView.scene = scene;
_cameraNode = [[SCNNode alloc] init];
_cameraNode.camera = [SCNCamera camera];
_cameraNode.camera.zFar = 500;
_cameraNode.position = SCNVector3Make(0, 60, 50);
_cameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI_4*0.75);
[scene.rootNode addChildNode:_cameraNode];
scnView.pointOfView = _cameraNode;
[self setupAccelerometer];
}
- (void)setupAccelerometer
{
_motionManager = [[CMMotionManager alloc] init];
GameViewController * __weak weakSelf = self;
if ([[GCController controllers] count] == 0 && [_motionManager isAccelerometerAvailable] == YES) {
[_motionManager setAccelerometerUpdateInterval:1/60.0];
[_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
[weakSelf accelerometerDidChange:accelerometerData.acceleration];
}];
}
}
- (void)accelerometerDidChange:(CMAcceleration)acceleration
{
//acceleration.x
//acceleration.y
//acceleration.z
// now we have the data and saved somewhere
}
If you want to combine gyroscope and accelerometer data to get an orientation, you'd probably be better off letting CoreMotion do that for you. The methods listed under Managing Device Motion Updates in the docs get you CMDeviceMotion objects; from one of those you can get a CMAttitude that represents an orientation, and from that you can get a rotation matrix or quaternion that you can apply to a node in your scene. (Possibly after further transforming it so that a neutral orientation fits into your scene the way you want.)
After you've oriented the node the way you want, getting a flight direction from that should be pretty simple:
Choose a flight direction in terms of the local coordinate space of the node and make a vector that points in that direction; e.g. a camera looks in the -z direction of its node, so if you want the camera node to look along its flight path you'll use a vector like {0, 0, -1}.
Convert that vector to scene space with convertPosition:toNode: and the scene's rootNode. This gets you the flight direction in terms of the scene, taking the node's orientation into account.
Use the converted vector to move the node, either by assigning it as the velocity for the node's physics body or by using it to come up with a new position and moving the node there with an action or animation.

Using CMAttitude with CMCalibratedMagneticField

I was wondering how to get the iphones orientation with respect to cardinal direction using Core Motion. I think the best route to go is to use the attitude of the device with respect to the magnetic field given by the magnetometer. From the appl e documentation it describes the attitude as:
A CMAttitude object represents a measurement of attitude—that is, the orientation of a body relative to a given frame of reference.
I was wondering how I could make this "given frame of reference" the cardinal directions. Maybe by using using CMCalibratedMagneticField?
Can anybody tell me how to do this or go an easier alternate route.
I'm not 100% sure if this is what you're looking for, but you can get device attitude values from the motion manager using the CMAttitudeReferenceFrameXMagneticNorthZVertical reference frame with the following. This code will dump the attitude of the device to log 30 times per second. If seems to me that yaw values are around 0 facing east, and extend to 180 degrees in both directions, positive values being counter clockwise, and negative being clockwise.
- (void)startMonitoring
{
if (!self.motionManager) {
self.motionManager = [CMMotionManager new];
[self.motionManager setDeviceMotionUpdateInterval:1.0 / 30.0];
}
NSOperationQueue *currentQueue = [NSOperationQueue currentQueue];
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical
toQueue:currentQueue
withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"%#",motion.attitude);
}];
}

How to add a SpriteKit Particle File to current view AND allow dynamic change with device movement?

I have created a simple sks particle file in my project,I would like to know A: how can I implement this particle in my view & how can I add the appropriate parameters so that the particles travel in the direction that the device is being held in, (very similar to iOS7 Dynamic Wallpapers for example), so in my case, if I have stones falling straight down and the device is tilted to the right, the stones should start falling with a different angle. I'd really appreciate some advice.
I was actually thinking about applying this effect to my own game as well. This is a crude answer and I haven't actually tried combining these two elements, but it should definitely give you some pointers.
#property (strong,nonatomic) SKEmitterNode * starEmitter;
#property (strong,nonatomic) CMMotionManager * self.motionManager;
// Create your emitter
NSString *starPath = [[NSBundle mainBundle] pathForResource:#"startButtonDisappear" ofType:#"sks"];
self.starEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:starPath];
// Create a motion manager
self.motionManager = [CMMotionManager new];
[self.motionManager startAccelerometerUpdates];
In your update method, calculate the gravity vector and assign acceleration properties of the emitter.
- (void)update:(NSTimeInterval)currentTime {
CMAccelerometerData* data = self.motionManager.accelerometerData;
// You really don't want NSLogs in this method in a release build, by the way
NSLog(#"x [%2.1f] y [%2.1f] z [%2.1f]",data.acceleration.x,data.acceleration.y,data.acceleration.z)
CGVector gravity;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft) {
gravity = CGVectorMake(data.acceleration.x, data.acceleration.y);
} else {
gravity = CGVectorMake(-data.acceleration.x, -data.acceleration.y);
}
CGFloat tmp = gravity.dy;
gravity.dy = gravity.dx;
gravity.dx = tmp;
// self.physicsWorld.gravity = gravity;
self.starEmitter.xAcceleration = gravity.x;
self.starEmitter.yAcceleration = gravity.y;
NSLog(#"Emitter acceleration set to [%+4.2f,%+4.2f]",gravity.dx,gravity.dy);
}

iPhone leaning angle - use Gyroscope?

I would like to know how many degrees is the iPhone leaning when being held upright - that is, if I point the iPhone up, I would like to know how many degrees it is looking up at? I think that I can use the gyroscope and core motion for this, and I think that it is pitch that I want, however, the pitch is a "rate of speed" in radians per second. I am not really interested in how quickly a user moved the iPhone, I am just interested in the leaning angle. So if I am pointing a phone up to take a picture, I would like to know the angle of pointing up - any ideas??
Thank you.
Gyro data is in radians per second, but what you are looking for is CMMotionManager.attitude property. It shows the attitude of the object in radians relative to a some frame of reference.
create class variable motionManager and init:
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.1f;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
[self processMotion:motion];
}];
process the updates, you are looking for pitch, but in this sample it will show you all three values so you can play around and decide what you need:
-(void)processMotion:(CMDeviceMotion*)motion {
NSLog(#"Roll: %.2f Pitch: %.2f Yaw: %.2f", motion.attitude.roll, motion.attitude.pitch, motion.attitude.yaw);
}
these are Euler Angles, you also have an option to get rotationMatrix, or quaternion format. Each with their own advantages and disadvantages.

Resources