@egjs/view360
Version:
360 integrated viewing solution from inside-out view to outside-in view. It provides user-friendly service by rotating 360 degrees through various user interaction such as motion sensor and touch.
496 lines (425 loc) • 15.6 kB
text/typescript
/* eslint-disable */
/*
* Copyright 2015 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// tslint:disable: only-arrow-functions
import { window as win, document as doc, navigator as nav } from "../../../../utils/browser";
const userAgent = nav?.userAgent ?? "";
const Util = (win ).Util || {};
Util.MIN_TIMESTEP = 0.001;
Util.MAX_TIMESTEP = 1;
Util.base64 = function(mimeType, base64) {
return "data:" + mimeType + ";base64," + base64;
};
Util.clamp = function(value, min, max) {
return Math.min(Math.max(min, value), max);
};
Util.lerp = function(a, b, t) {
return a + ((b - a) * t);
};
Util.isIOS = (function() {
const isIOS = /iPad|iPhone|iPod/.test(nav?.platform);
return function() {
return isIOS;
};
})();
Util.isWebViewAndroid = (function() {
const isWebViewAndroid = userAgent.indexOf("Version") !== -1 &&
userAgent.indexOf("Android") !== -1 &&
userAgent.indexOf("Chrome") !== -1;
return function() {
return isWebViewAndroid;
};
})();
Util.isSafari = (function() {
const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
return function() {
return isSafari;
};
})();
Util.isFirefoxAndroid = (function() {
const isFirefoxAndroid = userAgent.indexOf("Firefox") !== -1 &&
userAgent.indexOf("Android") !== -1;
return function() {
return isFirefoxAndroid;
};
})();
Util.isR7 = (function() {
const isR7 = userAgent.indexOf("R7 Build") !== -1;
return function() {
return isR7;
};
})();
Util.isLandscapeMode = function() {
const rtn = (win.orientation === 90 || win.orientation === -90);
return Util.isR7() ? !rtn : rtn;
};
// Helper method to validate the time steps of sensor timestamps.
Util.isTimestampDeltaValid = function(timestampDeltaS) {
if (isNaN(timestampDeltaS)) {
return false;
}
if (timestampDeltaS <= Util.MIN_TIMESTEP) {
return false;
}
if (timestampDeltaS > Util.MAX_TIMESTEP) {
return false;
}
return true;
};
Util.getScreenWidth = function() {
return Math.max(win.screen.width, win.screen.height) *
win.devicePixelRatio;
};
Util.getScreenHeight = function() {
return Math.min(win.screen.width, win.screen.height) *
win.devicePixelRatio;
};
Util.requestFullscreen = function(element) {
if (Util.isWebViewAndroid()) {
return false;
}
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else {
return false;
}
return true;
};
Util.exitFullscreen = function() {
if (doc.exitFullscreen) {
doc.exitFullscreen();
} else if (doc.webkitExitFullscreen) {
doc.webkitExitFullscreen();
} else if (doc.mozCancelFullScreen) {
doc.mozCancelFullScreen();
} else if (doc.msExitFullscreen) {
doc.msExitFullscreen();
} else {
return false;
}
return true;
};
Util.getFullscreenElement = function() {
return doc.fullscreenElement ||
doc.webkitFullscreenElement ||
doc.mozFullScreenElement ||
doc.msFullscreenElement;
};
Util.linkProgram = function(gl, vertexSource, fragmentSource, attribLocationMap) {
// No error checking for brevity.
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
for (const attribName in attribLocationMap)
gl.bindAttribLocation(program, attribLocationMap[attribName], attribName);
gl.linkProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
};
Util.getProgramUniforms = function(gl, program) {
const uniforms = {};
const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
let uniformName = "";
for (let i = 0; i < uniformCount; i++) {
const uniformInfo = gl.getActiveUniform(program, i);
uniformName = uniformInfo.name.replace("[0]", "");
uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
}
return uniforms;
};
Util.orthoMatrix = function(out, left, right, bottom, top, near, far) {
const lr = 1 / (left - right);
const bt = 1 / (bottom - top);
const nf = 1 / (near - far);
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
return out;
};
Util.copyArray = function(source, dest) {
for (let i = 0, n = source.length; i < n; i++) {
dest[i] = source[i];
}
};
Util.isMobile = function() {
let check = false;
(function(a) {
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))check = true;
})(userAgent || nav?.vendor || win.opera);
return check;
};
Util.extend = function(dest, src) {
for (const key in src) {
if (src.hasOwnProperty(key)) {
dest[key] = src[key];
}
}
return dest;
};
Util.safariCssSizeWorkaround = function(canvas) {
// TODO(smus): Remove this workaround when Safari for iOS is fixed.
// iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556).
//
// "To the last I grapple with thee;
// from hell's heart I stab at thee;
// for hate's sake I spit my last breath at thee."
// -- Moby Dick, by Herman Melville
if (Util.isIOS()) {
const width = canvas.style.width;
const height = canvas.style.height;
canvas.style.width = (parseInt(width) + 1) + "px";
canvas.style.height = (parseInt(height)) + "px";
setTimeout(function() {
canvas.style.width = width;
canvas.style.height = height;
}, 100);
}
// Debug only.
win.Util = Util;
win.canvas = canvas;
};
Util.isDebug = function() {
return Util.getQueryParameter("debug");
};
Util.getQueryParameter = function(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
const regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
const results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
};
Util.frameDataFromPose = (function() {
const piOver180 = Math.PI / 180.0;
const rad45 = Math.PI * 0.25;
// Borrowed from glMatrix.
function mat4_perspectiveFromFieldOfView(out, fov, near, far) {
const upTan = Math.tan(fov ? (fov.upDegrees * piOver180) : rad45);
const downTan = Math.tan(fov ? (fov.downDegrees * piOver180) : rad45);
const leftTan = Math.tan(fov ? (fov.leftDegrees * piOver180) : rad45);
const rightTan = Math.tan(fov ? (fov.rightDegrees * piOver180) : rad45);
const xScale = 2.0 / (leftTan + rightTan);
const yScale = 2.0 / (upTan + downTan);
out[0] = xScale;
out[1] = 0.0;
out[2] = 0.0;
out[3] = 0.0;
out[4] = 0.0;
out[5] = yScale;
out[6] = 0.0;
out[7] = 0.0;
out[8] = -((leftTan - rightTan) * xScale * 0.5);
out[9] = ((upTan - downTan) * yScale * 0.5);
out[10] = far / (near - far);
out[11] = -1.0;
out[12] = 0.0;
out[13] = 0.0;
out[14] = (far * near) / (near - far);
out[15] = 0.0;
return out;
}
function mat4_fromRotationTranslation(out, q, v) {
// Quaternion math
const x = q[0];
const y = q[1];
const z = q[2];
const w = q[3];
const x2 = x + x;
const y2 = y + y;
const z2 = z + z;
const xx = x * x2;
const xy = x * y2;
const xz = x * z2;
const yy = y * y2;
const yz = y * z2;
const zz = z * z2;
const wx = w * x2;
const wy = w * y2;
const wz = w * z2;
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = v[0];
out[13] = v[1];
out[14] = v[2];
out[15] = 1;
return out;
}
function mat4_translate(out, a, v) {
const x = v[0];
const y = v[1];
const z = v[2];
let a00;
let a01;
let a02;
let a03;
let a10;
let a11;
let a12;
let a13;
let a20;
let a21;
let a22;
let a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
}
function mat4_invert(out, a) {
const a00 = a[0];
const a01 = a[1];
const a02 = a[2];
const a03 = a[3];
const a10 = a[4];
const a11 = a[5];
const a12 = a[6];
const a13 = a[7];
const a20 = a[8];
const a21 = a[9];
const a22 = a[10];
const a23 = a[11];
const a30 = a[12];
const a31 = a[13];
const a32 = a[14];
const a33 = a[15];
const b00 = a00 * a11 - a01 * a10;
const b01 = a00 * a12 - a02 * a10;
const b02 = a00 * a13 - a03 * a10;
const b03 = a01 * a12 - a02 * a11;
const b04 = a01 * a13 - a03 * a11;
const b05 = a02 * a13 - a03 * a12;
const b06 = a20 * a31 - a21 * a30;
const b07 = a20 * a32 - a22 * a30;
const b08 = a20 * a33 - a23 * a30;
const b09 = a21 * a32 - a22 * a31;
const b10 = a21 * a33 - a23 * a31;
const b11 = a22 * a33 - a23 * a32;
// Calculate the determinant
let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return out;
}
const defaultOrientation = new Float32Array([0, 0, 0, 1]);
const defaultPosition = new Float32Array([0, 0, 0]);
function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) {
mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar);
const orientation = pose.orientation || defaultOrientation;
const position = pose.position || defaultPosition;
mat4_fromRotationTranslation(view, orientation, position);
if (parameters)
mat4_translate(view, view, parameters.offset);
mat4_invert(view, view);
}
return function(frameData, pose, vrDisplay) {
if (!frameData || !pose)
return false;
frameData.pose = pose;
frameData.timestamp = pose.timestamp;
updateEyeMatrices(
frameData.leftProjectionMatrix, frameData.leftViewMatrix,
pose, vrDisplay.getEyeParameters("left"), vrDisplay);
updateEyeMatrices(
frameData.rightProjectionMatrix, frameData.rightViewMatrix,
pose, vrDisplay.getEyeParameters("right"), vrDisplay);
return true;
};
})();
Util.isInsideCrossDomainIFrame = function() {
const isFramed = (win.self !== win.top);
const refDomain = Util.getDomainFromUrl(doc.referrer);
const thisDomain = Util.getDomainFromUrl(win.location.href);
return isFramed && (refDomain !== thisDomain);
};
// From http://stackoverflow.com/a/23945027.
Util.getDomainFromUrl = function(url) {
let domain;
// Find & remove protocol (http, ftp, etc.) and get domain.
if (url.indexOf("://") > -1) {
domain = url.split("/")[2];
} else {
domain = url.split("/")[0];
}
// find & remove port number
domain = domain.split(":")[0];
return domain;
};
export default Util;