読者です 読者をやめる 読者になる 読者になる

うさがにっき

読書感想文とプログラムのこと書いてきます

モチベーション3.0

読書感想

概要

ダニエル・ピンクによる現代においてどうやってやる気を引き出すかを書いた良書

一言で言うと金銭と労働のトレード(モチベーション2.0)から、仕事自体にやりがいを感じる(モチベーション3.0)時代になっている
もちろん生活をしていく上で必要最低限+αの賃金があることが前提であり、やりがいを感じれるようにできる限りやり方を作業者に任せて試行錯誤できる環境を作れるような配慮も必要になる

詳細

モチベーション2.0の盛衰

人類最初のモチベーションは生存を目的としていた(モチベーション1.0)
そこから発展して外的な報酬と罰を中心とした新たな仕組みが取り入れられた(モチベーション2.0)
これは20世紀のルーチンワークには有効だったが、21世紀を迎えてモチベーション2.0はわたしたちの組織、仕事に対する考え方やその手法とは互換性が無いことが明らかになってきた

アメとムチがうまくいかない理由

モチベーション2.0の「交換条件つき(こうしたら、おれをあげる)」報酬を提示しても、成果は期待したほど得られない
これは内発的動機付けを消滅させ、成果を減少させ、創造性を破壊し、人間お好ましい言動を阻害するから
また、モチベーション2.0ではいかに早く報酬を取得しようか考え反倫理的行動を助長し、短絡的な考えを促すこともあり得る

アメとムチがうまくいく特殊な状況

規則的なルーチンタスクであればモチベーション2.0は効果を発揮する
内的動機付けや、創造性がこの種の仕事には存在しないため悪影響をうけない
そのうえで、仕事の必要性を示し、それらが退屈であることを認め、望む方法でその仕事を冠せさせる自由を相手に与え馬場合には、モチベーション2.0(2.1になるかも)はより効果を発揮する場合もある

タイプIとタイプX

モチベーション2.0はタイプXの行動を前提にしこれを助長する
この行動は内発的な欲求よりも、外発的な欲求を活力の源とし、活動から満足感を得るというよりも、活動によって得られる外的報酬と結びついている
モチベーション3.0はタイプIの行動を前提としこれを助長する
この行動は活動によって得られる外的な報酬というより、活動自体からもたらされる内的な満足感と結びついている
仕事で成功を収め、プライベートを充実させるためには、自分も職場の仲間もタイプXからタイプIと移行する必要がある
タイプIは自分で意識することにより成長させることができ、パフォーマンスの向上、健康の増進、全般的な幸福度の上昇につながる

自律性

わたしたちの「初期設定」は自律的であり自己決定的である
だが「マネジメント」という時代遅れの概念により、この設定は変更されタイプIからタイプXへとわたしたちを変える
タイプIになって大きな成果を生み出すために、最初に必要とされるのは自律性
課題、時間、手法、チームについて自律性が必要になる
大きな自律を与える会社は競合他社より高い業績を上げる

熟達

モチベーション2.0では従順な姿勢が求められたのに対し、モチベーション3.0では積極的な関与が必要となる
積極的に関与してはじめて、熟達、何か価値のあること上達を生み出せる
熟達の追求は重要でありあがら第三の動機ではあまり機能していないが、経済発展において必要不可欠となる
熟達は「フロー(フローとは取り組んでいる課題が本質的に自分の能力と整合している場合の最適経験のこと)」から始まる
日々の活動を難しすぎず、易しすぎない教務、すなわち「ゴルディロックスの仕事」で補強している
ただし熟達は三つのルールがある

  1. 熟達は心の持ち方次第である、能力は固定的ではなく、無限に上昇が可能だと理解する
  2. 熟達には苦痛が伴う、努力、根性、意図的な訓練が必要となる
  3. 熟達は漸近線であり、完全に熟達を達成することは不可能である、だからこそもどかしいと同時に魅力的

目的

人間は本能的に人生の意義や目的を探すもの
自分以外の人、もの、社会などに利益に貢献する永続的な目的を求める
だが従来の企業は長年に私この目的を見栄えの良いアクセサリーのようなものだと考えていた

だが現在、これは変わりつつある
一つは社会を牽引していた人々が置いに差し掛かって自分たちを死から逃れられないことに気づいたからである
モチベーション3.0では願望の対象や指針として、目的の最大化が、利益の最大化と並んで認められている

感想

これまでのエンジニア経験から「給料を払うから〜の作業をやって」というふうにやり方を決められていた作業は確かに効率もなかったし、そこから新たなアイデアも出にくかったように感じていた

このもどかしさをうまく言語化してくれたのが本書になる
作業者に自律性をうながすことにより熟達を促し、金銭目的ではなく人生の目的として作業を行えるようになる
つまり仕事を楽しいと感じられるようになり、それがドライヴとなりまた熟達に励む構造が作成できるのだ

Tango現状まとめ

Androidd AR

概要

発表からしばらく動きがなかったProjectTangoがようやく今年の秋に端末発売となる(日本発売は未定)
発売前に現在わかっていることをまとめる

* 詳細

What is Tango

get.google.com
スマートフォンに深度センサーなど通常のスマホには無いセンサーを付け、それらからスマートフォンにより多くの情報を認識できるようにした端末をTango対応端末という(Daydream対応端末的な)
それらの情報を使ってMotionTracking, AreaLearning, DepthPerceptionなどの機能を実現できる
www.youtube.com

