Files
old-2sajsmeetup/index.html

678 lines
20 KiB
HTML
Raw Normal View History

2017-03-11 15:22:17 +01:00
<!DOCTYPE html>
<html lang="en">
<head>
2017-03-18 15:59:26 +01:00
<title>Web VR - #2 JS Meetup Sarajevo</title>
2017-03-11 15:22:17 +01:00
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<style>
body {
width: 100%;
height: 100%;
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
/* Position the button on the bottom of the page. */
#ui {
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
font-family: 'Karla', sans-serif;
z-index: 1;
}
2017-03-11 19:26:53 +01:00
#magic-window {
2017-03-11 15:22:17 +01:00
display: block;
color: white;
margin-top: 1em;
}
</style>
</head>
<body>
<div id="ui">
<div id="vr-button"></div>
2017-03-18 06:43:20 +01:00
<a id="magic-window">Try it without a headset</a>
2017-03-11 15:22:17 +01:00
</div>
</body>
<script>
/*
* Debug parameters.
*/
WebVRConfig = {
/**
* webvr-polyfill configuration
*/
// Forces availability of VR mode.
//FORCE_ENABLE_VR: true, // Default: false.
// Complementary filter coefficient. 0 for accelerometer, 1 for gyro.
//K_FILTER: 0.98, // Default: 0.98.
// How far into the future to predict during fast motion.
//PREDICTION_TIME_S: 0.040, // Default: 0.040 (in seconds).
// Flag to disable touch panner. In case you have your own touch controls
//TOUCH_PANNER_DISABLED: true, // Default: false.
// Enable yaw panning only, disabling roll and pitch. This can be useful for
// panoramas with nothing interesting above or below.
//YAW_ONLY: true, // Default: false.
// Enable the deprecated version of the API (navigator.getVRDevices).
//ENABLE_DEPRECATED_API: true, // Default: false.
// Scales the recommended buffer size reported by WebVR, which can improve
// performance. Making this very small can lower the effective resolution of
// your scene.
BUFFER_SCALE: 0.5, // default: 1.0
// Allow VRDisplay.submitFrame to change gl bindings, which is more
// efficient if the application code will re-bind it's resources on the
// next frame anyway.
// Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM,
// gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
// and gl.TEXTURE_BINDING_2D for texture unit 0
// Warning: enabling this might lead to rendering issues.
//DIRTY_SUBMIT_FRAME_BINDINGS: true // default: false
};
</script>
<!--
A polyfill for Promises. Needed for IE and Edge.
-->
<script src="node_modules/es6-promise/dist/es6-promise.min.js"></script>
<!--
three.js 3d library
-->
<script src="node_modules/three/build/three.min.js"></script>
<!--
VRControls.js acquires positional information from connected VR devices and applies the transformations to a three.js camera object.
-->
<script src="node_modules/three/examples/js/controls/VRControls.js"></script>
<!--
VREffect.js handles stereo camera setup and rendering.
-->
<script src="node_modules/three/examples/js/effects/VREffect.js"></script>
<!--
A polyfill for WebVR using the Device{Motion,Orientation}Event API.
-->
<script src="node_modules/webvr-polyfill/build/webvr-polyfill.min.js"></script>
<!--
A set of UI controls for entering VR mode.
-->
<script src="node_modules/webvr-ui/build/webvr-ui.min.js"></script>
2017-03-18 15:59:26 +01:00
2017-03-11 19:26:53 +01:00
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.5.0.js"></script>
2017-03-18 06:43:20 +01:00
<script src="./orbit-gamepad.js"></script>
2017-03-11 15:22:17 +01:00
<script>
2017-03-11 19:26:53 +01:00
var pubnub = new PubNub({
publishKey: 'pub-c-7f43492c-431c-4404-b7a7-96ee3f60086a',
subscribeKey: 'sub-c-1fd379da-067e-11e7-83b6-0619f8945a4f'
})
2017-03-18 15:59:26 +01:00
2017-03-18 06:43:20 +01:00
var heightOfUser = 1.6;
if (isClient()) {
2017-03-18 15:59:26 +01:00
// ablyChannel.subscribe('camsync', function(message) {
// if (vrButton.isPresenting()) {
// var newMatrix = message.data.camera;
// camera.matrix.fromArray(message.data.camera);
// camera.matrix.decompose(camera.position, camera.quaternion, camera.scale);
// dollyCam.matrix.fromArray(message.data.dollyCam);
// dollyCam.matrix.decompose(dollyCam.position, dollyCam.quaternion, dollyCam.scale);
// effect.render(scene, camera);
// controls.update();
// }
// });
2017-03-11 19:26:53 +01:00
// Subscribe to the demo_tutorial channel
pubnub.addListener({
message: function(message) {
2017-03-18 06:43:20 +01:00
if (vrButton.isPresenting()) {
2017-03-18 15:59:26 +01:00
var newMatrix = message.message.camera;
2017-03-18 06:43:20 +01:00
camera.matrix.fromArray(message.message.camera);
camera.matrix.decompose(camera.position, camera.quaternion, camera.scale);
dollyCam.matrix.fromArray(message.message.dollyCam);
dollyCam.matrix.decompose(dollyCam.position, dollyCam.quaternion, dollyCam.scale);
effect.render(scene, camera);
controls.update();
}
2017-03-11 19:26:53 +01:00
}
})
2017-03-18 15:59:26 +01:00
pubnub.subscribe({
2017-03-11 19:26:53 +01:00
channels: ['cameramatrix']
});
}
2017-03-18 06:43:20 +01:00
function isClient() {
2017-03-18 15:59:26 +01:00
return window.location.hash === '' || window.location.hash === '#client';
2017-03-18 06:43:20 +01:00
}
2017-03-11 19:26:53 +01:00
var oldCameraMatrix;
2017-03-18 15:59:26 +01:00
var oldDollyCamMatrix;
2017-03-11 19:26:53 +01:00
var CAM_DELTA = 0.02;
function matrixChanged(oldMatrix, newMatrix) {
if (!oldMatrix) {
return true;
}
var oldLength = oldMatrix.length;
var newLength = newMatrix.length;
if (oldLength != newLength) {
return true;
}
for (var i = 0; i < newLength; i++) {
2017-03-18 06:43:20 +01:00
if (Math.abs(oldMatrix[i] - newMatrix[i]) > CAM_DELTA) {
2017-03-11 19:26:53 +01:00
return true;
}
}
return false;
}
function publishSampleMessage() {
if (window.location.hash !== '#presenter') {
return;
}
var newCameraMatrix = camera.matrix.toArray();
2017-03-18 06:43:20 +01:00
var newDollyCamMatrix = dollyCam.matrix.toArray();
2017-03-11 19:26:53 +01:00
var publishConfig = {
channel: "cameramatrix",
2017-03-18 06:43:20 +01:00
message: {
camera: newCameraMatrix,
dollyCam: newDollyCamMatrix
}
2017-03-11 19:26:53 +01:00
}
2017-03-18 15:59:26 +01:00
// ablyChannel.publish('camsync', {
// camera: newCameraMatrix,
// dollyCam: newDollyCamMatrix
// });
if (matrixChanged(oldCameraMatrix, newCameraMatrix) || matrixChanged(oldDollyCamMatrix, newDollyCamMatrix)) {
2017-03-11 19:26:53 +01:00
pubnub.publish(publishConfig, function(status, response) {
//console.log(status, response);
})
oldCameraMatrix = newCameraMatrix;
2017-03-18 15:59:26 +01:00
oldDollyCamMatrix = newDollyCamMatrix;
2017-03-11 19:26:53 +01:00
}
}
2017-03-11 15:22:17 +01:00
// Last time the scene was rendered.
var lastRenderTime = 0;
// Currently active VRDisplay.
var vrDisplay;
// How big of a sphere to render.
2017-03-18 15:59:26 +01:00
var boxSize = 200;
2017-03-11 15:22:17 +01:00
// Various global THREE.Objects.
var scene;
var cube;
var controls;
2017-03-18 06:43:20 +01:00
var orbitControls;
2017-03-11 15:22:17 +01:00
var effect;
var camera;
2017-03-11 19:26:53 +01:00
var dollyCam;
2017-03-11 15:22:17 +01:00
// EnterVRButton for rendering enter/exit UI.
var vrButton;
function loadFont(afterFont) {
var loader = new THREE.FontLoader();
loader.load('fonts/helvetiker_regular.typeface.json', function(response) {
font = response;
if (afterFont) {
afterFont();
}
});
}
function degreesToRadians(degrees) {
2017-03-11 19:26:53 +01:00
return degrees * (Math.PI / 180.0);
2017-03-11 15:22:17 +01:00
}
function onLoad() {
// Setup three.js WebGL renderer. Note: Antialiasing is a big performance hit.
// Only enable it if you actually need to.
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
// Append the canvas element created by the renderer to document body element.
document.body.appendChild(renderer.domElement);
// Create a three.js scene.
scene = new THREE.Scene();
// Create a three.js camera.
var aspect = window.innerWidth / window.innerHeight;
camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 10000);
2017-03-18 06:43:20 +01:00
var dummyControls = {
update: function() {}
};
controls = (!isClient()) ? new THREE.VRControls(camera) : dummyControls;
2017-03-11 15:22:17 +01:00
controls.standing = true;
2017-03-18 06:43:20 +01:00
camera.position.y = heightOfUser;
2017-03-11 19:26:53 +01:00
var oldUpdate = controls.update;
controls.update = function() {
oldUpdate();
publishSampleMessage();
};
// The dolly has to be a PerspectiveCamera, as opposed
// to a simple Object3D, since that's what
// OrbitControls expects.
2017-03-18 06:43:20 +01:00
dollyCam = new THREE.PerspectiveCamera(75, aspect, 0.1, 10000);
2017-03-11 19:26:53 +01:00
/*var orbitControls = new THREE.OrbitControls(dollyCam); */
2017-03-18 06:43:20 +01:00
2017-03-11 19:26:53 +01:00
dollyCam.add(camera);
2017-03-18 06:43:20 +01:00
//orbitControls = new OrbitControls(dollyCam);
2017-03-11 19:26:53 +01:00
scene.add(dollyCam);
2017-03-11 15:22:17 +01:00
// Apply VR stereo rendering to renderer.
effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
var loader = new THREE.TextureLoader();
loader.load('img/livada.jpg', onTextureLoaded);
2017-03-18 15:59:26 +01:00
// // Create 3D objects.
// var geometry = new THREE.SphereGeometry(0.5, 0.5, 0.5);
// var material = new THREE.MeshNormalMaterial();
// cube = new THREE.Mesh(geometry, material);
//
// // Position cube mesh to be right in front of you.
// cube.position.set(0, heightOfUser, -1);
2017-03-11 15:22:17 +01:00
// Add cube mesh to your three.js scene
//scene.add(cube);
window.addEventListener('resize', onResize, true);
window.addEventListener('vrdisplaypresentchange', onResize, true);
// Initialize the WebVR UI.
var uiOptions = {
color: 'black',
background: 'white',
corners: 'square'
};
vrButton = new webvrui.EnterVRButton(renderer.domElement, uiOptions);
vrButton.on('exit', function() {
camera.quaternion.set(0, 0, 0, 1);
2017-03-18 06:43:20 +01:00
camera.position.set(0, heightOfUser, 0);
2017-03-11 15:22:17 +01:00
});
vrButton.on('hide', function() {
document.getElementById('ui').style.display = 'none';
});
vrButton.on('show', function() {
document.getElementById('ui').style.display = 'inherit';
});
document.getElementById('vr-button').appendChild(vrButton.domElement);
document.getElementById('magic-window').addEventListener('click', function() {
vrButton.requestEnterFullscreen();
});
var light = new THREE.PointLight(0xffffff, 1, 300);
light.position.set(0, 4.9, 0);
scene.add(light);
loadFont(function() {
displayText("Web VR", {
color: 0xffaa00,
2017-03-18 15:59:26 +01:00
size: 2,
2017-03-11 15:22:17 +01:00
height: 0.01,
specular: 0xaaaaaa
}, {
2017-03-18 15:59:26 +01:00
x: -5,
y: heightOfUser + 5,
z: -9
2017-03-11 15:22:17 +01:00
});
2017-03-18 15:59:26 +01:00
displayText("Senad Uka", {
2017-03-11 15:22:17 +01:00
color: 0x00ff00,
size: 0.3,
height: 0.01,
specular: 0xaaaaaa
}, {
x: 2,
2017-03-18 06:43:20 +01:00
y: heightOfUser + 2,
2017-03-11 15:22:17 +01:00
z: -2
}, {
x: 0.1,
y: degreesToRadians(-35),
z: 0
});
2017-03-18 15:59:26 +01:00
// displayText("JS Meetup Sarajevo", {
// color: 0xaaff12,
// size: 0.3,
// height: 0.02,
// specular: 0x11aaaa
// }, {
// x: 2,
// y: heightOfUser - 1,
// z: -2
// }, {
// x: degreesToRadians(-40),
// y: degreesToRadians(0),
// z: 0
// });
});
// 1 pravo
displayPicture('./img/jsmeetup.jpeg', {
width: 10,
height: 5,
depth: 0.1,
},
{
y: heightOfUser,
x: 0,
z: -9
}, {
x: 0,
y: 0,
z: 0
});
// 1 dole
displayPicture('./img/jsmeetup.jpeg', {
width: 5,
height: 2.5,
depth: 0.1,
},
{
y: 0.01,
x: 0,
z: 0
2017-03-11 15:22:17 +01:00
}, {
2017-03-18 15:59:26 +01:00
x: degreesToRadians(-90),
y: 0,
z: 0
});
// 1 desno
displayPicture('./img/jsmeetup.jpeg', {
width: 10,
height: 5,
depth: 0.1,
},
{
y: heightOfUser,
x: 10,
z: -5
2017-03-11 15:22:17 +01:00
}, {
2017-03-18 15:59:26 +01:00
x: 0,
y: 5,
2017-03-11 15:22:17 +01:00
z: 0
2017-03-18 15:59:26 +01:00
2017-03-11 15:22:17 +01:00
});
2017-03-18 15:59:26 +01:00
// 1 lijevo
displayPicture('./img/jsmeetup.jpeg', {
width: 10,
height: 5,
depth: 0.1,
},
{
y: heightOfUser,
x: -10,
z: -5
}, {
x: 0,
y: -5,
z: 0
});
2017-03-11 15:22:17 +01:00
}
function onTextureLoaded(texture) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
//texture.repeat.set(boxSize, boxSize);
var geometry = new THREE.SphereGeometry(boxSize, boxSize, boxSize);
var material = new THREE.MeshBasicMaterial({
map: texture,
/* color: 0x01BE00,
side: THREE.BackSide */
});
// Align the skybox to the floor (which is at y=0).
skybox = new THREE.Mesh(geometry, material);
skybox.position.y = boxSize / 3;
skybox.scale.x = -1;
scene.add(skybox);
// For high end VR devices like Vive and Oculus, take into account the stage
// parameters provided.
setupStage();
}
// Request animation frame loop function
function animate(timestamp) {
var delta = Math.min(timestamp - lastRenderTime, 500);
lastRenderTime = timestamp;
// Apply rotation to cube mesh
2017-03-18 15:59:26 +01:00
//cube.rotation.y += delta * 0.0006;
2017-03-11 15:22:17 +01:00
// Only update controls if we're presenting.
if (vrButton.isPresenting()) {
controls.update();
}
// Render the scene.
effect.render(scene, camera);
vrDisplay.requestAnimationFrame(animate);
}
function onResize(e) {
effect.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
// Get the HMD, and if we're dealing with something that specifies
// stageParameters, rearrange the scene.
function setupStage() {
navigator.getVRDisplays().then(function(displays) {
if (displays.length > 0) {
vrDisplay = displays[0];
if (vrDisplay.stageParameters) {
setStageDimensions(vrDisplay.stageParameters);
}
vrDisplay.requestAnimationFrame(animate);
}
});
}
function setStageDimensions(stage) {
// Make the skybox fit the stage.
var material = skybox.material;
scene.remove(skybox);
// Size the skybox according to the size of the actual stage.
var geometry = new THREE.BoxGeometry(stage.sizeX, boxSize, stage.sizeZ);
skybox = new THREE.Mesh(geometry, material);
// Place it on the floor.
skybox.position.y = boxSize / 2;
//scene.add(skybox);
// Place the cube in the middle of the scene, at user height.
2017-03-18 06:43:20 +01:00
cube.position.set(0, heightOfUser, 0);
2017-03-11 15:22:17 +01:00
}
var currentCityTextMesh;
function displayText(text,
format = {
color: 0xffaa00,
specular: 0xaaaaaa,
size: 0.5,
height: 0.01,
},
position = {
2017-03-18 06:43:20 +01:00
y: heightOfUser,
2017-03-11 15:22:17 +01:00
x: 0,
z: -1
}, rotation = {
x: 0,
y: 0,
z: 0
}) {
currentCityText = new THREE.TextGeometry(text, {
size: format.size,
height: format.height,
font: font,
curveSegments: 12,
bevelThickness: 0.01,
bevelSize: 0.01,
bevelEnabled: false
});
currentCityTextMesh = new THREE.Mesh(currentCityText, new THREE.MeshPhongMaterial({
color: format.color,
specular: format.specular
}));
currentCityTextMesh.position.y = position.y;
currentCityTextMesh.position.z = position.z;
currentCityTextMesh.position.x = position.x;
currentCityTextMesh.rotateX(rotation.x);
currentCityTextMesh.rotateY(rotation.y);
currentCityTextMesh.rotateZ(rotation.z);
scene.add(currentCityTextMesh);
}
2017-03-11 19:26:53 +01:00
function displayPicture(picture,
format = {
2017-03-18 15:59:26 +01:00
width: 10,
height: 5,
2017-03-11 19:26:53 +01:00
depth: 0.1,
},
position = {
2017-03-18 06:43:20 +01:00
y: heightOfUser,
2017-03-11 19:26:53 +01:00
x: 0,
z: -9
}, rotation = {
x: 0,
y: 0,
z: 0
}) {
// Add a repeating grid as a skybox.
var loader = new THREE.TextureLoader();
loader.load(picture, function(texture) {
var geometry = new THREE.BoxGeometry(format.width, format.height, format.depth);
var material = new THREE.MeshBasicMaterial({
map: texture,
/* color: 0x01BE00,
side: THREE.BackSide */
});
var display = new THREE.Mesh(geometry, material);
display.position.y = position.y;
display.position.z = position.z;
display.position.x = position.x;
display.rotateX(rotation.x);
display.rotateY(rotation.y);
display.rotateZ(rotation.z);
scene.add(display);
});
}
2017-03-18 06:43:20 +01:00
function buttonPressed(buttonNumber) {
var keyPanSpeed = 1;
var direction = camera.getWorldDirection();
var vectorUp = new THREE.Vector3(0, 1, 0);
var strafeVector = new THREE.Vector3().crossVectors(vectorUp, direction).normalize();
switch (buttonNumber) {
case 6:
dollyCam.position.add(direction.multiplyScalar(keyPanSpeed));
controls.update();
break;
case 7:
dollyCam.position.add(direction.multiplyScalar(-keyPanSpeed));
controls.update();
break;
case 8:
dollyCam.position.add(strafeVector.multiplyScalar(keyPanSpeed))
controls.update();
break;
case 9:
dollyCam.position.add(strafeVector.multiplyScalar(-keyPanSpeed))
controls.update();
break;
case 0:
dollyCam.position.x = 0;
dollyCam.position.y = 0;
dollyCam.position.z = 0;
controls.update();
break;
}
//alert(buttonNumber);
}
2017-03-11 15:22:17 +01:00
window.addEventListener('load', onLoad);
</script>
2017-03-18 06:43:20 +01:00
<script src="./gamepad.js"></script>
2017-03-11 15:22:17 +01:00
</html>