VRActivity with Native Code

To develop a VR application with a hybrid mode (mixed with java and ndk), it is recommended that the Activity of the application inherits the VRActivity provided in the Wave VR SDK.

In this tutorial, you will learn how to create an application with a hybrid mode. The sample project is called hellovr. To get the source code and build, refer to Native SDK Getting Started.

Contents

Loading Wave VR Libraries

Because Wave VR SDK is implemented as native C++, the app needs to access it in a JNI layer. In hellovr, the JNI layer is a library named libjninative.so. To load the JNI library, call System.loadLibrary() in the static initialization block of the MainActivity class.

import com.htc.vr.sdk.VRActivity;

public class MainActivity extends VRActivity {
    static {
        System.loadLibrary("jninative");
    }
}

Next, link the app’s native library to the Wave VR SDK libraries.

The jninative module rely the libwvr_api.so library which is Wave VR provided, so developer should import the wvr_client.aar to project. To package the libraries with the app, build them as the PREBUILT_SHARED_LIBRARY in the Android.mk.

VR_SDK_LIB := $(firstword  \
    $(realpath $(VR_SDK_ROOT)/jni/$(TARGET_ARCH_ABI))

include $(CLEAR_VARS)
LOCAL_MODULE := wvr_api
LOCAL_SRC_FILES := $(VR_SDK_LIB)/libwvr_api.so
include $(PREBUILT_SHARED_LIBRARY)

# others are skipped.  Please continue to include all of them

And build the sample code files with

COMMON_INCLUDES := \
$(LOCAL_PATH)/include \
$(VR_SDK_ROOT)/include \
$(LOCAL_PATH)/object \
$(LOCAL_PATH)/scene \
$(LOCAL_PATH)/shared \
$(LOCAL_PATH)

COMMON_FILES := \
hellovr.cpp \
Context.cpp \
shared/Matrices.cpp \
object/Texture.cpp \
object/VertexArrayObject.cpp \
object/FrameBufferObject.cpp \
object/Shader.cpp \
object/Object.cpp \
scene/SkyBox.cpp \
scene/ControllerAxes.cpp \
scene/Picture.cpp \
scene/ControllerCube.cpp \
scene/Sphere.cpp \
scene/Floor.cpp \

include $(CLEAR_VARS)
LOCAL_MODULE    := common
LOCAL_C_INCLUDES := $(COMMON_INCLUDES)
LOCAL_SRC_FILES := $(COMMON_FILES)
LOCAL_CFLAGS    := -g
LOCAL_LDLIBS    := -llog -ljnigraphics -landroid -lEGL -lGLESv3
LOCAL_SHARED_LIBRARIES := wvr_api
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := jninative
LOCAL_C_INCLUDES := $(COMMON_INCLUDES)
LOCAL_CFLAGS    := -g
LOCAL_LDLIBS    := -llog -landroid
LOCAL_SRC_FILES := \
jni.cpp

LOCAL_SHARED_LIBRARIES := common wvr_api include $(BUILD_SHARED_LIBRARY)

The LOCAL_SHARED_LIBRARIES variable must be set to wvr_api. Once this is set, the other prebuilt libraries will be linked. You don’t need to add them.

Registering the Main Function

Open the jni.cpp file.

#include <wvr/wvr.h>

int main(int argc, char *argv[]) {

    return 0;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    WVR_RegisterMain(main);
    return JNI_VERSION_1_6;
}

When MainActivity is launched, JNI_OnLoad() is called by System.loadLibrary(jninative). At this moment, the main() callback is registered to the VR system by WVR_RegisterMain. The main function must take this form: int (*)(int, char**).

In onCreate() of VRActivity, the VR server will be launched, and the VR client will prepare an OpenGL surface view. Then, the activity waits for the response from the VR server.

When the VR server is ready, a new thread will start to run and call the main() that was registered. Therefore, main() is not running in the Activity’s main thread.

Implementing the VR Work

The major steps for VR work appear in main().

// jni.cpp
#include <hellovr.h>

int main(int argc, char *argv[]) {
    LOGENTRY();
    MainApplication *app = new MainApplication();

    if (!app->initVR()) {
        app->shutdownVR();
        return 1;
    }

    if (!app->initGL()) {
        app->shutdownGL();
        app->shutdownVR();
        return 1;
    }

    while (1) {
        if (app->handleInput())
            break;

        if (app->renderFrame()) {
            LOGE("Unknown render error.  Quit.");
            break;
        }

        app->updateHMDMatrixPose();
    }

    app->shutdownGL();
    app->shutdownVR();

    return 0;
}

The MainApplication class is the hellovr main application.

// hellovr.cpp

bool MainApplication::initVR() {
    // Loading the WVR Runtime
    WVR_InitError eError = WVR_InitError_None;
    eError = WVR_Init(WVR_AppType_VRContent);

    if (eError != WVR_InitError_None)
        return false;

    // Must initialize render runtime before all OpenGL code.
    WVR_RenderInitParams_t param = {WVR_GraphicsApiType_OpenGL, WVR_RenderConfig_Timewarp_Asynchronous};
    WVR_RenderError pError = WVR_RenderInit(&param);

    return true;
}

void MainApplication::shutdownVR() {
    WVR_Quit();
}

First, invoke WVR_Init(). This must be done prior to any VR operation. WVR_Init() will initialize the VR system and return the enum WVR_InitError to know what error occurs.

Next, invoke WVR_RenderInit() before any OpenGL operations. The WVR_RenderInit() will initialize a render environment. After that, you can continue to initialize content.

The while loop will break if the activity is ended or if there is an error. The while loop calls HandleInput(), RenderFrame(), and updateHMDMatrixPose(), which are explained in the next chapter, Developing with the SDK.

When an app exits, invoke WVR_Quit() to release the resources allocated to the Wave VR runtime. After main() is returned, the activity will be finished too. If WVR_Quit() isn’t called when the application exits, WVR_Init() of the same activity process may generate an error the next time it is invoked.

Note

WVR_Init() can be run in different processes. For example, if there are two VR applications, one can run in the background while the other one runs in the foreground.