Skip to content
This repository has been archived by the owner on Feb 1, 2024. It is now read-only.

Add support to notify when the headset is (un)mounted #97

Merged
merged 1 commit into from
Apr 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/export_presets.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ one_click_deploy/clear_previous_install=true
custom_template/debug=""
custom_template/release=""
custom_template/use_custom_build=true
custom_template/plugins=""
custom_template/plugins="OVRMobile"
command_line/extra_args=""
version/code=1
version/name="1.0"
Expand Down
27 changes: 27 additions & 0 deletions plugin/src/main/cpp/jni/ovr_mobile_plugin_jni.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Created by Fredia Huya-Kouadio.
*/

#include <jni.h>

#include "ovr_mobile_session.h"
#include "jni_common.h"
#include "ovr_mobile_plugin_wrapper.h"

#undef JNI_PACKAGE_NAME
#define JNI_PACKAGE_NAME org_godotengine_plugin_vr_oculus_mobile

#undef JNI_CLASS_NAME
#define JNI_CLASS_NAME OvrMobilePlugin

extern "C" {
JNIEXPORT void JNICALL JNI_METHOD(initializeWrapper)(JNIEnv* env,
jobject object) {
ovrmobile::OvrMobilePluginWrapper::initializeWrapper(env, object);
}

JNIEXPORT void JNICALL JNI_METHOD(uninitializeWrapper)(JNIEnv* env,
jobject object) {
ovrmobile::OvrMobilePluginWrapper::uninitializeWrapper(env);
}
};
60 changes: 60 additions & 0 deletions plugin/src/main/cpp/jni/ovr_mobile_plugin_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Created by Fredia Huya-Kouadio.
*/

#include "ovr_mobile_plugin_wrapper.h"
#include "common.h"

namespace ovrmobile {

jobject OvrMobilePluginWrapper::ovr_mobile_plugin_instance = nullptr;
jmethodID OvrMobilePluginWrapper::on_headset_mounted_id = nullptr;
jmethodID OvrMobilePluginWrapper::on_headset_unmounted_id = nullptr;

OvrMobilePluginWrapper::OvrMobilePluginWrapper() {}

OvrMobilePluginWrapper::~OvrMobilePluginWrapper() {}

void OvrMobilePluginWrapper::initializeWrapper(JNIEnv* env,
jobject ovr_mobile_plugin) {
ovr_mobile_plugin_instance = env->NewGlobalRef(ovr_mobile_plugin);
ALOG_ASSERT(ovr_mobile_plugin_instance != nullptr, "Invalid jobject value.");

jclass
ovr_mobile_plugin_class = env->GetObjectClass(ovr_mobile_plugin_instance);
ALOG_ASSERT(ovr_mobile_plugin_class != nullptr, "Invalid jclass value.");

on_headset_mounted_id =
env->GetMethodID(ovr_mobile_plugin_class, "onHeadsetMounted", "()V");
ALOG_ASSERT(on_headset_mounted_id != nullptr,
"Unable to find onHeadsetMounted");

on_headset_unmounted_id =
env->GetMethodID(ovr_mobile_plugin_class, "onHeadsetUnmounted", "()V");
ALOG_ASSERT(on_headset_unmounted_id != nullptr,
"Unable to find onHeadsetUnmounted");
}

void OvrMobilePluginWrapper::uninitializeWrapper(JNIEnv* env) {
if (ovr_mobile_plugin_instance) {
env->DeleteGlobalRef(ovr_mobile_plugin_instance);
ovr_mobile_plugin_instance = nullptr;
on_headset_mounted_id = nullptr;
on_headset_unmounted_id = nullptr;
}
}

void OvrMobilePluginWrapper::on_headset_mounted() {
if (ovr_mobile_plugin_instance && on_headset_mounted_id) {
JNIEnv *env = android_api->godot_android_get_env();
env->CallVoidMethod(ovr_mobile_plugin_instance, on_headset_mounted_id);
}
}

void OvrMobilePluginWrapper::on_headset_unmounted() {
if (ovr_mobile_plugin_instance && on_headset_unmounted_id) {
JNIEnv* env = android_api->godot_android_get_env();
env->CallVoidMethod(ovr_mobile_plugin_instance, on_headset_unmounted_id);
}
}
} // namespace ovrmobile
35 changes: 35 additions & 0 deletions plugin/src/main/cpp/jni/ovr_mobile_plugin_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Created by Fredia Huya-Kouadio.
*/

#ifndef OVRMOBILE_OVR_MOBILE_PLUGIN_WRAPPER_H
#define OVRMOBILE_OVR_MOBILE_PLUGIN_WRAPPER_H

#include <jni.h>

namespace ovrmobile {

// Provide native access to the org.godotengine.plugin.vr.oculus.mobile.OvrMobilePlugin instance.
class OvrMobilePluginWrapper {
public:
static void initializeWrapper(JNIEnv* env, jobject ovr_mobile_plugin);

static void uninitializeWrapper(JNIEnv* env);

static void on_headset_mounted();

static void on_headset_unmounted();

private:
OvrMobilePluginWrapper();
~OvrMobilePluginWrapper();

static jobject ovr_mobile_plugin_instance;
static jmethodID on_headset_mounted_id;
static jmethodID on_headset_unmounted_id;

};

} // namespace ovrmobile

