Question

I'm using CMDeviceMotion and the attitude's quaternion to obtain the pitch and yaw values, which are then applied to a CC3Camera in a Cocos3D scene to rotate the camera around.

#define RadiansToDegrees(x) ((180 / M_PI) * x)

- (void)initializeScene
{
    //...

    CC3Camera *cam = [CC3Camera nodeWithName:@"Camera"];
    cam.location = cc3v(0, 10, 0.0001);
    cam.targetLocation = cc3v(0, 0, 0);
    _cameraBoom = [CC3Node nodeWithName:@"CameraBoom"];
    _cameraBoom.location = cc3v(0, 0, 0);
    [_cameraBoom addChild:cam];
    [self addChild:_cameraBoom];
    [self setActiveCamera:cam];
    _cameraBoom.rotation = cc3v(0, 90, 0);

    //...

    _motionManager = [[CMMotionManager alloc] init];
    _referenceAttitude = nil;
    _initialCameraRotation = _cameraBoom.rotation;

    [self enableMotion];
}

- (void)enableMotion
{
    CMDeviceMotion *deviceMotion = _motionManager.deviceMotion;
    _referenceAttitude = deviceMotion.attitude;
    _initialCameraRotation = _cameraBoom.rotation;

    [_motionManager startDeviceMotionUpdates];

    if (!_gyroTimer) {
        _gyroTimer = [NSTimer scheduledTimerWithTimeInterval:1 / 30.0
                                                      target:self
                                                    selector:@selector(doGyroUpdate)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

- (void)doGyroUpdate
{
    CMDeviceMotion *deviceMotion = _motionManager.deviceMotion;
    CMAttitude *attitude = deviceMotion.attitude;

    if (_referenceAttitude != nil) {
        [attitude multiplyByInverseOfAttitude:_referenceAttitude];
    }

    CMQuaternion quat = attitude.quaternion;
    double pitch = RadiansToDegrees(atan2(2 * (quat.x * quat.w + quat.y * quat.z), 1 - 2 * (quat.x * quat.x + quat.z * quat.z)));
    double yaw = RadiansToDegrees(asin(2 * (quat.x * quat.y + quat.w * quat.z)));

    _cameraBoom.rotation = CC3VectorAdd(_initialCameraRotation, cc3v(pitch, yaw, 0));
}

The pitch is in range [-π, π]. When the device is faced up the pitch = 0 and it becomes π/2 as I take the device from the table and point it to take a picture (portrait mode). The [-π, π] range enables me to rotate the device 360°. When faced down (i.e. device is upside down) the pitch value is π.

The yaw range is only [-π/2, π/2]. It starts at 0 and goes to π/2 when I rotate the device to the left. But if I rotate it beyond π/2, the yaw value starts to decrease.

Can I get the yaw value in range [-π, π], just like the pitch? It would be more useful to be able to rotate the camera sideways by 180° (to the left and to the right, to have a full 360° view) instead of flipping the device vertically to look behind with the camera.

Was it helpful?

Solution

Here's how I did it eventually with the built-in functions:

- (void)doGyroUpdate
{
    CMDeviceMotion *deviceMotion = _motionManager.deviceMotion;
    CMAttitude *attitude = deviceMotion.attitude;

    if (_referenceAttitude != nil) {
        [attitude multiplyByInverseOfAttitude:_referenceAttitude];
    }

    CMQuaternion quat = attitude.quaternion;
    CC3Quaternion cc3Quat = CC3QuaternionMake(quat.x, quat.y, quat.z, quat.w);
    CC3Vector rot = CC3RotationFromQuaternion(cc3Quat);

    _cameraBoom.rotation = cc3v(-rot.x, -rot.z, rot.y);
}

The result:

Rotating the camera like so enables you to look around at the skybox as if you would normally look at the world through the device's back camera. My CC3Camera object is inside a sphere with a HDRi image mapped on to it, on the inside (see this post).

To smoothly rotate the camera:

[_cameraBoom runAction:[CC3ActionRotateTo actionWithDuration:0.15 rotateTo:cc3v(-rot.x, -rot.z, rot.y)]];

Hope this helps someone else too.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top