既に海外では導入実績あり
Matterport and Google ATAP Project Tango - Matterport

開発端末

開発用端末が公開されているが日本では購入不可
Tango タブレット開発キット - このキットはデベロッパーを対象としています* - Google ストア
今秋Phab2 ProというTango端末がLenovoより発売される予定、しかしこれも日本で発売されるかは未定
Lenovo Phab 2 Pro | 世界初の Tango 対応スマートフォン | レノボジャパン

開発環境

開発用SDKは既に公開されている
Download the Tango SDK  |  Tango  |  Google Developers
リリースノートを見ると月に一度ほどアップデートされており活発
Release Notes  |  Tango  |  Google Developers

すでにTango用アプリはいくつかリリースされている
Project Tango - Google Play の Android アプリ
開発用アプリもいくつかリリースされている様子
Project Tango Experiments - Google Play の Android アプリ

サンプルアプリは以下からダウンロード可能・・・が現状輸入するなどしないと開発端末は手に入らない、エミュレーターなどもないので動かすことはできない
GitHub - googlesamples/tango-examples-java: Example projects for Project Tango Java API

Tangoの各機能

各機能の紹介とソースコードを眺めてみる
ちなみに各昨日のベストプラクティス的なのがある
Tango UX Best Practices  |  Tango  |  Google Developers

MotionTracking概要

Motion Tracking  |  Tango  |  Google Developers
端末が領域の中でどのように動いているかを検知する機能

MotionTrackingソース

onResume抜粋

   @Override
    protected void onResume() {
        super.onResume();

        // Initialize Tango Service as a normal Android Service, since we call
        // mTango.disconnect() in onPause, this will unbind Tango Service, so
        // every time when onResume gets called, we should create a new Tango object.
        mTango = new Tango(MotionTrackingActivity.this, new Runnable() {
            // Pass in a Runnable to be called from UI thread when Tango is ready,
            // this Runnable will be running on a new thread.
            // When Tango is ready, we can call Tango functions safely here only
            // when there is no UI thread changes involved.
            @Override
            public void run() {
                mConfig = setupTangoConfig(mTango);

                try {
                    setTangoListeners();
                } catch (TangoErrorException e) {
                    Log.e(TAG, getString(R.string.exception_tango_error), e);
                } catch (SecurityException e) {
                    Log.e(TAG, getString(R.string.permission_motion_tracking), e);
                }
                try {
                    mTango.connect(mConfig);
                } catch (TangoOutOfDateException e) {
                    Log.e(TAG, getString(R.string.exception_out_of_date), e);
                } catch (TangoErrorException e) {
                    Log.e(TAG, getString(R.string.exception_tango_error), e);
                }
            }
        });
    }

MotionTracking初期設定

    private TangoConfig mConfig;

    /**
     * Sets up the tango configuration object. Make sure mTango object is initialized before
     * making this call.
     */
    private TangoConfig setupTangoConfig(Tango tango) {
        // Create a new Tango Configuration and enable the MotionTrackingActivity API
        TangoConfig config = new TangoConfig();
        config = tango.getConfig(config.CONFIG_TYPE_DEFAULT);
        config.putBoolean(TangoConfig.KEY_BOOLEAN_MOTIONTRACKING, true);

        // Tango service should automatically attempt to recover when it enters an invalid state.
        config.putBoolean(TangoConfig.KEY_BOOLEAN_AUTORECOVERY, true);
        return config;
    }

取得しているconfig = tango.getConfig(config.CONFIG_TYPE_DEFAULT)には他にもいろいろある
CONFIG_TYPE_DEFAULTはMOTION_TRACKINGは取得できる
TangoConfig  |  Tango Java API  |  Google Developers

起動する前に以下二つの値にtrueを設定する必要がある

  • TangoConfig. KEY_BOOLEAN_MOTIONTRACKING
  • TangoConfig.KEY_BOOLEAN_AUTORECOVERY

setTangoListenersを設定する
ここで初期化した結果、イベントをキャッチできる

    /**
     * Set up the callback listeners for the Tango service, then begin using the Motion
     * Tracking API. This is called in response to the user clicking the 'Start' Button.
     */
    private void setTangoListeners() {
        // Lock configuration and connect to Tango
        // Select coordinate frame pair
        final ArrayList<TangoCoordinateFramePair> framePairs =
                new ArrayList<TangoCoordinateFramePair>();
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
                TangoPoseData.COORDINATE_FRAME_DEVICE));

        // Listen for new Tango data
        mTango.connectListener(framePairs, new OnTangoUpdateListener() {
            @Override
            public void onPoseAvailable(final TangoPoseData pose) {
                logPose(pose);
            }

            @Override
            public void onXyzIjAvailable(TangoXyzIjData arg0) {
                // We are not using TangoXyzIjData for this application
            }

            @Override
            public void onTangoEvent(final TangoEvent event) {
            }

            @Override
            public void onFrameAvailable(int cameraId) {
                // We are not using onFrameAvailable for this application
            }
        });
    }

まずMotionTrackingをどこを基準にするか、どのタイミングで更新するかをTangoCoordinateFramePairで設定する
どこを基準にするかは以下の二つを設定できる

  • COORDINATE_FRAME_START_OF_SERVICE

MotionTrackingスタート時

  • COORDINATE_FRAME_PREVIOUS_DEVICE_POSE

直前にDeviceが静止した時

