Skip to content

Commit

Permalink
Adding Chrome-side WebVR interface and Cardboard implementation
Browse files Browse the repository at this point in the history
WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html

For the Blink half of this CL, see https://codereview.chromium.org/848053002

Provides an Android-only, Cardboard-SDK-based implementation of the initial WebVR interface, as well as the basic plumbing required to get data from Javascript to the right place in the browser process and back.

BUG=389343

Review URL: https://codereview.chromium.org/829803003

Cr-Commit-Position: refs/heads/master@{#335062}
  • Loading branch information
toji authored and Commit bot committed Jun 18, 2015
1 parent 5d1fef6 commit 4ab52de
Show file tree
Hide file tree
Showing 47 changed files with 1,514 additions and 2 deletions.
12 changes: 12 additions & 0 deletions build/common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,14 @@
}, {
'pkg-config': 'pkg-config'
}],

# Enable WebVR support by default on Android
# Still requires command line flag to access API
['OS=="android"', {
'enable_webvr%': 1,
}, {
'enable_webvr%': 0,
}],
],

# Setting this to '0' will cause V8's startup snapshot to be
Expand Down Expand Up @@ -1232,6 +1240,7 @@
'mac_views_browser%': '<(mac_views_browser)',
'android_app_version_name%': '<(android_app_version_name)',
'android_app_version_code%': '<(android_app_version_code)',
'enable_webvr%': '<(enable_webvr)',

# Use system protobuf instead of bundled one.
'use_system_protobuf%': 0,
Expand Down Expand Up @@ -3030,6 +3039,9 @@
['v8_use_external_startup_data==1', {
'defines': ['V8_USE_EXTERNAL_STARTUP_DATA'],
}],
['enable_webvr==1', {
'defines': ['ENABLE_WEBVR'],
}],

# SAFE_BROWSING_SERVICE - browser manages a safe-browsing service.
# SAFE_BROWSING_DB_LOCAL - service manages a local database.
Expand Down
3 changes: 3 additions & 0 deletions build/config/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ config("feature_flags") {
if (enable_media_router) {
defines += [ "ENABLE_MEDIA_ROUTER=1" ]
}
if (enable_webvr) {
defines += [ "ENABLE_WEBVR" ]
}
}

# Debug/release ----------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions build/config/features.gni
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,7 @@ enable_hangout_services_extension = false

# Whether to back up data before sync.
enable_pre_sync_backup = is_win || is_mac || (is_linux && !is_chromeos)

