Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for haptic feedback on Pico controllers #2932

Merged
merged 2 commits into from
Mar 9, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ target_sources(
PUBLIC
src/picovr/cpp/native-lib.cpp
src/picovr/cpp/DeviceDelegatePicoVR.cpp
src/picovr/cpp/VRBrowserPico.cpp
)
elseif(NOAPI)
target_sources(
Expand Down
23 changes: 21 additions & 2 deletions app/src/picovr/cpp/DeviceDelegatePicoVR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#include "DeviceDelegatePicoVR.h"
#include "ElbowModel.h"
#include "BrowserEGLContext.h"
#include "VRBrowserPico.h"

#include <EGL/egl.h>
#include "vrb/CameraEye.h"
Expand Down Expand Up @@ -53,6 +53,7 @@ struct DeviceDelegatePicoVR::State {
float axisX = 0;
float axisY = 0;
ElbowModel::HandEnum hand;
int hapticFrameID = 0;
Controller()
: index(-1)
, created(false)
Expand Down Expand Up @@ -142,6 +143,16 @@ struct DeviceDelegatePicoVR::State {
}
}

void UpdateHaptics(Controller& aController) {
uint64_t inputFrameID = 0;
float pulseDuration = 0.0f, pulseIntensity = 0.0f;
controllerDelegate->GetHapticFeedback(aController.index, inputFrameID, pulseDuration, pulseIntensity);

if (aController.hapticFrameID != inputFrameID) {
VRBrowserPico::UpdateHaptics(aController.index, pulseIntensity, pulseDuration);
}
}

void UpdateControllers() {
for (int32_t i = 0; i < controllers.size(); ++i) {
if (!controllers[i].enabled) {
Expand Down Expand Up @@ -208,6 +219,10 @@ struct DeviceDelegatePicoVR::State {
}

controllerDelegate->SetTransform(i, transform);

if (controllerDelegate->GetHapticCount(i)) {
UpdateHaptics(controllers[i]);
}
}
}
};
Expand All @@ -227,6 +242,10 @@ DeviceDelegatePicoVR::SetRenderMode(const device::RenderMode aMode) {
return;
}
m.renderMode = aMode;
if (aMode == device::RenderMode::StandAlone) {
// Ensure that all haptics are cancelled when exiting WebVR
VRBrowserPico::CancelAllHaptics();
}
}

