1

In my Xamarin.Android application I have orientation data for X, Y, Z axes got from device's geomagnetic rotation vector composite sensor and processed with SensorManager.GetOrientation( ) method. I want to apply this orientation data in UrhoSharp's scene on the Rotation property of CameraNode. In other words I want to control the scene's camera using the device "orientation" sensors.

What I did so far in the SensorChanged event handler:

// app -> an instance of Urho.SimpleApplication
public void OnSensorChanged(SensorEvent e) {
    if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
        var rm = new float[9];
        SensorManager.GetRotationMatrixFromVector(rm, e.Values.ToArray());
        var ov = new float[3];
        SensorManager.GetOrientation(rm, ov);
        app.Pitch = (Urho.MathHelper.RadiansToDegrees(ov[0]) + 360) % 360;      // map [-Pi...+Pi] to [0...360]
        app.Yaw = (Urho.MathHelper.RadiansToDegrees(ov[1]) + 360) % 360;        // map [-Pi/2...+Pi/2] to [0...360]
        app.CameraNode.Rotation = new Urho.Quaternion(app.Pitch, app.Yaw, 0);
    }
}

But unfortunately it does not work as expected and camera looks always to wrong direction. Any idea?

Ladislav
  • 320
  • 3
  • 10

3 Answers3

1

The OnSensorChanged should be this:

   if (e.Sensor == mRotationSensor)
    {
        var rm = new float[9];
        SensorManager.GetRotationMatrixFromVector(rm, e.Values.ToArray());
        var ov = new float[3];
        SensorManager.GetOrientation(rm, ov);
        app.pitch = (Urho.MathHelper.RadiansToDegrees(ov[1]) + 360) % 360;      // map [-Pi...+Pi] to [0...360]
        app.yaw = (Urho.MathHelper.RadiansToDegrees(ov[0]) + 360) % 360;
        Log.Error("pitch=",app.pitch+"");
        Log.Error("yaw=", app.yaw + "");
        // map [-Pi/2...+Pi/2] to [0...360]
        app.cameraNode.Rotation = new Urho.Quaternion(app.pitch, app.yaw, 0);

    }

And you need add these for your SensorManager in the OnCreate method:

 mSensorManager = (SensorManager)GetSystemService(Activity.SensorService);
 mRotationSensor = mSensorManager.GetDefaultSensor(SensorType.RotationVector);
 mSensorManager.RegisterListener(this, mRotationSensor, SensorDelay.Game);

And add the variables:

 private SensorManager mSensorManager;
 private Sensor mRotationSensor;

Finally don't forget implement ISensorEventListener interface for you Activity.

I have provided a demo on github

Robbit
  • 4,300
  • 1
  • 13
  • 29
  • Hello @joe-lv-msft Modified my OnSensorChanged based on your example but still not really OK. What I want to reach is a similar functionality like in the [SkyView](https://play.google.com/store/apps/details?id=com.t11.skyviewfree) app. Basically I want to write my own planetarium application with live sky view. – Ladislav May 14 '18 at 09:35
  • Have you run my demo? Can it run on your phone? I will see SkyView – Robbit May 14 '18 at 09:36
  • @joe-lv-msft Going to try it. will let you know. Btw Android SDK also provides method [SensorManager.GetQuaternionFromVector( )](https://developer.xamarin.com/api/member/Android.Hardware.SensorManager.GetQuaternionFromVector/) Maybe it is better for this scenario. Or? – Ladislav May 14 '18 at 09:38
  • Why can't I find the app on my Google play store? – Robbit May 14 '18 at 09:58
  • No Idea but there are many other similar application, for example [StarChart](https://play.google.com/store/apps/details?id=com.escapistgames.starchart). Can we chat on PM? U can reach me via [Messenger](https://www.facebook.com/heller.ladislav) or [Hangouts](ladislav.heller@gmail.com) BTW I tried your app, it does not handle the case when there is no *SensorType.RotationVector* sensor on device. – Ladislav May 14 '18 at 10:06
1

Finally I solved it with some research and help of @joe

Here is the final version of method:

// [app] is an instance of Urho.SimpleApplication
public void OnSensorChanged(SensorEvent e) {
    if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
        var inR = new float[9];
        SensorManager.GetRotationMatrixFromVector(inR, e.Values.ToArray());
        var outR = new float[9];
        // we need to remap cooridante system, since the Y and Z axes will be swapped, when we pick up the device 
        if (SensorManager.RemapCoordinateSystem(inR, Android.Hardware.Axis.X, Android.Hardware.Axis.Z, outR)) {
            var ov = new float[3];
            SensorManager.GetOrientation(outR, ov);
            try {
                app.Pitch = (MathHelper.RadiansToDegrees(ov[1]) + 360) % 360;
                app.Yaw = (MathHelper.RadiansToDegrees(ov[0]) + 360) % 360;
                app.CameraNode.Rotation = new Quaternion(app.Pitch, app.Yaw, 0);
            }
            catch (System.Exception ex) {
                // while Urho.SimpleApplication is not fully started, the [app] properties are not available
                System.Diagnostics.Trace.WriteLine(ex.Message);
            }
        }
    }
}
Ladislav
  • 320
  • 3
  • 10
1

Another possible solution using quaternions:

public void OnSensorChanged(SensorEvent e) {
    if (e.Sensor.Type == SensorType.GeomagneticRotationVector) {
        var qv = new float[4];
        SensorManager.GetQuaternionFromVector(qv, e.Values.ToArray());
        try {
            app.CameraNode.Rotation = new Quaternion(qv[1], -qv[3], qv[2], qv[0]);
            app.CameraNode.Pitch(90.0f);
            app.CameraNode.Roll(180.0f);
        }
        catch (System.Exception ex) {
        }
    }
}
Ladislav
  • 320
  • 3
  • 10