# Enable WebVR support by default on Android
# Still requires command line flag to access API
enable_webvr = is_android
6 changes: 6 additions & 0 deletions chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -5950,6 +5950,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_ENABLE_WEBRTC_STUN_ORIGIN_DESCRIPTION" desc="Description of chrome:flags option to turn on Origin header for WebRTC STUN messages">
When enabled, Stun messages generated by WebRTC will contain the Origin header.
</message>
<message name="IDS_FLAGS_ENABLE_WEBVR_NAME" desc="Name of the 'Enable WebVR' flag.">
Enable WebVR
</message>
<message name="IDS_FLAGS_ENABLE_WEBVR_DESCRIPTION" desc="Description for the flag to enable WebVR APIs.">
Enabling this option allows web applications to access experimental Virtual Reality APIs.
</message>
<if expr="is_android">
<message name="IDS_FLAGS_DISABLE_WEBAUDIO_NAME" desc="Name of the 'Disable WebAudio' lab.">
Disable WebAudio
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,13 @@ const Experiment kExperiments[] = {
kOsMac,
ENABLE_DISABLE_VALUE_TYPE(switches::kEnableMacViewsNativeAppWindows,
switches::kDisableMacViewsNativeAppWindows)},
#endif
#if defined(ENABLE_WEBVR)
{"enable-webvr",
IDS_FLAGS_ENABLE_WEBVR_NAME,
IDS_FLAGS_ENABLE_WEBVR_DESCRIPTION,
kOsAll,
SINGLE_VALUE_TYPE(switches::kEnableWebVR)},
#endif
// NOTE: Adding new command-line switches requires adding corresponding
// entries to enum "LoginCustomFlags" in histograms.xml. See note in
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/chromeos/login/chrome_restart_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ std::string DeriveCommandLine(const GURL& start_url,
::switches::kVModule,
::switches::kEnableWebGLDraftExtensions,
::switches::kEnableWebGLImageChromium,
::switches::kEnableWebVR,
#if defined(ENABLE_WEBRTC)
::switches::kDisableWebRtcHWDecoding,
::switches::kDisableWebRtcHWEncoding,
Expand Down
19 changes: 19 additions & 0 deletions content/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -479,4 +479,23 @@ source_set("browser") {
if (enable_media_mojo_renderer) {
deps += [ "//media/mojo/services:renderer_service" ]
}

if (enable_webvr) {
sources += [
"vr/vr_device.cc",
"vr/vr_device.h",
"vr/vr_device_manager.cc",
"vr/vr_device_manager.h",
"vr/vr_device_provider.h",
]
}

if (enable_webvr && is_android) {
sources += [
"vr/android/cardboard/cardboard_vr_device.cc",
"vr/android/cardboard/cardboard_vr_device.h",
"vr/android/cardboard/cardboard_vr_device_provider.cc",
"vr/android/cardboard/cardboard_vr_device_provider.h",
]
}
}
1 change: 1 addition & 0 deletions content/browser/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ include_rules = [
"+third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h",
"+third_party/WebKit/public/platform/modules/notifications/WebNotificationPermission.h",
"+third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h",
"+third_party/WebKit/public/platform/modules/vr/WebVR.h",
"+third_party/WebKit/public/web/mac/WebScrollbarTheme.h",
"+third_party/WebKit/public/web/WebAXEnums.h",
"+third_party/WebKit/public/web/WebCompositionUnderline.h",
Expand Down
5 changes: 5 additions & 0 deletions content/browser/android/browser_jni_registrar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "content/browser/screen_orientation/screen_orientation_delegate_android.h"
#include "content/browser/speech/speech_recognizer_impl_android.h"
#include "content/browser/time_zone_monitor_android.h"
#include "content/browser/vr/android/cardboard/cardboard_vr_device.h"
#include "content/browser/web_contents/web_contents_android.h"
#include "mojo/android/system/core_impl.h"

Expand All @@ -52,6 +53,10 @@ base::android::RegistrationMethod kContentRegisteredMethods[] = {
{"BrowserAccessibilityManager",
content::RegisterBrowserAccessibilityManager},
{"BrowserStartupController", content::RegisterBrowserStartupController},
#if defined(ENABLE_WEBVR)
{"CardboardVRDevice",
content::CardboardVRDevice::RegisterCardboardVRDevice},
#endif
{"ChildProcessLauncher", content::RegisterChildProcessLauncher},
{"ContentReadbackHandler",
content::ContentReadbackHandler::RegisterContentReadbackHandler},
Expand Down
14 changes: 14 additions & 0 deletions content/browser/frame_host/render_frame_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
#include "media/mojo/services/mojo_renderer_service.h"
#endif

#if defined(ENABLE_WEBVR)
#include "content/browser/vr/vr_device_manager.h"
#endif

using base::TimeDelta;

namespace content {
Expand Down Expand Up @@ -1579,6 +1583,16 @@ void RenderFrameHostImpl::RegisterMojoServices() {
GetServiceRegistry()->AddService<mojo::Shell>(base::Bind(
&FrameMojoShell::BindRequest, base::Unretained(frame_mojo_shell_.get())));

#if defined(ENABLE_WEBVR)
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();

if (browser_command_line.HasSwitch(switches::kEnableWebVR)) {
GetServiceRegistry()->AddService<VRService>(
base::Bind(&VRDeviceManager::BindRequest));
}
#endif

GetContentClient()->browser()->OverrideRenderFrameMojoServices(
GetServiceRegistry(), this);
}
Expand Down
1 change: 1 addition & 0 deletions content/browser/renderer_host/render_process_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kEnableWebBluetooth,
switches::kEnableWebGLDraftExtensions,
switches::kEnableWebGLImageChromium,
switches::kEnableWebVR,
switches::kExplicitlyAllowedPorts,
switches::kForceDeviceScaleFactor,
switches::kForceDisplayList2dCanvas,
Expand Down
2 changes: 2 additions & 0 deletions content/browser/vr/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bajones@chromium.org
kbr@chromium.org
188 changes: 188 additions & 0 deletions content/browser/vr/android/cardboard/cardboard_vr_device.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/vr/android/cardboard/cardboard_vr_device.h"

#include <math.h>
#include <algorithm>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "jni/CardboardVRDevice_jni.h"

using base::android::AttachCurrentThread;

namespace content {

namespace {

// Source:
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
VRVector4Ptr MatrixToOrientationQuat(const float m[16]) {
VRVector4Ptr out = VRVector4::New();
float trace = m[0] + m[5] + m[10];
float root;
if (trace > 0.0f) {
root = sqrtf(1.0f + trace) * 2.0f;
out->x = (m[9] - m[6]) / root;
out->y = (m[2] - m[8]) / root;
out->z = (m[4] - m[1]) / root;
out->w = 0.25f * root;
} else if ((m[0] > m[5]) && (m[0] > m[10])) {
root = sqrtf(1.0f + m[0] - m[5] - m[10]) * 2.0f;
out->x = 0.25f * root;
out->y = (m[1] + m[4]) / root;
out->z = (m[2] + m[8]) / root;
out->w = (m[9] - m[6]) / root;
} else if (m[5] > m[10]) {
root = sqrtf(1.0f + m[5] - m[0] - m[10]) * 2.0f;
out->x = (m[1] + m[4]) / root;
out->y = 0.25f * root;
out->z = (m[6] + m[9]) / root;
out->w = (m[2] - m[8]) / root;
} else {
root = sqrtf(1.0f + m[10] - m[0] - m[5]) * 2.0f;
out->x = (m[2] + m[8]) / root;
out->y = (m[6] + m[9]) / root;
out->z = 0.25f * root;
out->w = (m[4] - m[1]) / root;
}

return out.Pass();
}

} // namespace

bool CardboardVRDevice::RegisterCardboardVRDevice(JNIEnv* env) {
return RegisterNativesImpl(env);
}

CardboardVRDevice::CardboardVRDevice(VRDeviceProvider* provider)
: VRDevice(provider), frame_index_(0) {
j_cardboard_device_.Reset(Java_CardboardVRDevice_create(
AttachCurrentThread(), base::android::GetApplicationContext()));
}

CardboardVRDevice::~CardboardVRDevice() {
Java_CardboardVRDevice_stopTracking(AttachCurrentThread(),
j_cardboard_device_.obj());
}

VRDeviceInfoPtr CardboardVRDevice::GetVRDevice() {
VRDeviceInfoPtr device = VRDeviceInfo::New();

JNIEnv* env = AttachCurrentThread();

ScopedJavaLocalRef<jstring> j_device_name =
Java_CardboardVRDevice_getDeviceName(env, j_cardboard_device_.obj());
device->deviceName =
base::android::ConvertJavaStringToUTF8(env, j_device_name.obj());

ScopedJavaLocalRef<jfloatArray> j_fov(env, env->NewFloatArray(4));
Java_CardboardVRDevice_getFieldOfView(env, j_cardboard_device_.obj(),
j_fov.obj());

std::vector<float> fov;
base::android::JavaFloatArrayToFloatVector(env, j_fov.obj(), &fov);

device->hmdInfo = VRHMDInfo::New();
VRHMDInfoPtr& hmdInfo = device->hmdInfo;

hmdInfo->leftEye = VREyeParameters::New();
hmdInfo->rightEye = VREyeParameters::New();
VREyeParametersPtr& leftEye = hmdInfo->leftEye;
VREyeParametersPtr& rightEye = hmdInfo->rightEye;

leftEye->recommendedFieldOfView = VRFieldOfView::New();
leftEye->recommendedFieldOfView->upDegrees = fov[0];
leftEye->recommendedFieldOfView->downDegrees = fov[1];
leftEye->recommendedFieldOfView->leftDegrees = fov[2];
leftEye->recommendedFieldOfView->rightDegrees = fov[3];

// Cardboard devices always assume a mirrored FOV, so this is just the left
// eye FOV with the left and right degrees swapped.
rightEye->recommendedFieldOfView = VRFieldOfView::New();
rightEye->recommendedFieldOfView->upDegrees = fov[0];
rightEye->recommendedFieldOfView->downDegrees = fov[1];
rightEye->recommendedFieldOfView->leftDegrees = fov[3];
rightEye->recommendedFieldOfView->rightDegrees = fov[2];

// Cardboard does not support configurable FOV.
leftEye->maximumFieldOfView = leftEye->recommendedFieldOfView.Clone();
rightEye->maximumFieldOfView = rightEye->recommendedFieldOfView.Clone();
leftEye->minimumFieldOfView = leftEye->recommendedFieldOfView.Clone();
rightEye->minimumFieldOfView = rightEye->recommendedFieldOfView.Clone();

float ipd = Java_CardboardVRDevice_getIpd(env, j_cardboard_device_.obj());

leftEye->eyeTranslation = VRVector3::New();
leftEye->eyeTranslation->x = ipd * -0.5f;
leftEye->eyeTranslation->y = 0.0f;
leftEye->eyeTranslation->z = 0.0f;

rightEye->eyeTranslation = VRVector3::New();
rightEye->eyeTranslation->x = ipd * 0.5f;
rightEye->eyeTranslation->y = 0.0f;
rightEye->eyeTranslation->z = 0.0f;

ScopedJavaLocalRef<jintArray> j_screen_size(env, env->NewIntArray(2));
Java_CardboardVRDevice_getScreenSize(env, j_cardboard_device_.obj(),
j_screen_size.obj());

std::vector<int> screen_size;
base::android::JavaIntArrayToIntVector(env, j_screen_size.obj(),
&screen_size);

leftEye->renderRect = VRRect::New();
leftEye->renderRect->x = 0;
leftEye->renderRect->y = 0;
leftEye->renderRect->width = screen_size[0] / 2.0;
leftEye->renderRect->height = screen_size[1];

rightEye->renderRect = VRRect::New();
rightEye->renderRect->x = screen_size[0] / 2.0;
rightEye->renderRect->y = 0;
rightEye->renderRect->width = screen_size[0] / 2.0;
rightEye->renderRect->height = screen_size[1];

return device.Pass();
}

VRSensorStatePtr CardboardVRDevice::GetSensorState() {
VRSensorStatePtr state = VRSensorState::New();

state->timestamp = base::Time::Now().ToJsTime();
state->frameIndex = frame_index_;

JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jfloatArray> j_head_matrix(env, env->NewFloatArray(16));
Java_CardboardVRDevice_getSensorState(env, j_cardboard_device_.obj(),
j_head_matrix.obj());

std::vector<float> head_matrix;
base::android::JavaFloatArrayToFloatVector(env, j_head_matrix.obj(),
&head_matrix);

state->orientation = MatrixToOrientationQuat(&head_matrix[0]);

state->position = VRVector3::New();
state->position->x = -head_matrix[12];
state->position->y = head_matrix[13];
state->position->z = head_matrix[14];

frame_index_++;

return state.Pass();
}

void CardboardVRDevice::ResetSensor() {
Java_CardboardVRDevice_resetSensor(AttachCurrentThread(),
j_cardboard_device_.obj());
}

} // namespace content
Loading

0 comments on commit 4ab52de

Please sign in to comment.