Gaze to load

We want to detect when the user looks at a thumbnail and highlight the image by changing its border color. If users move their gaze away from the thumbnail, it will unhighlight. When the user clicks on the Cardboard trigger, that image is loaded.

Gaze-based highlights

Fortunately, we implemented the isLooking detection in the RenderBox library at the end of Chapter 5, RenderBox Engine. If you remember, the technique determines whether the user is looking at the plane by checking whether the vector between the camera and the plane position is the same as the camera's view direction, within a threshold of tolerance.

We can use this in MainActivity. We'll write a selectObject helper method that checks whether any of the objects in the scene are selected and highlights them. First, let's declare some variables at the top of the MainActivity class. The selectedThumbnail object holds the currently selected thumbnail index. We define border colors for normal and selected states:

    final float[] selectedColor = new float[]{0, 0.5f, 0.5f, 1};
    final float[] invalidColor = new float[]{0.5f, 0, 0, 1};
    final float[] normalColor = new float[]{0, 0, 0, 1};
    Thumbnail selectedThumbnail = null;

Now the selectObject method goes through each thumbnail, checks whether it's isLooking, and highlights (or unhighlights) it accordingly:

    void selectObject() {
        selectedThumbnail = null;
        for (Thumbnail thumb : thumbnails) {
            if (thumb.image == null)
                return;
            Plane plane = thumb.plane;
            BorderMaterial material = (BorderMaterial) plane.getMaterial();
            if (plane.isLooking) {
                selectedThumbnail = thumb;
                material.borderColor = selectedColor;
            } else {
                material.borderColor = normalColor;
            }
        }
    }

RenderBox provides hooks, including postDraw where we'll check for selected objects. We want to use postDraw because we need to wait until draw is called on all of RenderObjects before we know which one the user is looking at. In MainActivity, add a call to the selectObject method as follows:

    @Override
    public void postDraw() {
        selectObject();
    }

Run the project. As you gaze at a thumbnail image, it should get highlighted!

Selecting and showing photos

Well, now that we can pick an image from the thumbnail grid, we need a way to click on it and show that image. That'll happen in MainActivity using the Cardboard SDK hook, onCardboardTrigger.

With all the work we've done so far, it's not going to take much more to implement this:

    @Override
    public void onCardboardTrigger() {
        if (selectedThumbnail != null) {
            showImage(selectedThumbnail.image);
        }
    }

Try and run it. Now highlight an image and pull the trigger. If you're lucky, it'll work…mine crashes.

Queue events

What's going on? We're running into thread-safe issues. So far, we've been executing all of our code from the render thread, which is started by the GLSurfaceView/CardboardView class via the Cardboard SDK. This thread owns the access to the GPU and to the particular surface we're rendering on. The call to onCardboardTrigger originates from a thread that is not the render thread. This means that we can't make any OpenGL calls from here. Luckily, GLSurfaceView provides a nifty way to execute arbitrary code on the render thread through a method called queueEvent. The queueEvent method takes a single Runnable argument, which is a Java class meant to create one-off procedures such as these (refer to http://developer.android.com/reference/android/opengl/GLSurfaceView.html#queueEvent(java.lang.Runnable).

Modify showImage to wrap it inside a Runnable argument, as follows:

    void showImage(final Image image) {
        cardboardView.queueEvent(new Runnable() {
            @Override
            public void run() {

                UnlitTexMaterial bgMaterial = (UnlitTexMaterial) photosphere.getMaterial();
                image.loadFullTexture(cardboardView);
                if (image.isPhotosphere) {
                    Log.d(TAG, "!!! is photosphere");
                    bgMaterial.setTexture(image.textureHandle);
                    screen.enabled = false;
                } else {
                    bgMaterial.setTexture(bgTextureHandle);
                    screen.enabled = true;
                    image.show(cardboardView, screen);
                }

            }
        });
    }

Note that any data passed to the anonymous class, such as our image, must be declared final to be accessible from the new procedure.

Try to run the project again. It should work. You can gaze at a thumbnail, click on the trigger, and that image will be shown, either on the virtual screen or in the background photosphere.

Using a vibrator

No worries, we're keeping it clean. We want to provide some haptic feedback to the user when an image has been selected, using the phone's vibrator. And fortunately, in Android, that's straightforward.

First, make sure that your AndroidManifest.xml file includes the following line of code:

    <uses-permission android:name="android.permission.VIBRATE" />

At the top of the MainActivity class, declare a vibrator variable:

    private Vibrator vibrator;

Then, in onCreate, add the following code to initialize it:

    vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

Then, use it in onCardboardTrigger, as follows:

       vibrator.vibrate(25);

Run it again. Click on it and you'll feel it. Ahhh! But don't get carried away, it's not that kind of vibrator.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset