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

Commit

Permalink
Fix the controller vibration API
Browse files Browse the repository at this point in the history
The vrapi_SetHapticVibrationSimple(...) method is supposed to be called once per frame.
To comply, the new logic batches the vibration requests (last one wins), and fire the method call once per frame during `_process` if a vibration request for the controller is available.
The API exposed by the plugin is augmented to take a `duration_in_ms` parameter, which the logic uses to keep firing vrapi_SetHapticVibrationSimple(...) calls until the duration has been reached.
  • Loading branch information
m4gr3d committed May 21, 2020
1 parent 88f7894 commit d279165
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 110 deletions.
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ trim_trailing_whitespace = true

[*.{cpp,hpp,c,h,mm}]
indent_size = 4
ij_c_indent_namespace_members = 4
ij_c_indent_namespace_members = 0

[{*.gradle,AndroidManifest.xml}]
indent_size = 4

[*.gd]
indent_style = tab
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var ovr_guardian_system = null;
var ovr_tracking_transform = null;
var ovr_utilities = null;
var ovr_vr_api_proxy = null;
var ovr_input = null;


# some of the Oculus VrAPI constants are defined in this file. Have a look into it to learn more
Expand Down Expand Up @@ -63,6 +64,7 @@ func _initialize_ovr_mobile_arvr_interface():
ovr_tracking_transform = load("res://addons/godot_ovrmobile/OvrTrackingTransform.gdns");
ovr_utilities = load("res://addons/godot_ovrmobile/OvrUtilities.gdns");
ovr_vr_api_proxy = load("res://addons/godot_ovrmobile/OvrVrApiProxy.gdns");
ovr_input = load("res://addons/godot_ovrmobile/OvrInput.gdns");

# and now instance the .gdns classes for use if load was successfull
if (ovr_display_refresh_rate): ovr_display_refresh_rate = ovr_display_refresh_rate.new()
Expand All @@ -71,7 +73,8 @@ func _initialize_ovr_mobile_arvr_interface():
if (ovr_tracking_transform): ovr_tracking_transform = ovr_tracking_transform.new()
if (ovr_utilities): ovr_utilities = ovr_utilities.new()
if (ovr_vr_api_proxy): ovr_vr_api_proxy = ovr_vr_api_proxy.new()

if (ovr_input): ovr_input = ovr_input.new()

# Connect to the plugin signals
_connect_to_signals()

