servo/tests/html/webvr/vr-controllers.html
2017-07-05 22:38:29 +02:00

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>