AR用の姿勢を得る

id:yellow_73:20110815 の続き。

センサの基準となる画面の向きはタブレットスマートフォンで違う

ヨー、ピッチ、ローについて、前の記事ではうそ書きましたー (差し替え済み)。平面に置いたのが基準です。

繰り返しですが、ポートレイトがセンサの基準になっているとは限りません。
getWindowManager().getDefaultDisplay().getRotation() で Surface.ROTATION_* (*は0, 90, 180, 270)が返ります。方位センサの基準からの回転角度と考えて差し支えないようです。

ヨー、ピッチ、ローの軸とねじの向き

ヨー、ピッチ、ローについては、次のような感じです。

ヨー(Z軸)は矢印方向に左ねじで進み、ピッチ(X軸)、ロール(Y軸)は右ねじで進みます。

個人的な趣味と違う

個人的趣味はこんなかんじ。

つまり、右手系なのはそのままで、X軸が北方向を向き(前の図でのY)、各軸矢印方向に同じ向きにねじる、というのがポイント。
なお、左ねじになってるのは、本来は右ねじなのですが、あとでロー、ピッチ、ヨーの回転行列の逆行列を使うため、左ねじでとっちゃいました。

センサ情報を得つつ個人的趣味の系に変換

センサ情報を得るのは http://techbooster.jpn.org/andriod/ui/443/ あたりをもとにして下さい。

    private static final float MATRIX000 = { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1,
            0, 0, 0, 0, 0, 1 };
    private static final float MATRIX090 = { 0, 0, 1, 0, -1, 0, 0, 0, 0, -1,
            0, 0, 0, 0, 0, 1 };
    private static final float MATRIX180 = { -1, 0, 0, 0, 0, 0, -1, 0, 0, -1,
            0, 0, 0, 0, 0, 1 };
    private static final float MATRIX270 = { 0, 0, -1, 0, 1, 0, 0, 0, 0, -1,
            0, 0, 0, 0, 0, 1 };

    float  inR = new float[16];
    float outR = new float[16];
    float magneticValues      = new float[3];
    float accelerometerValues = new float[3];

    private void multiple(int d, float L, float R, float[] A) {
        int r;
        int pa = 0;
        int plb;
        for (r = 0, plb = 0; r < d; r++, plb += d) {
            int ple = plb + d;
            for (int c = 0; c < d; c++) {
                float a = 0;
                int pl, pr;
                for (pl = plb, pr = c; pl < ple; pl++, pr += d) {
                    a += L[pl] * R[pr];
                }
                A[pa++] = a;
            }
        }
    }

    public void onSensorChanged(SensorEvent event) {
        ....
        // magneticValues と accelerometerValues はここでは取ってないので取ってきて下さい。

        if (magneticValues != null && accelerometerValues != null) {
            SensorManager.getRotationMatrix(inR, null, accelerometerValues,
                    magneticValues);
            int orientation = this.getWindowManager().getDefaultDisplay()
                    .getRotation();
            switch (orientation) {
            case Surface.ROTATION_0:
                this.multiple(4, inR, MATRIX000, outR);
                break;
            case Surface.ROTATION_90:
                this.multiple(4, inR, MATRIX090, outR);
                break;
            case Surface.ROTATION_180:
                this.multiple(4, inR, MATRIX180, outR);
                break;
            case Surface.ROTATION_270:
                this.multiple(4, inR, MATRIX270, outR);
                break;
            default:
                return;
            }
            SensorManager.getOrientation(outR, orientationValues);
            // 単位はラジアン
            float yaw = orientationValues[0];
            float pitch = -orientationValues[1];
            float role = -orientationValues[2];
            ...

どんだけ SensorManager#remapCoordinateSystem を信用してないんだ。
なお、センサのロールとピッチは右ねじで進みますが、趣味では左ねじで進むので、マップしたあとに逆転しています。