Expand Down Expand Up @@ -188,44 +191,52 @@ enum CONTROLLER_BUTTON {

# this is a function connected to the button release signal from the controller
func _on_LeftTouchController_button_pressed(button):
if (button != CONTROLLER_BUTTON.YB): return;

# examples on using the ovr api from gdscript
if (ovr_guardian_system):
print(" ovr_guardian_system.get_boundary_visible() == " + str(ovr_guardian_system.get_boundary_visible()));
#ovr_guardian_system.request_boundary_visible(true); # make the boundary always visible

# the oriented bounding box is the largest box that fits into the currently defined guardian
# the return value of this function is an array with [Transform(), Vector3()] where the Vector3
# is the scale of the box and Transform contains the position and orientation of the box.
# The height is not yet tracked by the oculus system and will be a default value.
print(" ovr_guardian_system.get_boundary_oriented_bounding_box() == " + str(ovr_guardian_system.get_boundary_oriented_bounding_box()));

if (ovr_tracking_transform):
print(" ovr_tracking_transform.get_tracking_space() == " + str(ovr_tracking_transform.get_tracking_space()));

# you can change the tracking space to control where the default floor level is and
# how recentring should behave.
#ovr_guardian_system.set_tracking_space(ovrVrApiTypes.OvrTrackingSpace.VRAPI_TRACKING_SPACE_STAGE);

if (ovr_utilities):
print(" ovr_utilities.get_ipd() == " + str(ovr_utilities.get_ipd()));

# you can access the accelerations and velocitys for the head and controllers
# that are predicted by the Oculus VrApi via these funcitons:
var controller_id = $LeftTouchController.controller_id;
print(" ovr_utilities.get_controller_linear_velocity(controller_id) == " + str(ovr_utilities.get_controller_linear_velocity(controller_id)));
print(" ovr_utilities.get_controller_linear_acceleration(controller_id) == " + str(ovr_utilities.get_controller_linear_acceleration(controller_id)));
print(" ovr_utilities.get_controller_angular_velocity(controller_id) == " + str(ovr_utilities.get_controller_angular_velocity(controller_id)));
print(" ovr_utilities.get_controller_angular_acceleration(controller_id) == " + str(ovr_utilities.get_controller_angular_acceleration(controller_id)));
if (button == CONTROLLER_BUTTON.YB):
# examples on using the ovr api from gdscript
if (ovr_guardian_system):
print(" ovr_guardian_system.get_boundary_visible() == " + str(ovr_guardian_system.get_boundary_visible()));
#ovr_guardian_system.request_boundary_visible(true); # make the boundary always visible

# the oriented bounding box is the largest box that fits into the currently defined guardian
# the return value of this function is an array with [Transform(), Vector3()] where the Vector3
# is the scale of the box and Transform contains the position and orientation of the box.
# The height is not yet tracked by the oculus system and will be a default value.
print(" ovr_guardian_system.get_boundary_oriented_bounding_box() == " + str(ovr_guardian_system.get_boundary_oriented_bounding_box()));

if (ovr_tracking_transform):
print(" ovr_tracking_transform.get_tracking_space() == " + str(ovr_tracking_transform.get_tracking_space()));

# you can change the tracking space to control where the default floor level is and
# how recentring should behave.
#ovr_guardian_system.set_tracking_space(ovrVrApiTypes.OvrTrackingSpace.VRAPI_TRACKING_SPACE_STAGE);

if (ovr_utilities):
print(" ovr_utilities.get_ipd() == " + str(ovr_utilities.get_ipd()));

# you can access the accelerations and velocitys for the head and controllers
# that are predicted by the Oculus VrApi via these funcitons:
var controller_id = $LeftTouchController.controller_id;
print(" ovr_utilities.get_controller_linear_velocity(controller_id) == " + str(ovr_utilities.get_controller_linear_velocity(controller_id)));
print(" ovr_utilities.get_controller_linear_acceleration(controller_id) == " + str(ovr_utilities.get_controller_linear_acceleration(controller_id)));
print(" ovr_utilities.get_controller_angular_velocity(controller_id) == " + str(ovr_utilities.get_controller_angular_velocity(controller_id)));
print(" ovr_utilities.get_controller_angular_acceleration(controller_id) == " + str(ovr_utilities.get_controller_angular_acceleration(controller_id)));

if (button == CONTROLLER_BUTTON.XA):
if (ovr_input):
print("Vibrating active controller...")
ovr_input.vibrate_controller(-1, 40, 0.5)


func _on_RightTouchController_button_pressed(button):
if (button != CONTROLLER_BUTTON.YB): return;

if (ovr_utilities):
# use this for fade to black for example: here we just do a color change
ovr_utilities.set_default_layer_color_scale(Color(0.5, 0.0, 1.0, 1.0));
if (button == CONTROLLER_BUTTON.YB):
if (ovr_utilities):
# use this for fade to black for example: here we just do a color change
ovr_utilities.set_default_layer_color_scale(Color(0.5, 0.0, 1.0, 1.0));

if (button == CONTROLLER_BUTTON.XA):
if (ovr_input):
print("Vibrating active controller...")
ovr_input.vibrate_controller(-1, 40, 0.5)


func _on_RightTouchController_button_release(button):
Expand Down
33 changes: 15 additions & 18 deletions plugin/src/main/cpp/api/ovr_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@
#include "api_common.h"

namespace ovrmobile {
bool vibrate_controller(OvrMobileSession *session, int controller_id, float intensity) {
return check_session_initialized<bool>(session, [&]() {
bool result = false;
const OvrMobileController *controller = session->get_ovr_mobile_controller();
if (controller) {
const OvrMobileController::ControllerState *state = controller->get_controller_state_by_id(
controller_id);
if (state &&
OvrMobileController::supports_haptic_vibration(state->remote_capabilities)) {
result = vrapi_SetHapticVibrationSimple(session->get_ovr_mobile_context(),
state->remote_capabilities.Header.DeviceID,
intensity) == ovrSuccess;
}
}
return result;
}, []() { return false; });
}
} // namespace ovrmobile
bool vibrate_controller(OvrMobileSession* session,
int controller_id,
int duration_in_ms,
float intensity) {
return check_session_initialized<bool>(session, [&]() {
bool result = false;
OvrMobileController* controller = session->get_ovr_mobile_controller();
if (controller) {
controller->vibrate_controller(controller_id, duration_in_ms, intensity);
result = true;
}
return result;
}, []() { return false; });
}
} // namespace ovrmobile
5 changes: 4 additions & 1 deletion plugin/src/main/cpp/api/ovr_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
namespace ovrmobile {
// Vibrate the controller matching the given controller ID.
// Returns true if the controller was vibrated, false otherwise.
bool vibrate_controller(OvrMobileSession *session, int controller_id, float intensity);
bool vibrate_controller(OvrMobileSession* session,
int controller_id,
int duration_in_ms,
float intensity);
} // namespace ovrmobile