どのタイミングで更新するかは以下を設定できる、他にもいろいろありそう

  • COORDINATE_FRAME_DEVICE

Deviceの更新タイミング

  • COORDINATE_FRAME_IMU

COORDINATE_FRAME_DEVICEとの違いがよくわからないが、内部更新タイミングとあった

  • COORDINATE_FRAME_DISPLAY

Display更新タイミング

Frameについては以下も参照
Frames of Reference  |  Tango  |  Google Developers

またMotionTrackingには三つの状態がある

  • TangoPoseData.POSE_INITIALIZING

初期化前、まだ使用できない

  • TangoPoseData.POSE_VALID

普通に使用できる

  • TangoPoseData.POSE_INVALID

値(取得された値?)が向こうなので再起動が必要
再起動の方法はTangoConfig.KEY_BOOLEAN_AUTORECOVERYに設定されている値によって自動再起動するかどうか設定できる

後は取得された
TangoXyzIjData  |  Tango Java API  |  Google Developers
とか
TangoEvent  |  Tango Java API  |  Google Developers
を使って処理を行う

AreaLearning概要

Area Learning  |  Tango  |  Google Developers
物理的な空間を端末内の仮想的な空間に記憶する機能

AreaLearningを使う上でkeyとなる機能はMotionTrackingを使った以下の機能になる

  • drift corrections

軌跡の精度の向上

  • localization

認識済みの場所のどこにいるか把握する

AreaLearnigソース確認

onResume抜粋

    @Override
    protected void onResume() {
        super.onResume();

        // Initialize Tango Service as a normal Android Service, since we call
        // mTango.disconnect() in onPause, this will unbind Tango Service, so
        // everytime when onResume gets called, we should create a new Tango object.
        mTango = new Tango(AreaDescriptionActivity.this, new Runnable() {
            // Pass in a Runnable to be called from UI thread when Tango is ready,
            // this Runnable will be running on a new thread.
            // When Tango is ready, we can call Tango functions safely here only
            // when there is no UI thread changes involved.
            @Override
            public void run() {
                mConfig = setTangoConfig(mTango, mIsLearningMode, mIsConstantSpaceRelocalize);

                synchronized (AreaDescriptionActivity.this) {
                    // Re-attach listeners.
                    try {
                        setUpTangoListeners();
                    } catch (TangoErrorException e) {
                        Log.e(TAG, getString(R.string.tango_error), e);
                    } catch (SecurityException e) {
                        Log.e(TAG, getString(R.string.no_permissions), e);
                    }

                    // Connect to the tango service (start receiving pose updates).
                    try {
                        mTango.connect(mConfig);
                    } catch (TangoOutOfDateException e) {
                        Log.e(TAG, getString(R.string.tango_out_of_date_exception), e);
                    } catch (TangoErrorException e) {
                        Log.e(TAG, getString(R.string.tango_error), e);
                    } catch (TangoInvalidException e) {
                        Log.e(TAG, getString(R.string.tango_invalid), e);
                    }
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (AreaDescriptionActivity.this) {
                            setupTextViewsAndButtons(mTango, mIsLearningMode,
                                    mIsConstantSpaceRelocalize);
                        }
                    }
                });
            }
        });
    }


AreaLearning初期設定

    /**
     * Sets up the tango configuration object. Make sure mTango object is initialized before
     * making this call.
     */
    private TangoConfig setTangoConfig(Tango tango, boolean isLearningMode, boolean isLoadAdf) {
        TangoConfig config;
        config = tango.getConfig(TangoConfig.CONFIG_TYPE_DEFAULT);
        // Check if learning mode
        if (isLearningMode) {
            // Set learning mode to config.
            config.putBoolean(TangoConfig.KEY_BOOLEAN_LEARNINGMODE, true);

        }
        // Check for Load ADF/Constant Space relocalization mode
        if (isLoadAdf) {
            ArrayList<String> fullUuidList;
            // Returns a list of ADFs with their UUIDs
            fullUuidList = tango.listAreaDescriptions();
            // Load the latest ADF if ADFs are found.
            if (fullUuidList.size() > 0) {
                config.putString(TangoConfig.KEY_STRING_AREADESCRIPTION,
                        fullUuidList.get(fullUuidList.size() - 1));
            }
        }
        return config;
    }

areaLearningを設定する
TangoConfig  |  Tango Java API  |  Google Developers

ADFファイルが必要になる場合は
TangoConfig.KEY_STRING_AREADESCRIPTIONにuuidを設定する
ADFファイルとはareaLearningした結果を保存したファイル、Tango APIを使用することにより保存できる

