うさがにっき

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

ViroCoreのSampleCodeを読み解く2

概要

  • 前回ViroCoreを試してだいぶよかった
  • 他に二つサンプルコードがあるので読み解く
  • 今回は3DModel(fbx)のload, 操作関係のメモ

詳細

とりあえずビルド、動かす

  • サンプルの中のARPlacingObjectsをimport、ビルド
    github.com https://raw.githubusercontent.com/viromedia/virocore/master/ARPlacingObjects/ViroARHitTestDemoActivity.gif

Projectを眺めてViroCoreの3DModelのloadや操作について確認

ProjectPackage構成

  • ViroActivity, ViroHelperについては中身同じなので前回の記事参照
  • ViroARObjectPlacementActivity.javaを読み解いていく
    f:id:tiro105:20180307150544p:plain:w200

ViroARObjectPlacementActivity.java

クラス変数

  • 3DModelを配置する際のカメラからの距離定義
  // Constants used to determine if plane or point is within bounds. Units in meters.
    static final float MIN_DISTANCE = .2f;
    static final float MAX_DISTANCE = 10f;
  • ARSceneと3DModelを保持する変数(Draggable3dObject)の宣言
    /*
    Reference to the arScene we will be creating within this activity
    */
    private ARScene mScene;

    /*
     List of draggable 3d objects in our scene.
     */
    private List<Draggable3dObject> mDraggableObjects;

コンストラクタ

  • Activityのコンストラクタで3DModelを保持するListをnew
    public ViroARObjectPlacementActivity() {
        mDraggableObjects = new ArrayList<Draggable3dObject>();
    }

onRendererStart()

  • 最初に全体の描画の流れを追って、最後にDraggable3dObjectについて説明
  • まずonCreate時の呼ばれるonRendererStart()
    @Override
    public void onRendererStart() {
        mScene = new ARScene();
        mScene.displayPointCloud(true);
        //add a listener to the scene so we can update 'AR Init' text.
        mScene.setListener(new ARSceneListener(this, mViroView));
        //add a light to the scene so our models can show up.
        mScene.getRootNode().addLight(new AmbientLight(Color.WHITE, 1000f));
        mViroView.setScene(mScene);
        View.inflate(this, R.layout.viro_view_ar_hit_test_hud, ((ViewGroup) mViroView));
    }
  • mScene.displayPointCloud(true);は自分で追加した、これがないと特徴点を認識しているかどうかがわからない
  • インスタンス化したmSceneに対してLinstenerや光を追加、mSceneをViewに設定

ARSceneListener

  • mScene(ARScene)に設定するListener
  • 前回の記事でいうTrackedPlanesController、実装すべきメソッドは以下を参照
    ARScene.Listener | Android Developers
  • 今回は初期化時に初期化が終わったTextを表示しているだけ
    private static class ARSceneListener implements ARScene.Listener {
        private WeakReference<Activity> mCurrentActivityWeak;

        public ARSceneListener(Activity activity, View rootView) {
            mCurrentActivityWeak = new WeakReference<Activity>(activity);
        }

        @Override
        public void onTrackingInitialized() {
            Activity activity = mCurrentActivityWeak.get();
            if (activity == null){
                return;
            }

            TextView initText = (TextView)activity.findViewById(R.id.initText);
            initText.setText("AR is initialized.");
        }

        @Override
        public void onAmbientLightUpdate(float v, float v1) {

        }

        @Override
        public void onAnchorFound(ARAnchor arAnchor, ARNode arNode) {

        }

        @Override
        public void onAnchorRemoved(ARAnchor arAnchor, ARNode arNode) {

        }

        @Override
        public void onAnchorUpdated(ARAnchor arAnchor, ARNode arNode) {

        }
    }

showPopup(View v)

  • layoutのonClick属性から呼び出されているメソッド
       <ImageButton
            android:id="@+id/imageButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="showPopup"
            android:background="@mipmap/viro_launcher" />
  • AlertDialogを表示して選択された3DModelをplaceObjectメソッドで配置、placeObjectメソッドについては後述
  • なおvrxとはViroMedia独自の3DModelファイル形式(ViroFBX)、以下のツールで作成することができる
    virocore.viromedia.com
   public void showPopup(View v) {

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        CharSequence itemsList[] = {"Coffee mug", "Flowers", "Smile Emoji"};
        builder.setTitle("Choose an object")
                .setItems(itemsList, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        switch (which) {
                            case 0:
                                placeObject("file:///android_asset/object_coffee_mug.vrx");
                                break;
                            case 1:
                                placeObject("file:///android_asset/object_flowers.vrx");
                                break;
                            case 2:
                                placeObject("file:///android_asset/emoji_smile.vrx");
                                break;
                        }
                    }
                });


        Dialog d = builder.create();
        d.show();
    }