#endif // OVR_INPUT_H
4 changes: 4 additions & 0 deletions plugin/src/main/cpp/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ inline bool check_bit(uint32_t in, uint32_t bits) {
return (in & bits) != 0;
}

inline double get_time_in_ms() {
return vrapi_GetTimeInSeconds() * 1000;
}

void godot_transform_from_ovrMatrix(godot_transform *p_dest, const ovrMatrix4f *p_matrix, godot_real p_world_scale);

void godot_transform_from_ovr_pose(godot_transform *dest, const ovrPosef &pose, const float world_scale);
Expand Down
2 changes: 2 additions & 0 deletions plugin/src/main/cpp/gdnative/godot_ovrmobile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "gdnative/nativescript/ovr_tracking_transform_ns.h"
#include "gdnative/nativescript/ovr_utilities_ns.h"
#include "gdnative/nativescript/ovr_hand_tracking_ns.h"
#include "gdnative/nativescript/ovr_input_ns.h"

// experimental low-level VrApi access
#include "gdnative/nativescript/ovr_vr_api_proxy_ns.h"
Expand All @@ -42,6 +43,7 @@ void GDN_EXPORT godot_ovrmobile_nativescript_init(void *handle) {
register_gdnative_utilities(handle);
register_gdnative_hand_tracking(handle);
register_gdnative_vr_api_proxy(handle);
register_gdnative_input(handle);
}