tangoListenerの設定

    private void setUpTangoListeners() {
        // Set Tango Listeners for Poses Device wrt Start of Service, Device wrt
        // ADF and Start of Service wrt ADF
        ArrayList<TangoCoordinateFramePair> framePairs = new ArrayList<TangoCoordinateFramePair>();
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
                TangoPoseData.COORDINATE_FRAME_DEVICE));
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
                TangoPoseData.COORDINATE_FRAME_DEVICE));
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
                TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE));

        mTango.connectListener(framePairs, new OnTangoUpdateListener() {
            @Override
            public void onXyzIjAvailable(TangoXyzIjData xyzij) {
                // Not using XyzIj data for this sample
            }

            // Listen to Tango Events
            @Override
            public void onTangoEvent(final TangoEvent event) {
            }

            @Override
            public void onPoseAvailable(TangoPoseData pose) {
                // Make sure to have atomic access to Tango Data so that
                // UI loop doesn't interfere while Pose call back is updating
                // the data.
                synchronized (mSharedLock) {
                    // Check for Device wrt ADF pose, Device wrt Start of Service pose,
                    // Start of Service wrt ADF pose (This pose determines if the device
                    // is relocalized or not).
                    if (pose.baseFrame == TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION
                            && pose.targetFrame == TangoPoseData
                            .COORDINATE_FRAME_START_OF_SERVICE) {
                        if (pose.statusCode == TangoPoseData.POSE_VALID) {
                            mIsRelocalized = true;
                        } else {
                            mIsRelocalized = false;
                        }
                    }
                }

                final double deltaTime = (pose.timestamp - mPreviousPoseTimeStamp) *
                        SECS_TO_MILLISECS;
                mPreviousPoseTimeStamp = pose.timestamp;
                mTimeToNextUpdate -= deltaTime;

                if (mTimeToNextUpdate < 0.0) {
                    mTimeToNextUpdate = UPDATE_INTERVAL_MS;

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            synchronized (mSharedLock) {
                                mSaveAdfButton.setEnabled(mIsRelocalized);
                                mRelocalizationTextView.setText(mIsRelocalized ?
                                        getString(R.string.localized) :
                                        getString(R.string.not_localized));
                            }
                        }
                    });
                }
            }

            @Override
            public void onFrameAvailable(int cameraId) {
                // We are not using onFrameAvailable for this application.
            }
        });
    }
depthPerception概要

Depth Perception  |  Tango  |  Google Developers
現実世界とのオブジェクトとの距離を把握できる機能
範囲は0.5~4m

depthPerceptionソース確認

いつも通りonResume

    @Override
    protected void onResume() {
        super.onResume();

        // Initialize Tango Service as a normal Android Service, since we call
        // mTango.disconnect() in onPause, this will unbind Tango Service, so
        // every time when onResume gets called, we should create a new Tango object.
        mTango = new Tango(DepthPerceptionActivity.this, new Runnable() {
            // Pass in a Runnable to be called from UI thread when Tango is ready,
            // this Runnable will be running on a new thread.
            // When Tango is ready, we can call Tango functions safely here only
            // when there is no UI thread changes involved.
            @Override
            public void run() {
                mConfig = setupTangoConfig(mTango);

                try {
                    setTangoListeners();
                } catch (TangoErrorException e) {
                    Log.e(TAG, getString(R.string.exception_tango_error), e);
                } catch (SecurityException e) {
                    Log.e(TAG, getString(R.string.permission_motion_tracking), e);
                }
                try {
                    mTango.connect(mConfig);
                } catch (TangoOutOfDateException e) {
                    Log.e(TAG, getString(R.string.exception_out_of_date), e);
                } catch (TangoErrorException e) {
                    Log.e(TAG, getString(R.string.exception_tango_error), e);
                }
            }
        });
    }


tangoの設定
depthをonに

    /**
     * Sets up the tango configuration object. Make sure mTango object is initialized before
     * making this call.
     */
    private TangoConfig setupTangoConfig(Tango tango) {
        // Create a new Tango Configuration and enable the Depth Sensing API
        TangoConfig config = new TangoConfig();
        config = tango.getConfig(config.CONFIG_TYPE_DEFAULT);
        config.putBoolean(TangoConfig.KEY_BOOLEAN_DEPTH, true);
        return config;
    }

tangolistenerの設定

    /**
     * Set up the callback listeners for the Tango service, then begin using the Motion
     * Tracking API. This is called in response to the user clicking the 'Start' Button.
     */
    private void setTangoListeners() {
        // Lock configuration and connect to Tango
        // Select coordinate frame pair
        final ArrayList<TangoCoordinateFramePair> framePairs =
                new ArrayList<TangoCoordinateFramePair>();
        framePairs.add(new TangoCoordinateFramePair(
                TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
                TangoPoseData.COORDINATE_FRAME_DEVICE));

        // Listen for new Tango data
        mTango.connectListener(framePairs, new OnTangoUpdateListener() {
            @Override
            public void onPoseAvailable(final TangoPoseData pose) {
                // We are not using TangoPoseData for this application
            }

            @Override
            public void onXyzIjAvailable(final TangoXyzIjData xyzIjData) {
                logXyzIj(xyzIjData);
            }

            @Override
            public void onTangoEvent(final TangoEvent event) {
            }

            @Override
            public void onFrameAvailable(int cameraId) {
                // We are not using onFrameAvailable for this application
            }
        });
    }

onXyzIjAvailableでdepthsensorからの座標を取得する
TangoXyzIjData  |  Tango Java API  |  Google Developers

取得したデータは以下の感じで使う

    /**
     * Log the point count and the average depth of the given XyzIj data
     * in the Logcat as information.
     */
    private void logXyzIj(TangoXyzIjData xyzIjData) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Point count: " + xyzIjData.xyzCount);
        stringBuilder.append(". Average depth (m): " + calculateAveragedDepth(xyzIjData.xyz));
        Log.i(TAG, stringBuilder.toString());
    }

    /**
     * Calculates the average depth from a point cloud buffer.
     */
    private float calculateAveragedDepth(FloatBuffer pointCloudBuffer) {
        int pointCount = pointCloudBuffer.capacity() / 3;
        float totalZ = 0;
        float averageZ = 0;
        for (int i = 0; i < pointCloudBuffer.capacity() - 3; i = i + 3) {
            totalZ = totalZ + pointCloudBuffer.get(i + 2);
        }
        if (pointCount != 0) {
            averageZ = totalZ / pointCount;
        }
        return averageZ;
    }