placeObject(final String fileName)

  • hitTestを行い、その結果から3DModelを配置する
    private void placeObject(final String fileName) {
        ViroViewARCore viewARView = (ViroViewARCore)mViroView;
        final Vector cameraPos  = viewARView.getLastCameraPositionRealtime();
        viewARView.performARHitTestWithRay(viewARView.getLastCameraForwardRealtime(), new ARHitTestListener() {
            @Override
            public void onHitTestFinished(ARHitTestResult[] arHitTestResults) {
                if(arHitTestResults != null ) {
                    if(arHitTestResults.length > 0) {
                        for (int i = 0; i < arHitTestResults.length; i++) {
                            ARHitTestResult result = arHitTestResults[i];
                            float distance = result.getPosition().distance(cameraPos);
                            if(distance > MIN_DISTANCE && distance < MAX_DISTANCE) {
                                // If we found a plane of feature point greater than .2 and less than 10 meters away
                                // then choose it!
                                add3dDraggableObject(fileName, result.getPosition());
                                return;
                            }
                        }
                    }
                }
                Toast.makeText(ViroARObjectPlacementActivity.this, "Unable to find suitable point or plane to place object!", Toast.LENGTH_LONG).show();
            }
        });
    }

add3dDraggableObject(String filename, Vector position)

  • Draggable3dObjectを作成し、listに追加して各種イベントを設定する、Draggable3dObjectについては後述
    private void add3dDraggableObject(String filename, Vector position) {
        Draggable3dObject draggable3dObject = new Draggable3dObject(filename);
        mDraggableObjects.add(draggable3dObject);
        draggable3dObject.addModelToPosition(position);
    }

Draggable3dObject

  • 3DModeのロード、移動、回転などのイベントを管理するクラス
クラス変数、コンストラクタ
        private String mFileName;
        private float rotateStart;
        private float scaleStart;


        public Draggable3dObject(String filename) {
            mFileName = filename;
        }
addModelToPosition(Vector position)
        private void addModelToPosition(Vector position) {
            final Object3D object3D = new Object3D();
            object3D.setPosition(position);
            // Shrink the objects as the original size is too large.
            object3D.setScale(new Vector(.2f, .2f, .2f));
  • Object3Dに対して、回転(setGestureRotateListener)、PinchInOut(setGesturePinchListener)、Drag(setDragListener)を設定
           // https://developer.viromedia.com/virocore/reference/com/viro/core/GestureRotateListener.html
            // nodeへのRotateイベントの口が予め用意されている
            object3D.setGestureRotateListener(new GestureRotateListener() {
                @Override
                public void onRotate(int i, Node node, float rotation, RotateState rotateState) {
                    if(rotateState == RotateState.ROTATE_START) {
                        rotateStart = object3D.getRotationEulerRealtime().y;
                    }
                    float totalRotationY = rotateStart + rotation;
                    object3D.setRotation(new Vector(0, totalRotationY, 0));
                }
            });

            // nodeへのPinch-InOutイベントの口が予め用意されている
            // https://developer.viromedia.com/virocore/reference/com/viro/core/GesturePinchListener.html
            object3D.setGesturePinchListener(new GesturePinchListener() {
                @Override
                public void onPinch(int i, Node node, float scale, PinchState pinchState) {
                    if(pinchState == PinchState.PINCH_START) {
                        scaleStart = object3D.getScaleRealtime().x;
                    } else {
                        object3D.setScale(new Vector(scaleStart * scale, scaleStart * scale, scaleStart * scale));
                    }
                }
            });

            object3D.setDragListener(new DragListener() {
                @Override
                public void onDrag(int i, Node node, Vector vector, Vector vector1) {

                }
            });
  • 非同期に3DModelをloadする
            // Load the Android model asynchronously.
            // loadできるモデルはFBX or OBJ
            // https://developer.viromedia.com/virocore/reference/com/viro/core/Object3D.html#loadModel(android.net.Uri, com.viro.core.Object3D.Type, com.viro.core.AsyncObject3DListener)
            object3D.loadModel(Uri.parse(mFileName), Object3D.Type.FBX, new AsyncObject3DListener() {
                @Override
                public void onObject3DLoaded(final Object3D object, final Object3D.Type type) {
                  //TODO: Display toast saying model loaded successfully.
                }

                @Override
                public void onObject3DFailed(String s) {
                    Toast.makeText(ViroARObjectPlacementActivity.this, "An error occured when loading the 3d Object!", Toast.LENGTH_LONG).show();
                }
            });
  • 3DModelのDragType定義、mSceneに配置
    // Make the object draggable.
            // https://developer.viromedia.com/virocore/reference/com/viro/core/Node.html#setDragType(com.viro.core.Node.DragType)
            // https://developer.viromedia.com/virocore/reference/com/viro/core/Node.DragType.html
            object3D.setDragType(Node.DragType.FIXED_TO_WORLD);
            mScene.getRootNode().addChildNode(object3D);

感想

  • fbxモデルも簡単にロードでき、簡単な操作(PinchInOut, Rotation)はデフォルトで定義されているので非常に扱いやすい
  • vrxなのはfbx sdkを使うとアプリサイズが大きくなりすぎてしまうらしい
  • glTFなどの形式も実装想定らしい
    github.com
  • やはりOpenGLESを扱うことを考えれば大変良い
  • 次は3つ目のサンプル読んで見てメモるべきことがあれば書く