void GDN_EXPORT godot_ovrmobile_nativescript_terminate(void *handle) {
Expand Down
5 changes: 3 additions & 2 deletions plugin/src/main/cpp/gdnative/nativescript/ovr_input_ns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ GDCALLINGCONV void ovr_input_destructor(godot_object *instance, void *method_dat
GDCALLINGCONV godot_variant vibrate_controller(godot_object *instance, void *method_data, void *p_user_data, int num_args, godot_variant **args) {
CHECK_USER_DATA(
const int controller_id = godot::api->godot_variant_as_int(args[0]);
const double intensity = godot::api->godot_variant_as_real(args[1]);
godot::api->godot_variant_new_bool(&ret, ovrmobile::vibrate_controller(ovr_mobile_session, controller_id, intensity));
const int duration_in_ms = godot::api->godot_variant_as_int(args[1]);
const double intensity = godot::api->godot_variant_as_real(args[2]);
godot::api->godot_variant_new_bool(&ret, ovrmobile::vibrate_controller(ovr_mobile_session, controller_id, duration_in_ms, intensity));

)
}
12 changes: 8 additions & 4 deletions plugin/src/main/cpp/jni/ovr_input_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@
extern "C" {

JNIEXPORT void JNICALL
JNI_METHOD(nativeVibrateController)(JNIEnv *env, jclass clazz, jint controller_id,
jfloat intensity) {
ovrmobile::vibrate_controller(get_session(), controller_id, intensity);
JNI_METHOD(vibrateController)(JNIEnv* env,
jclass,
jobject,
jint controller_id,
jint duration_in_ms,
jfloat intensity) {
ovrmobile::vibrate_controller(get_session(), controller_id, duration_in_ms, intensity);
}

};
};
106 changes: 74 additions & 32 deletions plugin/src/main/cpp/ovr_mobile_controller.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Created by Fredia Huya-Kouadio.
* Created by Fredia Huya-Kouadio.
*/

#include "ovr_mobile_controller.h"
Expand All @@ -15,8 +15,6 @@ const int kAnalogGripTriggerAxis = 3;
const float kIndexTriggerPressedThreshold = 0.6f;
const float kGripTriggerPressedThreshold = 0.6f;

const int kInvalidGodotControllerId = 0;

const char *kUnsupportedController = "Unsupported Controller";
const char *kGearVRController = "Gear VR Controller";
const char *kOculusGoController = "Oculus Go Controller";
Expand All @@ -25,6 +23,11 @@ const char *kOculusTouchRightController = "Oculus Touch Right Controller";
const char *kOculusTrackedLeftHand = "Oculus Tracked Left Hand";
const char *kOculusTrackedRightHand = "Oculus Tracked Right Hand";

// We set the duration for the controller rumble to 1 ms so it's only active for one frame.
const int kControllerRumbleDurationInMs = 1;

const double kEspilonTimeInMs = 5;

} // namespace

OvrMobileController::OvrMobileController() = default;
Expand All @@ -45,17 +48,41 @@ void OvrMobileController::update_controller_vibration(ovrMobile *ovr, Controller
return;
}

if (controller_state.capability_header.Type == ovrControllerType_TrackedRemote) {
if (!supports_haptic_vibration(controller_state.remote_capabilities)) {
return;
}

// Get the vibration intensity.
const float vibration = godot::arvr_api->godot_arvr_get_controller_rumble(controller_state.godot_controller_id);
vrapi_SetHapticVibrationSimple(ovr, controller_state.remote_capabilities.Header.DeviceID, vibration);
} else {
// unsupprted controller
}
if (!(controller_state.capability_header.Type == ovrControllerType_TrackedRemote)
|| !supports_haptic_vibration(controller_state.remote_capabilities)) {
// unsupported controller
return;
}

// Get the controller rumble intensity. This will override previous controller vibration
// requests as this takes precedence.
const float intensity = godot::arvr_api->godot_arvr_get_controller_rumble(controller_state.godot_controller_id);
vibrate_controller(controller_state.godot_controller_id, kControllerRumbleDurationInMs, intensity);

// Process controller vibrations.
ControllerVibration& vibration = controllers_vibrations[controller_state.godot_controller_id];
double current_time_in_ms = get_time_in_ms();
if (vibration.is_vibrating
&& (vibration.end_time_in_ms < (current_time_in_ms + kEspilonTimeInMs))) {
// Stop vibrating
vrapi_SetHapticVibrationSimple(ovr,
controller_state.remote_capabilities.Header.DeviceID,
0.0f);

// Reset the vibration parameters
vibration.end_time_in_ms = 0;
vibration.is_vibrating = false;
vibration.intensity = 0;
controllers_vibrations.erase(controller_state.godot_controller_id);
}

if (vibration.end_time_in_ms > 0) {
vrapi_SetHapticVibrationSimple(ovr,
controller_state.remote_capabilities.Header.DeviceID,
vibration.intensity);

vibration.is_vibrating = true;
}
}