感想

実機が無いので試せないのが辛いが大体の流れはわかった
どのセンサーを使うか登録してlistenerで取得する流れ

後はtango conceptが少しコツになるくらいので眺めておくと吉
Tango Concepts  |  Tango  |  Google Developers

daydream現状まとめ、サンプルソース眺めた感想(native層)

Android

概要

tiro105.hateblo.jp
の続き、native眺めてみまた&vulkan対応などの今後どうするのが正解なのか考えてみた

詳細

native層について

基本的にはOpenGLES 2の書き方をしているので、OpenGLES使える人はそんな困らないと思う(vulkan対応したソースとか欲しいけど)
ただGoogle VR関係のクラスを扱っている箇所があるのでそこだけ少し注意、けどこれまでcardboard作ってた人なら慣れてると思う

Activity側でのnativeの定義

  private native void nativeOnCreate(AssetManager assetManager, long gvrContextPtr);
  private native void nativeOnResume();
  private native void nativeOnPause();
  private native void nativeOnSurfaceCreated();
  private native void nativeOnSurfaceChanged(int width, int height);
  private native void nativeOnDrawFrame();
  private native void nativeOnDestroy();

long gvrContextPtrってのがGoogle VRクラスのポインタ、以前の記事であった以下のメソッドで該当箇所のポインタを取得している

gvrLayout.getGvrApi().getNativeGvrContext()


app_jni.hでは以下のように定義
この書き方すれば何個もメソッドの入り口作らなくていい、賢い

#define NATIVE_METHOD(return_type, method_name) \
  JNIEXPORT return_type JNICALL                 \
      Java_com_google_vr_ndk_samples_controllerpaint_MainActivity_##method_name

extern "C" {

NATIVE_METHOD(void, nativeOnCreate)(JNIEnv* env, jobject obj,
                                    jobject asset_mgr, jlong gvrContextPtr);
NATIVE_METHOD(void, nativeOnResume)(JNIEnv* env, jobject obj);
NATIVE_METHOD(void, nativeOnPause)(JNIEnv* env, jobject obj);
NATIVE_METHOD(void, nativeOnSurfaceCreated)(JNIEnv* env, jobject obj);
NATIVE_METHOD(void, nativeOnSurfaceChanged)(JNIEnv* env, jobject obj,
                                            jint width, jint height);
NATIVE_METHOD(void, nativeOnDrawFrame)(JNIEnv* env, jobject obj);
NATIVE_METHOD(void, nativeOnDestroy)(JNIEnv* env, jobject obj);

}


gvrContextPtrをもってるnativeOnCreateを追う
CHECKではちゃんとオブジェクトが入ってるか見てるだけっぽい
app_jni.cc

NATIVE_METHOD(void, nativeOnCreate)(JNIEnv* env, jobject obj,
                                    jobject asset_mgr, jlong gvr_context_ptr) {
  CHECK(!demo_app_);
  CHECK(gvr_context_ptr);
  demo_app_.reset(new DemoApp(env, asset_mgr, gvr_context_ptr));
}


見るべきはgvr_context_ptr周り
扱えるようにキャストした後、gvr_apiオブジェクト取得している
特にdaydreamを意識したソースは無いように見える
gvr::GvrApi Class Reference  |  Google VR  |  Google Developers
demoapp.cc

DemoApp::DemoApp(JNIEnv* env, jobject asset_mgr_obj, jlong gvr_context_ptr)
    :  // This is the GVR context pointer obtained from Java:
      gvr_context_(reinterpret_cast<gvr_context*>(gvr_context_ptr)),
      // Wrap the gvr_context* into a GvrApi C++ object for convenience:
      gvr_api_(gvr::GvrApi::WrapNonOwned(gvr_context_)),
      gvr_api_initialized_(false),
      shader_(-1),
      shader_u_color_(-1),
      shader_u_mvp_matrix_(-1),
      shader_u_sampler_(-1),
      shader_a_position_(-1),
      shader_a_texcoords_(-1),
      ground_texture_(-1),
      paint_texture_(-1),
      asset_mgr_(AAssetManager_fromJava(env, asset_mgr_obj)),
      recent_geom_vertex_count_(0),
      brush_stroke_total_vertices_(0),
      selected_color_(0),
      painting_(false),
      has_continuation_(false),
      switched_color_(false),
      stroke_width_(kMinStrokeWidth) {
  CHECK(asset_mgr_);
  LOGD("DemoApp initialized.");
}


全体を眺めたがOpenGLES2で描画している処理がほとんどで、他もdaydreamを意識したソースはなかったため解説は割愛

daydream開発の現場、今後

現在展開されているdaydream sampleは前述の通りopenGLES2を使ってGoogle VRでのVRを実現している
java側でdaydreamモードをONにすることにしても描画がopenGLES2であるならどれほど恩恵が得られるか少し疑問が残る(コントローラーが使えるくらいじゃ無いのか?)
やはり現状はvulkanを使ったサンプルが出るまで少し待つ、それか今はjava層だけに目を向けて置いてvulkanの書き方を身につけておくのがいいかもしれない