#endif // OVRMOBILE_OVR_MOBILE_PLUGIN_WRAPPER_H
23 changes: 23 additions & 0 deletions plugin/src/main/cpp/ovr_mobile_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ovr_mobile_session.h"
#include "common.h"
#include <unistd.h>
#include "jni/ovr_mobile_plugin_wrapper.h"

namespace ovrmobile {

Expand Down Expand Up @@ -231,6 +232,28 @@ void OvrMobileSession::process() {

// Update the oculus controllers state.
ovr_mobile_controller->process(ovr, &java, predicted_display_time);

// Update the headset_mounted state if necessary
bool is_mounted = is_headset_mounted();
if (headset_mounted != is_mounted) {
if (is_mounted) {
// Notify that the headset is mounted.
OvrMobilePluginWrapper::on_headset_mounted();
} else {
// Notify that the headset is unmounted.
OvrMobilePluginWrapper::on_headset_unmounted();
}
headset_mounted = is_mounted;
}
}

bool OvrMobileSession::is_headset_mounted() const {
if (!in_vr_mode()) {
return false;
}

return vrapi_GetSystemStatusInt(&java, VRAPI_SYS_STATUS_MOUNTED)
== VRAPI_TRUE;
}

bool OvrMobileSession::enter_vr_mode() {
Expand Down
7 changes: 5 additions & 2 deletions plugin/src/main/cpp/ovr_mobile_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ class OvrMobileSession {
// the api functionality.
bool initialize();

bool in_vr_mode() {
bool in_vr_mode() const {
return initialized && ovr != nullptr;
}

bool is_initialized() { return initialized; }
bool is_initialized() const { return initialized; }

// Notifies that the interface should be paused. For ovrmobile, this means we should leave vr mode if it's active.
void on_pause();
Expand Down Expand Up @@ -92,6 +92,8 @@ class OvrMobileSession {
return godot_eye == /* EYE_RIGHT */ 2 ? static_cast<int>(ovrEye::VRAPI_EYE_RIGHT) : static_cast<int>(ovrEye::VRAPI_EYE_LEFT);
}

bool is_headset_mounted() const;

static OvrMobileSession* singleton_instance;

bool initialized = false;
Expand All @@ -101,6 +103,7 @@ class OvrMobileSession {
unsigned int swap_interval;
uint64_t frame_index = 1;
double predicted_display_time = 0;
bool headset_mounted = true;

ovrVector4f default_layer_color_scale;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,84 @@ package org.godotengine.plugin.vr.oculus.mobile

import org.godotengine.godot.Godot
import org.godotengine.godot.plugin.GodotPlugin
import java.util.concurrent.ConcurrentLinkedQueue

/**
* Driver class for the Oculus Mobile Godot plugin.
*/
class OvrMobilePlugin(godot: Godot) : GodotPlugin(godot) {

/**
* Used to inquire about the headset state.
*/
interface HeadsetStateListener {
/**
* Notifies when the headset is mounted.
*/
fun onHeadsetMounted()

/**
* Notifies when the headset is unmounted.
*/
fun onHeadsetUnmounted()
}

companion object {
init {
System.loadLibrary("godot_ovrmobile")
}
}

private val headsetStateListeners = ConcurrentLinkedQueue<HeadsetStateListener>()

override fun getPluginGDNativeLibrariesPaths() =
setOf("addons/godot_ovrmobile/godot_ovrmobile.gdnlib")

override fun getPluginMethods() = listOf<String>()

override fun getPluginName() = "OVRMobile"

override fun onGLGodotMainLoopStarted() {
super.onGLGodotMainLoopStarted()
initializeWrapper()
}

override fun onMainDestroy() {
super.onMainDestroy()
runOnGLThread {
uninitializeWrapper()
}
}

fun registerHeadsetStateListener(listener: HeadsetStateListener) {
headsetStateListeners.add(listener)
}

fun unregisterHeadsetStateListener(listener: HeadsetStateListener) {
headsetStateListeners.remove(listener)
}

/**
* Invoked by the native code to signal the headset is mounted.
*/
private fun onHeadsetMounted() {
// TODO(m4gr3d): Emit headset mounted signal when https://github.com/godotengine/godot/pull/37305 is submitted.
for (listener in headsetStateListeners) {
listener.onHeadsetMounted()
}
}

/**
* Invoked by the native code to signal the headset is unmounted.
*/
private fun onHeadsetUnmounted() {
// TODO(m4gr3d): Emit headset unmounted signal when https://github.com/godotengine/godot/pull/37305 is submitted.
for (listener in headsetStateListeners) {
listener.onHeadsetUnmounted()
}
}

private external fun initializeWrapper()

private external fun uninitializeWrapper()
}