Files
tubestation/servo/tests/html/webvr/js/vr-cube-island.js
Imanol Fernandez 0dd62e4907 servo: Merge #14618 - WebVR API Implementation (from MortimerGoro:webvr_api); r=larsbergstrom,emilio,jdm,nox,asajeffrey,cvan
<!-- Please describe your changes on the following line: -->

WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;)

Info about the architecture:
https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run:

mach run tests/html/webvr/room-scale.html

Source-Repo: https://github.com/servo/servo
Source-Revision: 518ef39cfd429082dd8dc0d5b13e2db637d08a53
2017-01-09 06:39:45 -08:00

211 lines
7.0 KiB
JavaScript

// 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.
/* global mat4, WGLUProgram */
/*
Like CubeSea, but designed around a users physical space. One central platform
that maps to the users play area and several floating cubes that sit just
those boundries (just to add visual interest)
*/
window.VRCubeIsland = (function () {
"use strict";
var cubeIslandVS = [
"uniform mat4 projectionMat;",
"uniform mat4 modelViewMat;",
"attribute vec3 position;",
"attribute vec2 texCoord;",
"varying vec2 vTexCoord;",
"void main() {",
" vTexCoord = texCoord;",
" gl_Position = projectionMat * modelViewMat * vec4( position, 1.0 );",
"}",
].join("\n");
var cubeIslandFS = [
"precision mediump float;",
"uniform sampler2D diffuse;",
"varying vec2 vTexCoord;",
"void main() {",
" gl_FragColor = texture2D(diffuse, vTexCoord);",
"}",
].join("\n");
var CubeIsland = function (gl, texture, width, depth) {
this.gl = gl;
this.statsMat = mat4.create();
this.texture = texture;
this.program = new WGLUProgram(gl);
this.program.attachShaderSource(cubeIslandVS, gl.VERTEX_SHADER);
this.program.attachShaderSource(cubeIslandFS, gl.FRAGMENT_SHADER);
this.program.bindAttribLocation({
position: 0,
texCoord: 1
});
this.program.link();
this.vertBuffer = gl.createBuffer();
this.indexBuffer = gl.createBuffer();
this.resize(width, depth);
};
CubeIsland.prototype.resize = function (width, depth) {
var gl = this.gl;
this.width = width;
this.depth = depth;
var cubeVerts = [];
var cubeIndices = [];
// Build a single box.
function appendBox (left, bottom, back, right, top, front) {
// Bottom
var idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 1, idx + 2);
cubeIndices.push(idx, idx + 2, idx + 3);
cubeVerts.push(left, bottom, back, 0.0, 1.0);
cubeVerts.push(right, bottom, back, 1.0, 1.0);
cubeVerts.push(right, bottom, front, 1.0, 0.0);
cubeVerts.push(left, bottom, front, 0.0, 0.0);
// Top
idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 2, idx + 1);
cubeIndices.push(idx, idx + 3, idx + 2);
cubeVerts.push(left, top, back, 0.0, 0.0);
cubeVerts.push(right, top, back, 1.0, 0.0);
cubeVerts.push(right, top, front, 1.0, 1.0);
cubeVerts.push(left, top, front, 0.0, 1.0);
// Left
idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 2, idx + 1);
cubeIndices.push(idx, idx + 3, idx + 2);
cubeVerts.push(left, bottom, back, 0.0, 1.0);
cubeVerts.push(left, top, back, 0.0, 0.0);
cubeVerts.push(left, top, front, 1.0, 0.0);
cubeVerts.push(left, bottom, front, 1.0, 1.0);
// Right
idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 1, idx + 2);
cubeIndices.push(idx, idx + 2, idx + 3);
cubeVerts.push(right, bottom, back, 1.0, 1.0);
cubeVerts.push(right, top, back, 1.0, 0.0);
cubeVerts.push(right, top, front, 0.0, 0.0);
cubeVerts.push(right, bottom, front, 0.0, 1.0);
// Back
idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 2, idx + 1);
cubeIndices.push(idx, idx + 3, idx + 2);
cubeVerts.push(left, bottom, back, 1.0, 1.0);
cubeVerts.push(right, bottom, back, 0.0, 1.0);
cubeVerts.push(right, top, back, 0.0, 0.0);
cubeVerts.push(left, top, back, 1.0, 0.0);
// Front
idx = cubeVerts.length / 5.0;
cubeIndices.push(idx, idx + 1, idx + 2);
cubeIndices.push(idx, idx + 2, idx + 3);
cubeVerts.push(left, bottom, front, 0.0, 1.0);
cubeVerts.push(right, bottom, front, 1.0, 1.0);
cubeVerts.push(right, top, front, 1.0, 0.0);
cubeVerts.push(left, top, front, 0.0, 0.0);
}
// Appends a cube with the given centerpoint and size.
function appendCube (x, y, z, size) {
var halfSize = size * 0.5;
appendBox(x - halfSize, y - halfSize, z - halfSize,
x + halfSize, y + halfSize, z + halfSize);
}
// Main "island", covers where the user can safely stand. Top of the cube
// (the ground the user stands on) should be at Y=0 to align with users
// floor. X=0 and Z=0 should be at the center of the users play space.
appendBox(-width * 0.5, -width, -depth * 0.5, width * 0.5, 0, depth * 0.5);
// A sprinkling of other cubes to make things more visually interesting.
appendCube(1.1, 0.3, (-depth * 0.5) - 0.8, 0.5);
appendCube(-0.5, 1.0, (-depth * 0.5) - 0.9, 0.75);
appendCube(0.6, 1.5, (-depth * 0.5) - 0.6, 0.4);
appendCube(-1.0, 0.5, (-depth * 0.5) - 0.5, 0.2);
appendCube((-width * 0.5) - 0.8, 0.3, -1.1, 0.5);
appendCube((-width * 0.5) - 0.9, 1.0, 0.5, 0.75);
appendCube((-width * 0.5) - 0.6, 1.5, -0.6, 0.4);
appendCube((-width * 0.5) - 0.5, 0.5, 1.0, 0.2);
appendCube((width * 0.5) + 0.8, 0.3, 1.1, 0.5);
appendCube((width * 0.5) + 0.9, 1.0, -0.5, 0.75);
appendCube((width * 0.5) + 0.6, 1.5, 0.6, 0.4);
appendCube((width * 0.5) + 0.5, 0.5, -1.0, 0.2);
appendCube(1.1, 1.4, (depth * 0.5) + 0.8, 0.5);
appendCube(-0.5, 1.0, (depth * 0.5) + 0.9, 0.75);
appendCube(0.6, 0.4, (depth * 0.5) + 0.6, 0.4);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVerts), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeIndices), gl.STATIC_DRAW);
this.indexCount = cubeIndices.length;
};
CubeIsland.prototype.render = function (projectionMat, modelViewMat, stats) {
var gl = this.gl;
var program = this.program;
program.use();
gl.uniformMatrix4fv(program.uniform.projectionMat, false, projectionMat);
gl.uniformMatrix4fv(program.uniform.modelViewMat, false, modelViewMat);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.enableVertexAttribArray(program.attrib.position);
gl.enableVertexAttribArray(program.attrib.texCoord);
gl.vertexAttribPointer(program.attrib.position, 3, gl.FLOAT, false, 20, 0);
gl.vertexAttribPointer(program.attrib.texCoord, 2, gl.FLOAT, false, 20, 12);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(this.program.uniform.diffuse, 0);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_SHORT, 0);
if (stats) {
// To ensure that the FPS counter is visible in VR mode we have to
// render it as part of the scene.
mat4.fromTranslation(this.statsMat, [0, 1.5, -this.depth * 0.5]);
mat4.scale(this.statsMat, this.statsMat, [0.5, 0.5, 0.5]);
mat4.rotateX(this.statsMat, this.statsMat, -0.75);
mat4.multiply(this.statsMat, modelViewMat, this.statsMat);
stats.render(projectionMat, this.statsMat);
}
};
return CubeIsland;
})();