もしくはUnityやUnreal EngineのVulkan対応を待つという手もある
両方ともdaydreamでのvulkan対応を表明しているため今のうちに各IDEでのdaydreamの書き方を勉強しておけば後々vulkan対応時に焦らず対応できそう
Make your Daydreams a reality – Unity Blog
Vulkan Support Coming Daydream via Unreal Engine | VRFocus

こんな感じでボタンひとつで設定できるととても楽できて嬉しい
Unity - マニュアル: iOS Player Settings

感想

結論としてはvulkan対応のベストプラクティスが謎の現在ではdaydreamの本格的な開発はまだ早いと思われる
しかしvulkan自体のサンプルは公開されているのでvulkanの書き方を事前に学んでおくなどの事前準備は十分にできそう
Unityではdaydream sampleも公開されているので今のうちに書き方を習得しておけばvulkan対応もすぐできるだろう

daydream現状まとめ、サンプルソース眺めた感想(java層)

Android

www.youtube.com

概要

daydream気になる、けど現状どうなの?開発行ける雰囲気?
2016/7/16時点での雰囲気を掴む
サンプルコードあったら読んでみる

詳細

情報

qiita16件、けどだいたいGoogleIO軽くまとめた感じの記事
「daydream」の検索結果 - Qiita
stackoverflow161件、あんまり盛り上がってないかな?
Human Verification - Stack Overflow
公式情報
Developer Overview  |  Google VR  |  Google Developers
リンク切れてるところもところどころあるけどだいたい雰囲気でわかる

開発環境

Android or Unity
とりあえずAndroidで試してみる

daydream環境

daydreamはヘッドセット&コントローラーが別売りでそれにAndroidをセッティングして使用する
ヘッドセット、コントローラーはまだ買えない

現状開発環境として必要なのはコントローラー用のAndroidエミュレータとVR表示用Androidの2台

コントローラ側AndroidはNじゃなくても大丈夫、端末もそれほどスペックを要求しない
VR側はNexus6PかつN preview4以上推奨、5xでも起動はしたけど端末が発熱するだけで全然動かなかった。。。

apkとか

コントローラーはAndroid用のエミュレーターがapkとして配布されていたはず・・・なのだが公式サイトから飛んだ先が404ニナッテル
設定手順
Set up a Daydream Development Kit  |  Google VR  |  Google Developers

apk(無いよ)
https://github.com/googlevr/gvr-android-sdk/blob/master/ndk-beta/apks/controller_emulator.apk?raw=true


困った、探した、(それっぽいの)あった
gvr-android-sdk/apks at master · googlevr/gvr-android-sdk · GitHub
apkのみ

VR側も404になってるけど多分これ
gvr-android-sdk/ndk-beta/demos/controllerpaint at master · googlevr/gvr-android-sdk · GitHub
ソース見れる!

ソース解析

Activityは1つ

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setImmersiveSticky();
    getWindow()
        .getDecorView()
        .setOnSystemUiVisibilityChangeListener(
            new View.OnSystemUiVisibilityChangeListener() {
              @Override
              public void onSystemUiVisibilityChange(int visibility) {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                  setImmersiveSticky();
                }
              }
            });

    // Enable VR mode, if the device supports it.
    AndroidCompat.setVrModeEnabled(this, true);
    // Get the GvrLayout.
    gvrLayout = new GvrLayout(this);

    // Enable scan line racing, if possible.
    if (gvrLayout.setScanlineRacingEnabled(true)) {
      Log.d(TAG, "Successfully enabled scanline racing.");
      // Scanline racing decouples the app framerate from the display framerate,
      // allowing immersive interaction even at the throttled clockrates set by
      // sustained performance mode.
      AndroidCompat.setSustainedPerformanceMode(this, true);
    } else {
      Log.w(TAG, "Failed to enable scanline racing.");
    }

    // Configure the GLSurfaceView.
    surfaceView = new GLSurfaceView(this);
    surfaceView.setEGLContextClientVersion(2);
    surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
    surfaceView.setPreserveEGLContextOnPause(true);
    surfaceView.setRenderer(renderer);

    // Set the GLSurfaceView as the GvrLayout's presentation view.
    gvrLayout.setPresentationView(surfaceView);

    // Enable and configure the back button in the UI layout.
    gvrLayout
        .getUiLayout()
        .setBackButtonListener(
            new Runnable() {
              @Override
              public void run() {
                onBackPressed();
              }
            });

    // Add the GvrLayout to the View hierarchy.
    setContentView(gvrLayout);

    assetManager = getResources().getAssets();

    nativeOnCreate(assetManager, gvrLayout.getGvrApi().getNativeGvrContext());

    // Prevent screen from dimming/locking.
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}

setImmersiveStickyでフルスクリーン表示に

private void setImmersiveSticky() {
    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
  }


AndroidCompat.setVrModeEnabledはGoogle VR SDKのメソッド
AndroidCompat  |  Google VR  |  Google Developers
N+であればVRモードになる
例えばコントローラーが使えるようになったりする、これでdaydream modeになるってことでいいのかな?


GvrLayoutはGoogle VR SDKにあるlayout
GvrLayout  |  Google VR  |  Google Developers
中身はだいたいsurfaceview, GLSurfaceviewにしてVRを表現するらしい
こんな感じ

gvrLayout.setPresentationView(surfaceView);


また、GvrLayoutから取得できるGvrApiを使えばステレオVRなどが簡単に設定できるらしい・・・けどこれnativeでなんか書いて設定してるって感じに見える
GvrApiはポイントになりそう
GvrApi  |  Google VR  |  Google Developers