void OvrMobileController::update_controller_input_state(ovrMobile *ovr,ControllerState& controller_state) {
Expand Down Expand Up @@ -214,9 +241,10 @@ void OvrMobileController::update_controller_tracking_state_hand(ovrMobile *ovr,


void OvrMobileController::update_controllers_connection_state(ovrMobile *ovr, ovrJava *java) {
// Reset the controllers connected state.
// Reset the controllers connected and active state.
for (auto &controller : controllers) {
controller.connected = false;
controller.primary = false;
}

// Check controller(s) connection state.
Expand All @@ -237,7 +265,7 @@ void OvrMobileController::update_controllers_connection_state(ovrMobile *ovr, ov
// We're handling a Gear VR or Oculus Go controller. Let's query the user dominant hand to find out where to
// assign the controller.
auto dominant_hand = static_cast<ovrHandedness>(vrapi_GetSystemPropertyInt(java, VRAPI_SYS_PROP_DOMINANT_HAND));
handedness = get_controller_handedness(dominant_hand);
handedness = dominant_hand == VRAPI_HAND_LEFT ? LEFT_HAND : RIGHT_HAND;
}

// this check forces a reconnect below because the type of the controller changed
Expand All @@ -257,7 +285,7 @@ void OvrMobileController::update_controllers_connection_state(ovrMobile *ovr, ov
}

ControllerHand handedness = get_controller_handedness(tracked_hand_capabilities);

// this check forces a reconnect below because the type of the controller changed
if (controllers[handedness].capability_header.Type != ovrControllerType_Hand && controllers[handedness].godot_controller_id != kInvalidGodotControllerId) {
godot::arvr_api->godot_arvr_remove_controller(controllers[handedness].godot_controller_id);
Expand All @@ -271,24 +299,38 @@ void OvrMobileController::update_controllers_connection_state(ovrMobile *ovr, ov
}
}

// Notify Godot of the updated connection states.
// Get the active input device id.
int active_input_device_id = -1;
vrapi_GetPropertyInt(java, VRAPI_ACTIVE_INPUT_DEVICE_ID, &active_input_device_id);

for (int hand = 0; hand < MAX_HANDS; hand++) {
ControllerState *controller = &controllers[hand];

if (controller->connected && controller->godot_controller_id == kInvalidGodotControllerId) {
// Register the controller with Godot.
if (controller->capability_header.Type == ovrControllerType_TrackedRemote) {
controller->godot_controller_id = godot::arvr_api->godot_arvr_add_controller(
const_cast<char *>(get_controller_model_name(*controller)), get_godot_hand(static_cast<ControllerHand>(hand)),
has_orientation_tracking(controller->remote_capabilities),
has_position_tracking(controller->remote_capabilities));
} else if (controller->capability_header.Type == ovrControllerType_Hand) {
controller->godot_controller_id = godot::arvr_api->godot_arvr_add_controller(
const_cast<char *>(get_controller_model_name(*controller)), get_godot_hand(static_cast<ControllerHand>(hand)),
true, true);
}
ALOGV("Updated Controller '%s' (Godot id %d; Oculus id %d)", get_controller_model_name(*controller), controller->godot_controller_id, hand);
} else if (!controller->connected && controller->godot_controller_id != kInvalidGodotControllerId) {
// Notify Godot of the updated connection states.
if (controller->connected) {
controller->primary = controller->capability_header.DeviceID == active_input_device_id;

if (controller->godot_controller_id == kInvalidGodotControllerId) {
// Register the controller with Godot.
if (controller->capability_header.Type == ovrControllerType_TrackedRemote) {
controller->godot_controller_id = godot::arvr_api->godot_arvr_add_controller(
const_cast<char*>(get_controller_model_name(*controller)),
get_godot_hand(static_cast<ControllerHand>(hand)),
has_orientation_tracking(controller->remote_capabilities),
has_position_tracking(controller->remote_capabilities));
} else if (controller->capability_header.Type == ovrControllerType_Hand) {
controller->godot_controller_id = godot::arvr_api->godot_arvr_add_controller(
const_cast<char*>(get_controller_model_name(*controller)),
get_godot_hand(static_cast<ControllerHand>(hand)),
true,
true);
}
ALOGV("Updated Controller '%s' (Godot id %d; Oculus id %d)",
get_controller_model_name(*controller),
controller->godot_controller_id,
hand);
}
} else if (controller->godot_controller_id != kInvalidGodotControllerId) {
// Unregister the controller from Godot.
ALOGV("Unregistered Controller '%s' (Godot id %d; Oculus id %d)", get_controller_model_name(*controller), controller->godot_controller_id, hand);
godot::arvr_api->godot_arvr_remove_controller(controller->godot_controller_id);
Expand Down
Loading

0 comments on commit d279165

Please sign in to comment.