device::RenderMode
Expand Down Expand Up @@ -296,7 +315,7 @@ DeviceDelegatePicoVR::SetControllerDelegate(ControllerDelegatePtr& aController)
beam.TranslateInPlace(vrb::Vector(0.0f, 0.012f, -0.06f));
m.controllerDelegate->CreateController(index, int32_t(controller.hand), controller.IsRightHand() ? "Pico Neo 2 (Right)" : "Pico Neo 2 (LEFT)", beam);
m.controllerDelegate->SetButtonCount(index, kNumButtons);
m.controllerDelegate->SetHapticCount(index, 0);
m.controllerDelegate->SetHapticCount(index, 1);
} else {
vrb::Matrix beam = vrb::Matrix::Rotation(vrb::Vector(1.0f, 0.0f, 0.0f), -vrb::PI_FLOAT / 11.5f);

Expand Down
76 changes: 76 additions & 0 deletions app/src/picovr/cpp/VRBrowserPico.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "VRBrowserPico.h"
#include "vrb/ConcreteClass.h"
#include "vrb/Logger.h"
#include "JNIUtil.h"

namespace {

const char* sUpdateHapticsName = "updateHaptics";
const char* sUpdateHapticsSignature = "(IFF)V";
const char* sCancelAllHapticsName = "cancelAllHaptics";
const char* sCancelAllHapticsSignature = "()V";

JNIEnv* sEnv = nullptr;
jclass sBrowserClass = nullptr;
jobject sActivity = nullptr;
jmethodID sUpdateHaptics = nullptr;
jmethodID sCancelAllHaptics = nullptr;
}

namespace crow {

void
VRBrowserPico::InitializeJava(JNIEnv* aEnv, jobject aActivity) {
if (aEnv == sEnv) {
return;
}
sEnv = aEnv;
if (!sEnv) {
return;
}
sActivity = sEnv->NewGlobalRef(aActivity);
sBrowserClass = sEnv->GetObjectClass(sActivity);
if (!sBrowserClass) {
return;
}

sUpdateHaptics = FindJNIMethodID(sEnv, sBrowserClass, sUpdateHapticsName, sUpdateHapticsSignature);
sCancelAllHaptics = FindJNIMethodID(sEnv, sBrowserClass, sCancelAllHapticsName, sCancelAllHapticsSignature);
}

void
VRBrowserPico::ShutdownJava() {
if (!sEnv) {
return;
}
if (sActivity) {
sEnv->DeleteGlobalRef(sActivity);
sActivity = nullptr;
}

sBrowserClass = nullptr;
sUpdateHaptics = nullptr;
sCancelAllHaptics = nullptr;
sEnv = nullptr;
}


void
VRBrowserPico::UpdateHaptics(jint aControllerIndex, jfloat aIntensity, jfloat aDuration) {
if (!ValidateMethodID(sEnv, sActivity, sUpdateHaptics, __FUNCTION__)) { return; }
sEnv->CallVoidMethod(sActivity, sUpdateHaptics, aControllerIndex, aIntensity, aDuration);
CheckJNIException(sEnv, __FUNCTION__);
}
void
VRBrowserPico::CancelAllHaptics() {
if (!ValidateMethodID(sEnv, sActivity, sCancelAllHaptics, __FUNCTION__)) { return; }
sEnv->CallVoidMethod(sActivity, sCancelAllHaptics);
CheckJNIException(sEnv, __FUNCTION__);
}

} // namespace crow
23 changes: 23 additions & 0 deletions app/src/picovr/cpp/VRBrowserPico.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#pragma once
#include "vrb/MacroUtils.h"

#include <memory>
#include <string>
#include <jni.h>
#include <functional>

namespace crow {

namespace VRBrowserPico {
void InitializeJava(JNIEnv* aEnv, jobject aActivity);
void ShutdownJava();
void UpdateHaptics(jint aControllerIndex, jfloat aIntensity, jfloat aDuration);
void CancelAllHaptics();
} // namespace VRBrowser;

} // namespace crow
3 changes: 3 additions & 0 deletions app/src/picovr/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "BrowserWorld.h"
#include "DeviceDelegatePicoVR.h"
#include "VRBrowserPico.h"
#include "vrb/GLError.h"
#include "vrb/Logger.h"
#include "vrb/RunnableQueue.h"
Expand Down Expand Up @@ -36,6 +37,7 @@ JNI_METHOD(void, nativeInitialize)
(JNIEnv* aEnv, jobject aActivity, jint width, jint height, jobject aAssetManager, jint type, jint focusInex) {
gDestroyed = false;
sQueue->AttachToThread();
VRBrowserPico::InitializeJava(aEnv, aActivity);
if (!sDevice) {
sDevice = crow::DeviceDelegatePicoVR::Create(BrowserWorld::Instance().GetRenderContext());
}
Expand All @@ -61,6 +63,7 @@ JNI_METHOD(void, nativeDestroy)
BrowserWorld::Instance().RegisterDeviceDelegate(nullptr);
sDevice = nullptr;
BrowserWorld::Destroy();
VRBrowserPico::ShutdownJava();
}

JNI_METHOD(void, nativePause)
Expand Down
27 changes: 27 additions & 0 deletions app/src/picovr/java/org/mozilla/vrbrowser/PlatformActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Keep;
import androidx.annotation.NonNull;

import com.picovr.client.HbListener;
Expand Down Expand Up @@ -97,6 +98,7 @@ public void onBindService() {
@Override
protected void onPause() {
if (mControllerManager != null) {
cancelAllHaptics();
mControllerManager.unbindService();
} else if (mHbManager != null) {
mHbManager.Pause();
Expand Down Expand Up @@ -315,6 +317,31 @@ public void onChannelChanged(int i, int i1) {

}

// Called by native
@Keep
@SuppressWarnings("unused")
private void updateHaptics(int aControllerIndex, float aIntensity, float aDurationSeconds) {
runOnUiThread(() -> {
if (mControllerManager != null) {
float intensity = 255.0f * Math.max(Math.min(aIntensity, 1.0f), 0.0f);
int durationMs = Math.round(aDurationSeconds * 1000);
ControllerClient.vibrateCV2ControllerStrength(intensity, durationMs, 1 - aControllerIndex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to swap the hands to get the correct controller to vibrate.

}
});
}

// Called by native
@Keep
@SuppressWarnings("unused")
private void cancelAllHaptics() {
runOnUiThread(() -> {
if (mControllerManager != null) {
ControllerClient.vibrateCV2ControllerStrength(0, 0, 0);
ControllerClient.vibrateCV2ControllerStrength(0, 0, 1);
}
});
}

protected native void nativeOnCreate();
protected native void nativeInitialize(int width, int height, Object aAssetManager, int type, int focusIndex);
protected native void nativeShutdown();
Expand Down