gvrApi = gvrLayout.getApi();
nativeOnDrawFrame(gvrApi.getNativeGvrContext());


スマホの動きが検知できてるか確認
動きが検知できたらsustainedperformancemodeなるものにする、なんだろこれ

   // Enable scan line racing, if possible.
    if (gvrLayout.setScanlineRacingEnabled(true)) {
      Log.d(TAG, "Successfully enabled scanline racing.");
      // Scanline racing decouples the app framerate from the display framerate,
      // allowing immersive interaction even at the throttled clockrates set by
      // sustained performance mode.
      AndroidCompat.setSustainedPerformanceMode(this, true);
    } else {
      Log.w(TAG, "Failed to enable scanline racing.");
    }

GLSurfaceView設定
GLSurfaceViewについては以下
GLSurfaceView | Android Developers

setEGLContextClientVersion指定してるってことはVulkan使ってないのかな?
nativeざっと眺めてもvk系の関数はなかった

    // Configure the GLSurfaceView.
    surfaceView = new GLSurfaceView(this);
    
    surfaceView.setEGLContextClientVersion(2);
    surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
    surfaceView.setPreserveEGLContextOnPause(true);
    surfaceView.setRenderer(renderer);


renderは以下の感じでnativeでやってる

  private final GLSurfaceView.Renderer renderer = new GLSurfaceView.Renderer() {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
      nativeOnSurfaceCreated();
    }
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
      nativeOnSurfaceChanged(width, height);
    }
    @Override
    public void onDrawFrame(GL10 gl) {
      nativeOnDrawFrame();
    }
  };


Gvrlayoutを使うとデフォルトでバックボタンが効かなくなるらしい
GvrUiLayout  |  Google VR  |  Google Developers
のでわざわざイベントとってonBackPressedしてる

    // Set the GLSurfaceView as the GvrLayout's presentation view.
    gvrLayout.setPresentationView(surfaceView);

    // Enable and configure the back button in the UI layout.
    gvrLayout
        .getUiLayout()
        .setBackButtonListener(
            new Runnable() {
              @Override
              public void run() {
                onBackPressed();
              }
            });

    // Add the GvrLayout to the View hierarchy.
    setContentView(gvrLayout);

    assetManager = getResources().getAssets();

    nativeOnCreate(assetManager, gvrLayout.getGvrApi().getNativeGvrContext());

    // Prevent screen from dimming/locking.
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  }

わからないこと

  • 公式からのサンプルリンク切れてたけどもっといいサンプルがどこかにあるのか?
  • google vr sdkで作れるよ!って言ってるけどcardboardとの違いはsetVrModeEnabledでいいのか?

感想

ドキュメントがリンク切れがあったりするがAPI Documentは割としっかりしてたので思ってたより理解できた
java層読むだけでも勉強になる
時間あったらcpp層も読んでみよう
これでOpen GLES層が既存とあまり変わってないならJava層少し触るだけでdaydream対応できるってことになるのでCardboardアプリが簡単にdaydream対応できるかも?

よく使うショートカット

Android AndroidStudio

概要

よく使う、使いそうなショートカットをまとめる

詳細

Find Action(COMMAND + SHIFT + A)

AndroidStudioのすべての機能にアクセスできる
f:id:tiro105:20160701224211p:plain

Basic Completion(CTRL+SPACE)

文字列から予測する補完機能
Macではspotlightと被っているのでspotlightを変えるか、AndroidStudio側を変えるかする

Smart Type Completion(CTRL + SHIFT SPACE)

型を使った補完機能
今必要としている型を予測して補完してくれる
f:id:tiro105:20160701224731p:plain

Quick Fix(ALT + ENTER)

AndroidStudioから提示されたエラー修正を取り込む

Complete Current Statement(COMMAND + SHIFT + ENTER)

現在の文を完了させることができる
フォーマットしつつ、セミコロンや括弧を補完してくれる
f:id:tiro105:20160701225313p:plain
f:id:tiro105:20160701225327p:plain

Parameter Info(COMMAND + P)

メソッド引数を表示
f:id:tiro105:20160701225440p:plain

Generate(COMMAND + N)

コードの自動生成(getterやsetterなど)
f:id:tiro105:20160701225558p:plain

Extract(COMMAND + ALT + (V F M C))

項目を抜き出す

例えばtaroと文字列を入力した状態
f:id:tiro105:20160701230034p:plain

ここで COMMAND + ALR + Vをすると変数を作成して代入してくれる
f:id:tiro105:20160701230114p:plain

COMMAND + ALR + Fをするとクラス変数として代入
f:id:tiro105:20160701230146p:plain

VはVariable, FはField, MはMethod, CはConstというふうに覚えれば楽

Postfix Completion

たとえば「変数.par」を入力してENTERをおすと変数を括弧でくくってくれる

.notnull

f:id:tiro105:20160701230514p:plain
f:id:tiro105:20160701230525p:plain

.var

ローカル変数に代入
f:id:tiro105:20160701230704p:plain
f:id:tiro105:20160701230717p:plain

Recent Files(COMMAND + E)

最近使ったフィアルを開く
f:id:tiro105:20160701230856p:plain

Search Everywhere

SHIFT二回押し

すべてのものから検索を行う
f:id:tiro105:20160701231204p:plain

Navigate Symnol(Command + SHIFT + O)

