mirror of
https://github.com/servo/servo.git
synced 2025-06-17 04:44:28 +00:00
419 lines
15 KiB
HTML
419 lines
15 KiB
HTML
<!doctype html>
|
|
<!--
|
|
Copyright 2016 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.
|
|
-->
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
|
|
<!--Chrome origin trial header-->
|
|
|
|
<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-13" content="AtpEVUJTjLpU/IMdKA/u8TRWqVUKfA6aJQsonwi01IPxqA16zX7L4BMa9E4g4DdJW80v3N6jqde4pXeqd2GYCg4AAABJeyJvcmlnaW4iOiJodHRwczovL3dlYnZyLmluZm86NDQzIiwiZmVhdHVyZSI6IldlYlZSIiwiZXhwaXJ5IjoxNDkyMTAzODUyfQ==">
|
|
<title>XX - VR Controllers</title>
|
|
|
|
<!--
|
|
This sample demonstrates how to handle gamepads with 6DoF support, such as
|
|
the Vive controllers or Oculus touch. PLEASE NOTE: The additions to the
|
|
gamepad API used here are not yet part of the standard, and subject to
|
|
change at any time!
|
|
-->
|
|
|
|
<style>
|
|
#webgl-canvas {
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
left: 0;
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 0;
|
|
width: 100%;
|
|
}
|
|
</style>
|
|
|
|
<!-- This entire block in only to facilitate dynamically enabling and
|
|
disabling the WebVR polyfill, and is not necessary for most WebVR apps.
|
|
If you want to use the polyfill in your app, just include the js file and
|
|
everything will work the way you want it to by default. -->
|
|
<script>
|
|
var WebVRConfig = {
|
|
// Prevents the polyfill from initializing automatically.
|
|
DEFER_INITIALIZATION: true,
|
|
// Ensures the polyfill is always active when initialized, even if the
|
|
// native API is available. This is probably NOT what most pages want.
|
|
POLYFILL_MODE: "ALWAYS",
|
|
// Polyfill optimizations
|
|
DIRTY_SUBMIT_FRAME_BINDINGS: true,
|
|
BUFFER_SCALE: 0.75,
|
|
};
|
|
</script>
|
|
<script src="js/third-party/webvr-polyfill.js"></script>
|
|
<script src="js/third-party/wglu/wglu-url.js"></script>
|
|
<script>
|
|
// Dynamically turn the polyfill on if requested by the query args.
|
|
if (WGLUUrl.getBool('polyfill', false)) {
|
|
InitializeWebVRPolyfill();
|
|
} else {
|
|
// Shim for migration from older version of WebVR. Shouldn't be necessary for very long.
|
|
InitializeSpecShim();
|
|
}
|
|
</script>
|
|
<!-- End sample polyfill enabling logic -->
|
|
|
|
<script src="js/third-party/gl-matrix-min.js"></script>
|
|
|
|
<script src="js/third-party/wglu/wglu-debug-geometry.js"></script>
|
|
<script src="js/third-party/wglu/wglu-program.js"></script>
|
|
<script src="js/third-party/wglu/wglu-stats.js"></script>
|
|
<script src="js/third-party/wglu/wglu-texture.js"></script>
|
|
|
|
<script src="js/vr-cube-island.js"></script>
|
|
<script src="js/vr-samples-util.js"></script>
|
|
</head>
|
|
<body>
|
|
<canvas id="webgl-canvas"></canvas>
|
|
<script>
|
|
/* global mat4, vec3, VRCubeIsland, WGLUDebugGeometry, WGLUStats, WGLUTextureLoader, VRSamplesUtil */
|
|
(function () {
|
|
"use strict";
|
|
|
|
var PLAYER_HEIGHT = 1.65;
|
|
|
|
var vrDisplay = null;
|
|
var frameData = null;
|
|
var projectionMat = mat4.create();
|
|
var viewMat = mat4.create();
|
|
var poseMat = mat4.create();
|
|
var gamepadMat = mat4.create();
|
|
var gamepadMat2 = mat4.create();
|
|
var gamepadColor = vec4.create();
|
|
var standingPosition = vec3.create();
|
|
var vrPresentButton = null;
|
|
var orientation = [0, 0, 0, 1];
|
|
var position = [0, 0, 0];
|
|
|
|
|
|
window.addEventListener("gamepadconnected", function(ev) {
|
|
var gamepad = ev.gamepad;
|
|
console.log("Gamepad connected", gamepad.index, gamepad.id, gamepad.displayId);
|
|
});
|
|
|
|
window.addEventListener("gamepaddisconnected", function(ev) {
|
|
var gamepad = ev.gamepad;
|
|
console.log("Gamepad disconnected", gamepad.index, gamepad.id, gamepad.displayId);
|
|
});
|
|
|
|
// ===================================================
|
|
// WebGL scene setup. This code is not WebVR specific.
|
|
// ===================================================
|
|
|
|
// WebGL setup.
|
|
var webglCanvas = document.getElementById("webgl-canvas");
|
|
var gl = null;
|
|
var cubeIsland = null;
|
|
var stats = null;
|
|
var debugGeom = null;
|
|
|
|
function initWebGL (preserveDrawingBuffer) {
|
|
var glAttribs = {
|
|
alpha: false,
|
|
antialias: false,
|
|
preserveDrawingBuffer: false
|
|
};
|
|
gl = webglCanvas.getContext("webgl", glAttribs);
|
|
if (!gl) {
|
|
gl = webglCanvas.getContext("experimental-webgl", glAttribs);
|
|
if (!gl) {
|
|
VRSamplesUtil.addError("Your browser does not support WebGL.");
|
|
return;
|
|
}
|
|
}
|
|
//gl.clearColor(0.1, 0.2, 0.3, 1.0);
|
|
gl.clearColor(0.0, 119.0/255.0, 51.0/255.0, 1.0);
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.enable(gl.CULL_FACE);
|
|
|
|
var textureLoader = new WGLUTextureLoader(gl);
|
|
var texture = textureLoader.loadTexture("media/textures/cube-sea.png");
|
|
|
|
cubeIsland = new VRCubeIsland(gl, texture, 2, 2);
|
|
|
|
stats = new WGLUStats(gl);
|
|
debugGeom = new WGLUDebugGeometry(gl);
|
|
|
|
// Wait until we have a WebGL context to resize and start rendering.
|
|
window.addEventListener("resize", onResize, false);
|
|
onResize();
|
|
window.requestAnimationFrame(onAnimationFrame);
|
|
}
|
|
|
|
// ================================
|
|
// WebVR-specific code begins here.
|
|
// ================================
|
|
|
|
function onVRRequestPresent () {
|
|
vrDisplay.requestPresent([{ source: webglCanvas }]).then(function () {
|
|
}, function (err) {
|
|
var errMsg = "requestPresent failed.";
|
|
if (err && err.message) {
|
|
errMsg += "<br/>" + err.message
|
|
}
|
|
VRSamplesUtil.addError(errMsg, 2000);
|
|
});
|
|
}
|
|
|
|
function onVRExitPresent () {
|
|
if (!vrDisplay.isPresenting)
|
|
return;
|
|
|
|
vrDisplay.exitPresent().then(function () {
|
|
}, function () {
|
|
VRSamplesUtil.addError("exitPresent failed.", 2000);
|
|
});
|
|
}
|
|
|
|
function onVRPresentChange () {
|
|
onResize();
|
|
|
|
if (vrDisplay.isPresenting) {
|
|
if (vrDisplay.capabilities.hasExternalDisplay) {
|
|
VRSamplesUtil.removeButton(vrPresentButton);
|
|
vrPresentButton = VRSamplesUtil.addButton("Exit VR", "E", "media/icons/cardboard64.png", onVRExitPresent);
|
|
}
|
|
} else {
|
|
if (vrDisplay.capabilities.hasExternalDisplay) {
|
|
VRSamplesUtil.removeButton(vrPresentButton);
|
|
vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (navigator.getVRDisplays) {
|
|
frameData = new VRFrameData();
|
|
navigator.getVRDisplays().then(function (displays) {
|
|
if (displays.length > 0) {
|
|
vrDisplay = displays[displays.length - 1];
|
|
vrDisplay.depthNear = 0.1;
|
|
vrDisplay.depthFar = 1024.0;
|
|
|
|
initWebGL(true);
|
|
|
|
if (vrDisplay.stageParameters &&
|
|
vrDisplay.stageParameters.sizeX > 0 &&
|
|
vrDisplay.stageParameters.sizeZ > 0) {
|
|
cubeIsland.resize(vrDisplay.stageParameters.sizeX, vrDisplay.stageParameters.sizeZ);
|
|
}
|
|
|
|
VRSamplesUtil.addButton("Reset Pose", "R", null, function () { vrDisplay.resetPose(); });
|
|
|
|
if (vrDisplay.capabilities.canPresent)
|
|
vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
|
|
|
|
window.addEventListener('vrdisplaypresentchange', onVRPresentChange, false);
|
|
window.addEventListener('vrdisplayactivate', onVRRequestPresent, false);
|
|
window.addEventListener('vrdisplaydeactivate', onVRExitPresent, false);
|
|
console.log(navigator.getGamepads());
|
|
setTimeout(function(){
|
|
onVRRequestPresent();
|
|
}, 5);
|
|
|
|
} else {
|
|
initWebGL(false);
|
|
VRSamplesUtil.addInfo("WebVR supported, but no VRDisplays found.", 3000);
|
|
}
|
|
});
|
|
} else if (navigator.getVRDevices) {
|
|
initWebGL(false);
|
|
VRSamplesUtil.addError("Your browser supports WebVR but not the latest version. See <a href='http://webvr.info'>webvr.info</a> for more info.");
|
|
} else {
|
|
initWebGL(false);
|
|
VRSamplesUtil.addError("Your browser does not support WebVR. See <a href='http://webvr.info'>webvr.info</a> for assistance.");
|
|
}
|
|
|
|
function onResize () {
|
|
if (vrDisplay && vrDisplay.isPresenting) {
|
|
var leftEye = vrDisplay.getEyeParameters("left");
|
|
var rightEye = vrDisplay.getEyeParameters("right");
|
|
|
|
webglCanvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) * 2;
|
|
webglCanvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
|
|
} else {
|
|
webglCanvas.width = window.innerWidth * 2.0 * window.devicePixelRatio;
|
|
webglCanvas.height = window.innerHeight * 2.0 * window.devicePixelRatio;
|
|
}
|
|
}
|
|
|
|
function onClick() {
|
|
//onVRRequestPresent();
|
|
}
|
|
webglCanvas.addEventListener("click", onClick, false);
|
|
|
|
function getStandingViewMatrix (out, view) {
|
|
if (vrDisplay.stageParameters) {
|
|
mat4.invert(out, vrDisplay.stageParameters.sittingToStandingTransform);
|
|
mat4.multiply(out, view, out);
|
|
} else {
|
|
mat4.identity(out);
|
|
mat4.translate(out, out, [0, PLAYER_HEIGHT, 0]);
|
|
mat4.invert(out, out);
|
|
mat4.multiply(out, view, out);
|
|
}
|
|
}
|
|
|
|
function getPoseMatrix (out, pose, isGamepad) {
|
|
orientation = pose.orientation;
|
|
position = pose.position;
|
|
if (!orientation) { orientation = [0, 0, 0, 1]; }
|
|
if (!position) {
|
|
// If this is a gamepad without a pose set it out in front of us so
|
|
// we can see it.
|
|
position = isGamepad ? [0.1, -0.1, -0.5] : [0, 0, 0];
|
|
}
|
|
|
|
|
|
if (vrDisplay.stageParameters) {
|
|
mat4.fromRotationTranslation(out, orientation, position);
|
|
mat4.multiply(out, vrDisplay.stageParameters.sittingToStandingTransform, out);
|
|
} else {
|
|
vec3.add(standingPosition, position, [0, PLAYER_HEIGHT, 0]);
|
|
mat4.fromRotationTranslation(out, orientation, standingPosition);
|
|
}
|
|
}
|
|
|
|
function renderSceneView (projection, view, gamepads) {
|
|
cubeIsland.render(projection, view, stats);
|
|
|
|
debugGeom.bind(projection, view);
|
|
|
|
// Render every gamepad with a pose we found
|
|
for (var i = 0; i < gamepads.length; ++i) {
|
|
var gamepad = gamepads[i];
|
|
|
|
// Because this sample is done in standing space we need to apply
|
|
// the same transformation to the gamepad pose as we did the
|
|
// VRDisplay's pose.
|
|
getPoseMatrix(gamepadMat, gamepad.pose, true);
|
|
|
|
// Loop through all the gamepad's axes and scale the gamepad geometry
|
|
// by their value.
|
|
var scale = [0.1, 0.1, 0.1];
|
|
for (var j = 0; j < gamepad.axes.length; ++j) {
|
|
switch (j%3) {
|
|
case 0:
|
|
scale[0] *= 1.0 + gamepad.axes[j];
|
|
break;
|
|
case 1:
|
|
scale[1] *= 1.0 + gamepad.axes[j];
|
|
break;
|
|
case 2:
|
|
scale[2] *= 1.0 + gamepad.axes[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Scaled down to from 1 meter to be something closer to the size of
|
|
// a hand.
|
|
mat4.scale(gamepadMat, gamepadMat, scale);
|
|
|
|
// Rotate -90 deg so the point of the cone faces "forward"
|
|
mat4.rotateX(gamepadMat, gamepadMat, -Math.PI * 0.5);
|
|
|
|
// Show the gamepad's cube as red if any buttons are pressed, blue
|
|
// otherwise.
|
|
vec4.set(gamepadColor, 0, 0, 1, 1);
|
|
var buttons = gamepad.buttons;
|
|
for (var j = 0; j < buttons.length; ++j) {
|
|
if (buttons[j].pressed) {
|
|
vec4.set(gamepadColor, buttons[j].value || 1.0, 0, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
debugGeom.drawConeWithMatrix(gamepadMat, gamepadColor);
|
|
|
|
// Draw a "handle" for the gamepad
|
|
mat4.identity(gamepadMat2);
|
|
mat4.translate(gamepadMat2, gamepadMat2, [0, -0.5, -0.3]);
|
|
mat4.rotateX(gamepadMat2, gamepadMat2, -Math.PI * 0.2);
|
|
mat4.scale(gamepadMat2, gamepadMat2, [0.25, 0.25, 0.5]);
|
|
|
|
mat4.multiply(gamepadMat, gamepadMat, gamepadMat2);
|
|
|
|
debugGeom.drawBoxWithMatrix(gamepadMat, gamepadColor);
|
|
}
|
|
}
|
|
|
|
function onAnimationFrame (t) {
|
|
stats.begin();
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
if (vrDisplay) {
|
|
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
|
|
|
vrDisplay.getFrameData(frameData);
|
|
|
|
// Loop over every gamepad and if we find any that have a pose use it.
|
|
var vrGamepads = [];
|
|
var gamepads = navigator.getGamepads();
|
|
for (var i = 0; i < gamepads.length; ++i) {
|
|
var gamepad = gamepads[i];
|
|
// The array may contain undefined gamepads, so check for that as
|
|
// well as a non-null pose.
|
|
if (gamepad) {
|
|
if (gamepad.pose)
|
|
vrGamepads.push(gamepad);
|
|
if ("hapticActuators" in gamepad && gamepad.hapticActuators.length > 0) {
|
|
for (var j = 0; j < gamepad.buttons.length; ++j) {
|
|
if (gamepad.buttons[j].pressed) {
|
|
// Vibrate the gamepad using to the value of the button as
|
|
// the vibration intensity.
|
|
gamepad.hapticActuators[0].pulse(gamepad.buttons[j].value, 100);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (vrDisplay.isPresenting) {
|
|
gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
|
|
getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
|
|
renderSceneView(frameData.leftProjectionMatrix, viewMat, vrGamepads);
|
|
|
|
gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
|
|
getStandingViewMatrix(viewMat, frameData.rightViewMatrix);
|
|
renderSceneView(frameData.rightProjectionMatrix, viewMat, vrGamepads);
|
|
|
|
vrDisplay.submitFrame();
|
|
} else {
|
|
gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
|
|
mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
|
|
getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
|
|
renderSceneView(projectionMat, viewMat, vrGamepads);
|
|
stats.renderOrtho();
|
|
}
|
|
} else {
|
|
window.requestAnimationFrame(onAnimationFrame);
|
|
|
|
// No VRDisplay found.
|
|
gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
|
|
mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
|
|
mat4.identity(viewMat);
|
|
mat4.translate(viewMat, viewMat, [0, -PLAYER_HEIGHT, 0]);
|
|
cubeIsland.render(projectionMat, viewMat, stats);
|
|
|
|
stats.renderOrtho();
|
|
}
|
|
|
|
stats.end();
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|