メソッドやメンバ変数から検索、XMLなどはでてこない
Search Everywhereより時間がかからないのでよく使う
f:id:tiro105:20160701231351p:plain

Navigate Declaration(COMMAND + B)

メソッドの宣言を開く

Navigate Call Hierarchy(CTRL + ALT + H)

メソッドの宣言で使うと読み出し元一覧が表示される
f:id:tiro105:20160701231633p:plain

参考

黒帯エンジニアが教えるプロの技術 Android開発の教科書 (ヤフー黒帯シリーズ)

黒帯エンジニアが教えるプロの技術 Android開発の教科書 (ヤフー黒帯シリーズ)

Vulkanのサンプルを動かしてみた

Android

概要

VulkanとはAndroid nから実装されるOpen Gl ESの後継となる機能
以下の情報をもとにVulkanのサンプルを動かすまでの険しい道のり
Vulkan Setup | Android Developers

Vulkanの情報があまり見つからなかったのでまとめた
(まだpreviewなのでどんどん変わっていくと思うので注意、n-ndkも7月にアップデートされる様子)

詳細

N用AndroidNdkダウンロード

N用のAndroidNdkをダウンロードする
Home · android-ndk/ndk Wiki · GitHub

サンプルアプリ、環境構築

サンプルアプリダウンロード

$ git clone https://github.com/googlesamples/vulkan-basic-samples.git

ディレクトリ移動

$ cd LunarGSamples

環境構築用shellscript実行

$ ./update_external_sources.sh -s -g
shellscriptがエラーになる時

以下エラーが実行に発生した
変数が読み込めない
ハイフン(-)が変数として読み込めないので以下変数の名前を変更
create_spirv-tools→create_spirv_tools
update_spirv-tools→update_spirv_tools
build_spirv-tools→build_spirv_tools

cmakeコマンドがない
cmakeコマンドがないのでmacportsからインストールする

$ sudo port install cmake +gui

portコマンドがない時は以下参照
http://qiita.com/us10096698/items/54195e60b7afb60105ef

サンプルプロジェクトのインポート

ダウンロードしたプロジェクトをAndroidStudioにインポート
すべてのプロジェクトを閉じ、Eclipse用プロジェクトのインポートを選択
LunarGSamples/API-Samples/androidを選択しOK

AndroidNdkの設定

プロジェクトを右クリックし、open module setting選択
sdk-settingからndkにダウンロードしてきたr-12betaを選択する

build

対象のプロジェクトを選び、build.gradleのcompileSdkVersionがN(23)になっているので24に修正

compileSdkVersion = 24

実行するprojectを選択し、Build > Build Project ~を選択
最初のビルドはすごい時間かかる(10分以上かかった

build時の権限エラー
make: /Users/01011818/Library/Android/android-ndk-r12b/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-g++: Permission denied

などコマンドの権限関係でエラーになる時はAndroidStudioを管理者権限で実行する
(コマンド自体は755で実行できそうなのだが・・・)

感想

変数名エラーなど凄まじいエラーが隠れておりなかなか苦労した・・・
月一アップデートだから仕方ないか・・・

NのDataSaverを試してみた

Android

概要

Android N Developer preview2に入っているDataSaverの挙動を調べてみた

詳細

What's DataSaver?

アプリのバックグラウンド通信を禁止することにより、モバイル通信量を少なくする機能
WIFI環境に入ればバックグラウンドは自動的に行われる
また、ホワイトリストに対象アプリを入れることで、バックグラウンド通信を許可するアプリも設定することが可能になる

開発環境構築

まずはAndroid Nの開発環境作るところから
プレビューのセットアップ | Android Developers
今回は公式も日本語版の説明を用意してくれており非常にわかりやすかった

簡単に箇条書きにすると

  • Android Studio 2.1を入手
  • N用のAndroid SDK入手
  • Java8を入手、設定
  • gradleファイルを以下のように設定
android {
  compileSdkVersion 'android-N'
  buildToolsVersion '24.0.0-rc1'
  ...

  defaultConfig {
     minSdkVersion 'N'
     targetSdkVersion 'N'
     ...
  }
  ...
}

端末のDataSaverモードをONにする

設定 > データ使用量 > データセーバー に移動し、ONに設定
f:id:tiro105:20160504162727p:plain

データへの無制限アクセスからホワイトリストの設定が可能
f:id:tiro105:20160504163033p:plain
f:id:tiro105:20160504163049p:plain

アプリからDataSaverモードを確認する

ConnectivityManagerを使って確認する
ManifestにNETWORK_STATEの権限を追加

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
        int restrictBackgroundStatus = connectivityManager.getRestrictBackgroundStatus();

        switch (restrictBackgroundStatus) {
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
                Log.v("TAG", "RESTRICT_BACKGROUND_STATUS_DISABLED");
                break;
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
                Log.v("TAG", "RESTRICT_BACKGROUND_STATUS_ENABLED");
                break;
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
                Log.v("TAG", "RESTRICT_BACKGROUND_STATUS_WHITELISTED");
                break;

        }

・・・が、preview2で確認したところDataSaverをONにしたらENABLEが帰ってきて、OFFにしたらDISABLEが帰ってきた(ホワイトリストはちゃんと帰ってきた)
バックグラウンドのステータスなので逆じゃない?
でもRESTRICTだからこれでいいのかな?禁止中がenableになってるってことかなー
一応Issueを投げてはおいたので、答え待ち