playcanvas
Version:
PlayCanvas WebGL game engine
2,175 lines (1,769 loc) • 1.9 MB
JavaScript
/**
* @license
* PlayCanvas Engine v1.41.0-dev revision bbc2326ad
* Copyright 2011-2021 PlayCanvas Ltd. All rights reserved.
*/
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function (predicate) {
if (this == null) {
throw TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (typeof predicate !== 'function') {
throw TypeError('predicate must be a function');
}
var thisArg = arguments[1];
var k = 0;
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
k++;
}
return undefined;
},
configurable: true,
writable: true
});
}
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function (predicate) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var thisArg = arguments[1];
var k = 0;
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return k;
}
k++;
}
return -1;
},
configurable: true,
writable: true
});
}
Math.log2 = Math.log2 || function (x) {
return Math.log(x) * Math.LOG2E;
};
if (!Math.sign) {
Math.sign = function (x) {
return (x > 0) - (x < 0) || +x;
};
}
if (Number.isFinite === undefined) Number.isFinite = function (value) {
return typeof value === 'number' && isFinite(value);
};
if (typeof Object.assign != 'function') {
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
(function () {
if (typeof navigator === 'undefined' || typeof document === 'undefined') {
return;
}
navigator.pointer = navigator.pointer || navigator.webkitPointer || navigator.mozPointer;
var pointerlockchange = function pointerlockchange() {
var e = document.createEvent('CustomEvent');
e.initCustomEvent('pointerlockchange', true, false, null);
document.dispatchEvent(e);
};
var pointerlockerror = function pointerlockerror() {
var e = document.createEvent('CustomEvent');
e.initCustomEvent('pointerlockerror', true, false, null);
document.dispatchEvent(e);
};
document.addEventListener('webkitpointerlockchange', pointerlockchange, false);
document.addEventListener('webkitpointerlocklost', pointerlockchange, false);
document.addEventListener('mozpointerlockchange', pointerlockchange, false);
document.addEventListener('mozpointerlocklost', pointerlockchange, false);
document.addEventListener('webkitpointerlockerror', pointerlockerror, false);
document.addEventListener('mozpointerlockerror', pointerlockerror, false);
if (Element.prototype.mozRequestPointerLock) {
Element.prototype.requestPointerLock = function () {
this.mozRequestPointerLock();
};
} else {
Element.prototype.requestPointerLock = Element.prototype.requestPointerLock || Element.prototype.webkitRequestPointerLock || Element.prototype.mozRequestPointerLock;
}
if (!Element.prototype.requestPointerLock && navigator.pointer) {
Element.prototype.requestPointerLock = function () {
var el = this;
document.pointerLockElement = el;
navigator.pointer.lock(el, pointerlockchange, pointerlockerror);
};
}
document.exitPointerLock = document.exitPointerLock || document.webkitExitPointerLock || document.mozExitPointerLock;
if (!document.exitPointerLock) {
document.exitPointerLock = function () {
if (navigator.pointer) {
document.pointerLockElement = null;
navigator.pointer.unlock();
}
};
}
})();
(function () {
if (typeof window === 'undefined') return;
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
})();
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (search, this_len) {
if (this_len === undefined || this_len > this.length) {
this_len = this.length;
}
return this.substring(this_len - search.length, this_len) === search;
};
}
if (!String.prototype.includes) {
String.prototype.includes = function (search, start) {
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (search, pos) {
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
};
}
(function () {
var glErrorShadow = {};
function error(msg) {
if (window.console && window.console.error) {
window.console.error(msg);
}
}
function log(msg) {
if (window.console && window.console.log) {
window.console.log(msg);
}
}
function synthesizeGLError(err, opt_msg) {
glErrorShadow[err] = true;
if (opt_msg !== undefined) {
error(opt_msg);
}
}
function wrapGLError(gl) {
var f = gl.getError;
gl.getError = function () {
var err;
do {
err = f.apply(gl);
if (err != gl.NO_ERROR) {
glErrorShadow[err] = true;
}
} while (err != gl.NO_ERROR);
for (var err in glErrorShadow) {
if (glErrorShadow[err]) {
delete glErrorShadow[err];
return parseInt(err);
}
}
return gl.NO_ERROR;
};
}
var WebGLVertexArrayObjectOES = function WebGLVertexArrayObjectOES(ext) {
var gl = ext.gl;
this.ext = ext;
this.isAlive = true;
this.hasBeenBound = false;
this.elementArrayBuffer = null;
this.attribs = new Array(ext.maxVertexAttribs);
for (var n = 0; n < this.attribs.length; n++) {
var attrib = new WebGLVertexArrayObjectOES.VertexAttrib(gl);
this.attribs[n] = attrib;
}
this.maxAttrib = 0;
};
WebGLVertexArrayObjectOES.VertexAttrib = function VertexAttrib(gl) {
this.enabled = false;
this.buffer = null;
this.size = 4;
this.type = gl.FLOAT;
this.normalized = false;
this.stride = 16;
this.offset = 0;
this.cached = "";
this.recache();
};
WebGLVertexArrayObjectOES.VertexAttrib.prototype.recache = function recache() {
this.cached = [this.size, this.type, this.normalized, this.stride, this.offset].join(":");
};
var OESVertexArrayObject = function OESVertexArrayObject(gl) {
var self = this;
this.gl = gl;
wrapGLError(gl);
var original = this.original = {
getParameter: gl.getParameter,
enableVertexAttribArray: gl.enableVertexAttribArray,
disableVertexAttribArray: gl.disableVertexAttribArray,
bindBuffer: gl.bindBuffer,
getVertexAttrib: gl.getVertexAttrib,
vertexAttribPointer: gl.vertexAttribPointer
};
gl.getParameter = function getParameter(pname) {
if (pname == self.VERTEX_ARRAY_BINDING_OES) {
if (self.currentVertexArrayObject == self.defaultVertexArrayObject) {
return null;
} else {
return self.currentVertexArrayObject;
}
}
return original.getParameter.apply(this, arguments);
};
gl.enableVertexAttribArray = function enableVertexAttribArray(index) {
var vao = self.currentVertexArrayObject;
vao.maxAttrib = Math.max(vao.maxAttrib, index);
var attrib = vao.attribs[index];
attrib.enabled = true;
return original.enableVertexAttribArray.apply(this, arguments);
};
gl.disableVertexAttribArray = function disableVertexAttribArray(index) {
var vao = self.currentVertexArrayObject;
vao.maxAttrib = Math.max(vao.maxAttrib, index);
var attrib = vao.attribs[index];
attrib.enabled = false;
return original.disableVertexAttribArray.apply(this, arguments);
};
gl.bindBuffer = function bindBuffer(target, buffer) {
switch (target) {
case gl.ARRAY_BUFFER:
self.currentArrayBuffer = buffer;
break;
case gl.ELEMENT_ARRAY_BUFFER:
self.currentVertexArrayObject.elementArrayBuffer = buffer;
break;
}
return original.bindBuffer.apply(this, arguments);
};
gl.getVertexAttrib = function getVertexAttrib(index, pname) {
var vao = self.currentVertexArrayObject;
var attrib = vao.attribs[index];
switch (pname) {
case gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
return attrib.buffer;
case gl.VERTEX_ATTRIB_ARRAY_ENABLED:
return attrib.enabled;
case gl.VERTEX_ATTRIB_ARRAY_SIZE:
return attrib.size;
case gl.VERTEX_ATTRIB_ARRAY_STRIDE:
return attrib.stride;
case gl.VERTEX_ATTRIB_ARRAY_TYPE:
return attrib.type;
case gl.VERTEX_ATTRIB_ARRAY_NORMALIZED:
return attrib.normalized;
default:
return original.getVertexAttrib.apply(this, arguments);
}
};
gl.vertexAttribPointer = function vertexAttribPointer(indx, size, type, normalized, stride, offset) {
var vao = self.currentVertexArrayObject;
vao.maxAttrib = Math.max(vao.maxAttrib, indx);
var attrib = vao.attribs[indx];
attrib.buffer = self.currentArrayBuffer;
attrib.size = size;
attrib.type = type;
attrib.normalized = normalized;
attrib.stride = stride;
attrib.offset = offset;
attrib.recache();
return original.vertexAttribPointer.apply(this, arguments);
};
if (gl.instrumentExtension) {
gl.instrumentExtension(this, "OES_vertex_array_object");
}
gl.canvas.addEventListener('webglcontextrestored', function () {
log("OESVertexArrayObject emulation library context restored");
self.reset_();
}, true);
this.reset_();
};
OESVertexArrayObject.prototype.VERTEX_ARRAY_BINDING_OES = 0x85B5;
OESVertexArrayObject.prototype.reset_ = function reset_() {
var contextWasLost = this.vertexArrayObjects !== undefined;
if (contextWasLost) {
for (var ii = 0; ii < this.vertexArrayObjects.length; ++ii) {
this.vertexArrayObjects.isAlive = false;
}
}
var gl = this.gl;
this.maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
this.defaultVertexArrayObject = new WebGLVertexArrayObjectOES(this);
this.currentVertexArrayObject = null;
this.currentArrayBuffer = null;
this.vertexArrayObjects = [this.defaultVertexArrayObject];
this.bindVertexArrayOES(null);
};
OESVertexArrayObject.prototype.createVertexArrayOES = function createVertexArrayOES() {
var arrayObject = new WebGLVertexArrayObjectOES(this);
this.vertexArrayObjects.push(arrayObject);
return arrayObject;
};
OESVertexArrayObject.prototype.deleteVertexArrayOES = function deleteVertexArrayOES(arrayObject) {
arrayObject.isAlive = false;
this.vertexArrayObjects.splice(this.vertexArrayObjects.indexOf(arrayObject), 1);
if (this.currentVertexArrayObject == arrayObject) {
this.bindVertexArrayOES(null);
}
};
OESVertexArrayObject.prototype.isVertexArrayOES = function isVertexArrayOES(arrayObject) {
if (arrayObject && arrayObject instanceof WebGLVertexArrayObjectOES) {
if (arrayObject.hasBeenBound && arrayObject.ext == this) {
return true;
}
}
return false;
};
OESVertexArrayObject.prototype.bindVertexArrayOES = function bindVertexArrayOES(arrayObject) {
var gl = this.gl;
if (arrayObject && !arrayObject.isAlive) {
synthesizeGLError(gl.INVALID_OPERATION, "bindVertexArrayOES: attempt to bind deleted arrayObject");
return;
}
var original = this.original;
var oldVAO = this.currentVertexArrayObject;
this.currentVertexArrayObject = arrayObject || this.defaultVertexArrayObject;
this.currentVertexArrayObject.hasBeenBound = true;
var newVAO = this.currentVertexArrayObject;
if (oldVAO == newVAO) {
return;
}
if (!oldVAO || newVAO.elementArrayBuffer != oldVAO.elementArrayBuffer) {
original.bindBuffer.call(gl, gl.ELEMENT_ARRAY_BUFFER, newVAO.elementArrayBuffer);
}
var currentBinding = this.currentArrayBuffer;
var maxAttrib = Math.max(oldVAO ? oldVAO.maxAttrib : 0, newVAO.maxAttrib);
for (var n = 0; n <= maxAttrib; n++) {
var attrib = newVAO.attribs[n];
var oldAttrib = oldVAO ? oldVAO.attribs[n] : null;
if (!oldVAO || attrib.enabled != oldAttrib.enabled) {
if (attrib.enabled) {
original.enableVertexAttribArray.call(gl, n);
} else {
original.disableVertexAttribArray.call(gl, n);
}
}
if (attrib.enabled) {
var bufferChanged = false;
if (!oldVAO || attrib.buffer != oldAttrib.buffer) {
if (currentBinding != attrib.buffer) {
original.bindBuffer.call(gl, gl.ARRAY_BUFFER, attrib.buffer);
currentBinding = attrib.buffer;
}
bufferChanged = true;
}
if (bufferChanged || attrib.cached != oldAttrib.cached) {
original.vertexAttribPointer.call(gl, n, attrib.size, attrib.type, attrib.normalized, attrib.stride, attrib.offset);
}
}
}
if (this.currentArrayBuffer != currentBinding) {
original.bindBuffer.call(gl, gl.ARRAY_BUFFER, this.currentArrayBuffer);
}
};
window.setupVertexArrayObject = function (gl) {
if (gl.getSupportedExtensions) {
var exts = gl.getSupportedExtensions();
if (exts.indexOf("OES_vertex_array_object") != -1) {
return;
}
} else if (gl.getExtension) {
var vao = gl.getExtension("OES_vertex_array_object");
if (vao) {
return;
}
}
if (gl.getSupportedExtensions) {
var original_getSupportedExtensions = gl.getSupportedExtensions;
gl.getSupportedExtensions = function getSupportedExtensions() {
var list = original_getSupportedExtensions.call(this) || [];
list.push("OES_vertex_array_object");
return list;
};
}
var original_getExtension = gl.getExtension;
gl.getExtension = function getExtension(name) {
if (name == "OES_vertex_array_object") {
if (!gl.__OESVertexArrayObject) {
gl.__OESVertexArrayObject = new OESVertexArrayObject(gl);
}
return gl.__OESVertexArrayObject;
}
if (original_getExtension) {
return original_getExtension.call(this, name);
} else {
return null;
}
};
};
})();
const _typeLookup = function () {
const result = {};
const names = ["Array", "Object", "Function", "Date", "RegExp", "Float32Array"];
for (let i = 0; i < names.length; i++) result["[object " + names[i] + "]"] = names[i].toLowerCase();
return result;
}();
const version = "1.41.0-dev";
const revision = "bbc2326ad";
const config = {};
const common = {};
const apps = {};
const data = {};
function type$1(obj) {
if (obj === null) {
return "null";
}
const type = typeof obj;
if (type === "undefined" || type === "number" || type === "string" || type === "boolean") {
return type;
}
return _typeLookup[Object.prototype.toString.call(obj)];
}
function extend(target, ex) {
for (const prop in ex) {
const copy = ex[prop];
if (type$1(copy) === "object") {
target[prop] = extend({}, copy);
} else if (type$1(copy) === "array") {
target[prop] = extend([], copy);
} else {
target[prop] = copy;
}
}
return target;
}
function isDefined(o) {
let a;
return o !== a;
}
let table = null;
let row = null;
let title = null;
let field = null;
function init() {
table = document.createElement('table');
row = document.createElement('tr');
title = document.createElement('td');
field = document.createElement('td');
table.style.cssText = 'position:absolute;font-family:sans-serif;font-size:12px;color:#cccccc';
table.style.top = '0px';
table.style.left = '0px';
table.style.border = 'thin solid #cccccc';
document.body.appendChild(table);
}
const debug = {
display: function (data) {
if (!table) {
init();
}
table.innerHTML = '';
for (const key in data) {
const r = row.cloneNode();
const t = title.cloneNode();
const f = field.cloneNode();
t.textContent = key;
f.textContent = data[key];
r.appendChild(t);
r.appendChild(f);
table.appendChild(r);
}
}
};
class EventHandler {
constructor() {
this.initEventHandler();
}
initEventHandler() {
this._callbacks = {};
this._callbackActive = {};
}
_addCallback(name, callback, scope, once = false) {
if (!name || typeof name !== 'string' || !callback) return;
if (!this._callbacks[name]) this._callbacks[name] = [];
if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name]) this._callbackActive[name] = this._callbackActive[name].slice();
this._callbacks[name].push({
callback: callback,
scope: scope || this,
once: once
});
}
on(name, callback, scope) {
this._addCallback(name, callback, scope, false);
return this;
}
off(name, callback, scope) {
if (name) {
if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name]) this._callbackActive[name] = this._callbackActive[name].slice();
} else {
for (const key in this._callbackActive) {
if (!this._callbacks[key]) continue;
if (this._callbacks[key] !== this._callbackActive[key]) continue;
this._callbackActive[key] = this._callbackActive[key].slice();
}
}
if (!name) {
this._callbacks = {};
} else if (!callback) {
if (this._callbacks[name]) this._callbacks[name] = [];
} else {
const events = this._callbacks[name];
if (!events) return this;
let count = events.length;
for (let i = 0; i < count; i++) {
if (events[i].callback !== callback) continue;
if (scope && events[i].scope !== scope) continue;
events[i--] = events[--count];
}
events.length = count;
}
return this;
}
fire(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
if (!name || !this._callbacks[name]) return this;
let callbacks;
if (!this._callbackActive[name]) {
this._callbackActive[name] = this._callbacks[name];
} else {
if (this._callbackActive[name] === this._callbacks[name]) this._callbackActive[name] = this._callbackActive[name].slice();
callbacks = this._callbacks[name].slice();
}
for (let i = 0; (callbacks || this._callbackActive[name]) && i < (callbacks || this._callbackActive[name]).length; i++) {
const evt = (callbacks || this._callbackActive[name])[i];
evt.callback.call(evt.scope, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
if (evt.once) {
const ind = this._callbacks[name].indexOf(evt);
if (ind !== -1) {
if (this._callbackActive[name] === this._callbacks[name]) this._callbackActive[name] = this._callbackActive[name].slice();
this._callbacks[name].splice(ind, 1);
}
}
}
if (!callbacks) this._callbackActive[name] = null;
return this;
}
once(name, callback, scope) {
this._addCallback(name, callback, scope, true);
return this;
}
hasEvent(name) {
return this._callbacks[name] && this._callbacks[name].length !== 0 || false;
}
}
const events = {
attach: function (target) {
const ev = events;
target._addCallback = ev._addCallback;
target.on = ev.on;
target.off = ev.off;
target.fire = ev.fire;
target.once = ev.once;
target.hasEvent = ev.hasEvent;
target._callbacks = {};
target._callbackActive = {};
return target;
},
_addCallback: EventHandler.prototype._addCallback,
on: EventHandler.prototype.on,
off: EventHandler.prototype.off,
fire: EventHandler.prototype.fire,
once: EventHandler.prototype.once,
hasEvent: EventHandler.prototype.hasEvent
};
const guid = {
create: function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}
};
const path = {
delimiter: "/",
join: function () {
const num = arguments.length;
let result = arguments[0];
for (let index = 0; index < num - 1; ++index) {
const one = arguments[index];
const two = arguments[index + 1];
if (!isDefined(one) || !isDefined(two)) {
throw new Error("undefined argument to pc.path.join");
}
if (two[0] === path.delimiter) {
result = two;
continue;
}
if (one && two && one[one.length - 1] !== path.delimiter && two[0] !== path.delimiter) {
result += path.delimiter + two;
} else {
result += two;
}
}
return result;
},
normalize: function (pathname) {
const lead = pathname.startsWith(path.delimiter);
const trail = pathname.endsWith(path.delimiter);
const parts = pathname.split('/');
let result = '';
let cleaned = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i] === '') continue;
if (parts[i] === '.') continue;
if (parts[i] === '..' && cleaned.length > 0) {
cleaned = cleaned.slice(0, cleaned.length - 2);
continue;
}
if (i > 0) cleaned.push(path.delimiter);
cleaned.push(parts[i]);
}
result = cleaned.join('');
if (!lead && result[0] === path.delimiter) {
result = result.slice(1);
}
if (trail && result[result.length - 1] !== path.delimiter) {
result += path.delimiter;
}
return result;
},
split: function (pathname) {
const parts = pathname.split(path.delimiter);
const tail = parts.slice(parts.length - 1)[0];
const head = parts.slice(0, parts.length - 1).join(path.delimiter);
return [head, tail];
},
getBasename: function (pathname) {
return path.split(pathname)[1];
},
getDirectory: function (pathname) {
const parts = pathname.split(path.delimiter);
return parts.slice(0, parts.length - 1).join(path.delimiter);
},
getExtension: function (pathname) {
const ext = pathname.split('?')[0].split('.').pop();
if (ext !== pathname) {
return "." + ext;
}
return "";
},
isRelativePath: function (pathname) {
return pathname.charAt(0) !== "/" && pathname.match(/:\/\//) === null;
},
extractPath: function (pathname) {
let result = "";
const parts = pathname.split("/");
let i = 0;
if (parts.length > 1) {
if (path.isRelativePath(pathname)) {
if (parts[0] === ".") {
for (i = 0; i < parts.length - 1; ++i) {
result += i === 0 ? parts[i] : "/" + parts[i];
}
} else if (parts[0] === "..") {
for (i = 0; i < parts.length - 1; ++i) {
result += i === 0 ? parts[i] : "/" + parts[i];
}
} else {
result = ".";
for (i = 0; i < parts.length - 1; ++i) {
result += "/" + parts[i];
}
}
} else {
for (i = 0; i < parts.length - 1; ++i) {
result += i === 0 ? parts[i] : "/" + parts[i];
}
}
}
return result;
}
};
let desktop = false;
let mobile = false;
let windows = false;
let xbox = false;
let android = false;
let ios = false;
let touch = false;
let gamepads = false;
let workers = false;
let passiveEvents = false;
if (typeof navigator !== 'undefined') {
const ua = navigator.userAgent;
if (/(windows|mac os|linux|cros)/i.test(ua)) desktop = true;
if (/xbox/i.test(ua)) xbox = true;
if (/(windows phone|iemobile|wpdesktop)/i.test(ua)) {
desktop = false;
mobile = true;
windows = true;
} else if (/android/i.test(ua)) {
desktop = false;
mobile = true;
android = true;
} else if (/ip([ao]d|hone)/i.test(ua)) {
desktop = false;
mobile = true;
ios = true;
}
if (typeof window !== 'undefined') {
touch = 'ontouchstart' in window || 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
}
gamepads = 'getGamepads' in navigator;
workers = typeof Worker !== 'undefined';
try {
const opts = Object.defineProperty({}, 'passive', {
get: function () {
passiveEvents = true;
return false;
}
});
window.addEventListener("testpassive", null, opts);
window.removeEventListener("testpassive", null, opts);
} catch (e) {}
}
const platform = {
desktop: desktop,
mobile: mobile,
ios: ios,
android: android,
windows: windows,
xbox: xbox,
gamepads: gamepads,
touch: touch,
workers: workers,
passiveEvents: passiveEvents
};
const ASCII_LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
const ASCII_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const ASCII_LETTERS = ASCII_LOWERCASE + ASCII_UPPERCASE;
const HIGH_SURROGATE_BEGIN = 0xD800;
const HIGH_SURROGATE_END = 0xDBFF;
const LOW_SURROGATE_BEGIN = 0xDC00;
const LOW_SURROGATE_END = 0xDFFF;
const ZERO_WIDTH_JOINER = 0x200D;
const REGIONAL_INDICATOR_BEGIN = 0x1F1E6;
const REGIONAL_INDICATOR_END = 0x1F1FF;
const FITZPATRICK_MODIFIER_BEGIN = 0x1F3FB;
const FITZPATRICK_MODIFIER_END = 0x1F3FF;
const DIACRITICAL_MARKS_BEGIN = 0x20D0;
const DIACRITICAL_MARKS_END = 0x20FF;
const VARIATION_MODIFIER_BEGIN = 0xFE00;
const VARIATION_MODIFIER_END = 0xFE0F;
function getCodePointData(string, i = 0) {
const size = string.length;
if (i < 0 || i >= size) {
return null;
}
const first = string.charCodeAt(i);
if (size > 1 && first >= HIGH_SURROGATE_BEGIN && first <= HIGH_SURROGATE_END) {
const second = string.charCodeAt(i + 1);
if (second >= LOW_SURROGATE_BEGIN && second <= LOW_SURROGATE_END) {
return {
code: (first - HIGH_SURROGATE_BEGIN) * 0x400 + second - LOW_SURROGATE_BEGIN + 0x10000,
long: true
};
}
}
return {
code: first,
long: false
};
}
function isCodeBetween(string, begin, end) {
if (!string) return false;
const codeData = getCodePointData(string);
if (codeData) {
const code = codeData.code;
return code >= begin && code <= end;
}
return false;
}
function numCharsToTakeForNextSymbol(string, index) {
if (index === string.length - 1) {
return 1;
}
if (isCodeBetween(string[index], HIGH_SURROGATE_BEGIN, HIGH_SURROGATE_END)) {
const first = string.substring(index, index + 2);
const second = string.substring(index + 2, index + 4);
if (isCodeBetween(second, FITZPATRICK_MODIFIER_BEGIN, FITZPATRICK_MODIFIER_END) || isCodeBetween(first, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END) && isCodeBetween(second, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END)) {
return 4;
}
if (isCodeBetween(second, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {
return 3;
}
return 2;
}
if (isCodeBetween(string[index + 1], VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {
return 2;
}
return 1;
}
const string = {
ASCII_LOWERCASE: ASCII_LOWERCASE,
ASCII_UPPERCASE: ASCII_UPPERCASE,
ASCII_LETTERS: ASCII_LETTERS,
format: function (s) {
for (let i = 1; i < arguments.length; i++) {
s = s.replace('{' + (i - 1) + '}', arguments[i]);
}
return s;
},
toBool: function (s, strict = false) {
if (s === 'true') {
return true;
}
if (strict) {
if (s === 'false') {
return false;
}
throw new TypeError('Not a boolean string');
}
return false;
},
getCodePoint: function (string, i) {
const codePointData = getCodePointData(string, i);
return codePointData && codePointData.code;
},
getCodePoints: function (string) {
if (typeof string !== 'string') {
throw new TypeError('Not a string');
}
let i = 0;
const arr = [];
let codePoint;
while (!!(codePoint = getCodePointData(string, i))) {
arr.push(codePoint.code);
i += codePoint.long ? 2 : 1;
}
return arr;
},
getSymbols: function (string) {
if (typeof string !== 'string') {
throw new TypeError('Not a string');
}
let index = 0;
const length = string.length;
const output = [];
let take = 0;
let ch;
while (index < length) {
take += numCharsToTakeForNextSymbol(string, index + take);
ch = string[index + take];
if (isCodeBetween(ch, DIACRITICAL_MARKS_BEGIN, DIACRITICAL_MARKS_END)) {
ch = string[index + take++];
}
if (isCodeBetween(ch, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {
ch = string[index + take++];
}
if (ch && ch.charCodeAt(0) === ZERO_WIDTH_JOINER) {
ch = string[index + take++];
continue;
}
const char = string.substring(index, index + take);
output.push(char);
index += take;
take = 0;
}
return output;
},
fromCodePoint: function () {
const chars = [];
let current;
let codePoint;
let units;
for (let i = 0; i < arguments.length; ++i) {
current = Number(arguments[i]);
codePoint = current - 0x10000;
units = current > 0xFFFF ? [(codePoint >> 10) + 0xD800, codePoint % 0x400 + 0xDC00] : [current];
chars.push(String.fromCharCode.apply(null, units));
}
return chars.join('');
}
};
class IndexedList {
constructor() {
this._list = [];
this._index = {};
}
push(key, item) {
if (this._index[key]) {
throw Error("Key already in index " + key);
}
const location = this._list.push(item) - 1;
this._index[key] = location;
}
has(key) {
return this._index[key] !== undefined;
}
get(key) {
const location = this._index[key];
if (location !== undefined) {
return this._list[location];
}
return null;
}
remove(key) {
const location = this._index[key];
if (location !== undefined) {
this._list.splice(location, 1);
delete this._index[key];
for (key in this._index) {
const idx = this._index[key];
if (idx > location) {
this._index[key] = idx - 1;
}
}
return true;
}
return false;
}
list() {
return this._list;
}
clear() {
this._list.length = 0;
for (const prop in this._index) {
delete this._index[prop];
}
}
}
class SortedLoopArray {
constructor(args) {
this._sortBy = args.sortBy;
this.items = [];
this.length = 0;
this.loopIndex = -1;
this._sortHandler = this._doSort.bind(this);
}
_binarySearch(item) {
let left = 0;
let right = this.items.length - 1;
const search = item[this._sortBy];
let middle;
let current;
while (left <= right) {
middle = Math.floor((left + right) / 2);
current = this.items[middle][this._sortBy];
if (current <= search) {
left = middle + 1;
} else if (current > search) {
right = middle - 1;
}
}
return left;
}
_doSort(a, b) {
const sortBy = this._sortBy;
return a[sortBy] - b[sortBy];
}
insert(item) {
const index = this._binarySearch(item);
this.items.splice(index, 0, item);
this.length++;
if (this.loopIndex >= index) {
this.loopIndex++;
}
}
append(item) {
this.items.push(item);
this.length++;
}
remove(item) {
const idx = this.items.indexOf(item);
if (idx < 0) return;
this.items.splice(idx, 1);
this.length--;
if (this.loopIndex >= idx) {
this.loopIndex--;
}
}
sort() {
const current = this.loopIndex >= 0 ? this.items[this.loopIndex] : null;
this.items.sort(this._sortHandler);
if (current !== null) {
this.loopIndex = this.items.indexOf(current);
}
}
}
class Tags extends EventHandler {
constructor(parent) {
super();
this._index = {};
this._list = [];
this._parent = parent;
}
add() {
let changed = false;
const tags = this._processArguments(arguments, true);
if (!tags.length) return changed;
for (let i = 0; i < tags.length; i++) {
if (this._index[tags[i]]) continue;
changed = true;
this._index[tags[i]] = true;
this._list.push(tags[i]);
this.fire('add', tags[i], this._parent);
}
if (changed) this.fire('change', this._parent);
return changed;
}
remove() {
let changed = false;
if (!this._list.length) return changed;
const tags = this._processArguments(arguments, true);
if (!tags.length) return changed;
for (let i = 0; i < tags.length; i++) {
if (!this._index[tags[i]]) continue;
changed = true;
delete this._index[tags[i]];
this._list.splice(this._list.indexOf(tags[i]), 1);
this.fire('remove', tags[i], this._parent);
}
if (changed) this.fire('change', this._parent);
return changed;
}
clear() {
if (!this._list.length) return;
const tags = this._list.slice(0);
this._list = [];
this._index = {};
for (let i = 0; i < tags.length; i++) this.fire('remove', tags[i], this._parent);
this.fire('change', this._parent);
}
has() {
if (!this._list.length) return false;
return this._has(this._processArguments(arguments));
}
_has(tags) {
if (!this._list.length || !tags.length) return false;
for (let i = 0; i < tags.length; i++) {
if (tags[i].length === 1) {
if (this._index[tags[i][0]]) return true;
} else {
let multiple = true;
for (let t = 0; t < tags[i].length; t++) {
if (this._index[tags[i][t]]) continue;
multiple = false;
break;
}
if (multiple) return true;
}
}
return false;
}
list() {
return this._list.slice(0);
}
_processArguments(args, flat) {
const tags = [];
let tmp = [];
if (!args || !args.length) return tags;
for (let i = 0; i < args.length; i++) {
if (args[i] instanceof Array) {
if (!flat) tmp = [];
for (let t = 0; t < args[i].length; t++) {
if (typeof args[i][t] !== 'string') continue;
if (flat) {
tags.push(args[i][t]);
} else {
tmp.push(args[i][t]);
}
}
if (!flat && tmp.length) tags.push(tmp);
} else if (typeof args[i] === 'string') {
if (flat) {
tags.push(args[i]);
} else {
tags.push([args[i]]);
}
}
}
return tags;
}
get size() {
return this._list.length;
}
}
const now = typeof window !== 'undefined' && window.performance && window.performance.now && window.performance.timing ? function () {
return window.performance.now();
} : Date.now;
class Timer {
constructor() {
this._isRunning = false;
this._a = 0;
this._b = 0;
}
start() {
this._isRunning = true;
this._a = now();
}
stop() {
this._isRunning = false;
this._b = now();
}
getMilliseconds() {
return this._b - this._a;
}
}
function createURI(options) {
let s = "";
if ((options.authority || options.scheme) && (options.host || options.hostpath)) {
throw new Error("Can't have 'scheme' or 'authority' and 'host' or 'hostpath' option");
}
if (options.host && options.hostpath) {
throw new Error("Can't have 'host' and 'hostpath' option");
}
if (options.path && options.hostpath) {
throw new Error("Can't have 'path' and 'hostpath' option");
}
if (options.scheme) {
s += options.scheme + ":";
}
if (options.authority) {
s += "//" + options.authority;
}
if (options.host) {
s += options.host;
}
if (options.path) {
s += options.path;
}
if (options.hostpath) {
s += options.hostpath;
}
if (options.query) {
s += "?" + options.query;
}
if (options.fragment) {
s += "#" + options.fragment;
}
return s;
}
const re = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
class URI {
constructor(uri) {
const result = uri.match(re);
this.scheme = result[2];
this.authority = result[4];
this.path = result[5];
this.query = result[7];
this.fragment = result[9];
}
toString() {
let s = "";
if (this.scheme) {
s += this.scheme + ":";
}
if (this.authority) {
s += "//" + this.authority;
}
s += this.path;
if (this.query) {
s += "?" + this.query;
}
if (this.fragment) {
s += "#" + this.fragment;
}
return s;
}
getQuery() {
const result = {};
if (this.query) {
const queryParams = decodeURIComponent(this.query).split("&");
for (const queryParam of queryParams) {
const pair = queryParam.split("=");
result[pair[0]] = pair[1];
}
}
return result;
}
setQuery(params) {
let q = "";
for (const key in params) {
if (params.hasOwnProperty(key)) {
if (q !== "") {
q += "&";
}
q += encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
}
}
this.query = q;
}
}
const math = {
DEG_TO_RAD: Math.PI / 180,
RAD_TO_DEG: 180 / Math.PI,
clamp: function (value, min, max) {
if (value >= max) return max;
if (value <= min) return min;
return value;
},
intToBytes24: function (i) {
const r = i >> 16 & 0xff;
const g = i >> 8 & 0xff;
const b = i & 0xff;
return [r, g, b];
},
intToBytes32: function (i) {
const r = i >> 24 & 0xff;
const g = i >> 16 & 0xff;
const b = i >> 8 & 0xff;
const a = i & 0xff;
return [r, g, b, a];
},
bytesToInt24: function (r, g, b) {
if (r.length) {
b = r[2];
g = r[1];
r = r[0];
}
return r << 16 | g << 8 | b;
},
bytesToInt32: function (r, g, b, a) {
if (r.length) {
a = r[3];
b = r[2];
g = r[1];
r = r[0];
}
return (r << 24 | g << 16 | b << 8 | a) >>> 32;
},
lerp: function (a, b, alpha) {
return a + (b - a) * math.clamp(alpha, 0, 1);
},
lerpAngle: function (a, b, alpha) {
if (b - a > 180) {
b -= 360;
}
if (b - a < -180) {
b += 360;
}
return math.lerp(a, b, math.clamp(alpha, 0, 1));
},
powerOfTwo: function (x) {
return x !== 0 && !(x & x - 1);
},
nextPowerOfTwo: function (val) {
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
val++;
return val;
},
random: function (min, max) {
const diff = max - min;
return Math.random() * diff + min;
},
smoothstep: function (min, max, x) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * (3 - 2 * x);
},
smootherstep: function (min, max, x) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * x * (x * (x * 6 - 15) + 10);
},
roundUp: function (numToRound, multiple) {
if (multiple === 0) return numToRound;
return Math.ceil(numToRound / multiple) * multiple;
},
float2Half: function () {
const floatView = new Float32Array(1);
const int32View = new Int32Array(floatView.buffer);
return function (val) {
floatView[0] = val;
const x = int32View[0];
let bits = x >> 16 & 0x8000;
let m = x >> 12 & 0x07ff;
const e = x >> 23 & 0xff;
if (e < 103) {
return bits;
}
if (e > 142) {
bits |= 0x7c00;
bits |= (e === 255 ? 0 : 1) && x & 0x007fffff;
return bits;
}
if (e < 113) {
m |= 0x0800;
bits |= (m >> 114 - e) + (m >> 113 - e & 1);
return bits;
}
bits |= e - 112 << 10 | m >> 1;
bits += m & 1;
return bits;
};
}(),
between: function (num, a, b, inclusive) {
const min = Math.min(a, b);
const max = Math.max(a, b);
return inclusive ? num >= min && num <= max : num > min && num < max;
}
};
class Http {
constructor() {
this.ContentType = Http.ContentType;
this.ResponseType = Http.ResponseType;
this.binaryExtensions = Http.binaryExtensions;
}
get(url, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
return this.request("GET", url, options, callback);
}
post(url, data, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
options.postdata = data;
return this.request("POST", url, options, callback);
}
put(url, data, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
options.postdata = data;
return this.request("PUT", url, options, callback);
}
del(url, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
return this.request("DELETE", url, options, callback);
}
request(method, url, options, callback) {
var uri, query, timestamp, postdata, xhr;
var errored = false;
if (typeof options === "function") {
callback = options;
options = {};
}
if (options.retry) {
options = Object.assign({
retries: 0,
maxRetries: 5
}, options);
}
options.callback = callback;
if (options.async == null) {
options.async = true;
}
if (options.headers == null) {
options.headers = {};
}
if (options.postdata != null) {
if (options.postdata instanceof Document) {
postdata = options.postdata;
} else if (options.postdata instanceof FormData) {
postdata = options.postdata;
} else if (options.postdata instanceof Object) {
var contentType = options.headers["Content-Type"];
if (contentType === undefined) {
options.headers["Content-Type"] = Http.ContentType.FORM_URLENCODED;
contentType = options.headers["Content-Type"];
}
switch (contentType) {
case Http.ContentType.FORM_URLENCODED:
postdata = "";
var bFirstItem = true;
for (var key in options.postdata) {
if (options.postdata.hasOwnProperty(key)) {
if (bFirstItem) {
bFirstItem = false;
} else {
postdata += "&";
}
postdata += escape(key) + "=" + escape(options.postdata[key]);
}
}
break;
default:
case Http.ContentType.JSON:
if (contentType == null) {
options.headers["Content-Type"] = Http.ContentType.JSON;
}
postdata = JSON.stringify(options.postdata);
break;
}
} else {
postdata = options.postdata;
}
}
if (options.cache === false) {
timestamp = now();
uri = new URI(url);
if (!uri.query) {
uri.query = "ts=" + timestamp;
} else {
uri.query = uri.query + "&ts=" + timestamp;
}
url = uri.toString();
}
if (options.query) {
uri = new URI(url);
query = extend(uri.getQuery(), options.query);
uri.setQuery(query);
url = uri.toString();
}
xhr = new XMLHttpRequest();
xhr.open(method, url, options.async);
xhr.withCredentials = options.withCredentials !== undefined ? options.withCredentials : false;
xhr.responseType = options.responseType || this._guessResponseType(url);
for (var header in options.headers) {
if (options.headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header, options.headers[header]);
}
}
xhr.onreadystatechange = function () {
this._onReadyStateChange(method, url, options, xhr);
}.bind(this);
xhr.onerror = function () {
this._onError(method, url, options, xhr);
errored = true;
}.bind(this);
try {
xhr.send(postdata);
} catch (e) {
if (!errored) {
options.error(xhr.status, xhr, e);
}
}
return xhr;
}
_guessResponseType(url) {
var uri = new URI(url);
var ext = path.getExtension(uri.path);
if (Http.binaryExtensions.indexOf(ext) >= 0) {
return Http.ResponseType.ARRAY_BUFFER;
}
if (ext === ".xml") {
return Http.ResponseType.DOCUMENT;
}
return Http.ResponseType.TEXT;
}
_isBinaryContentType(contentType) {
var binTypes = [Http.ContentType.MP4, Http.ContentType.WAV, Http.ContentType.OGG, Http.ContentType.MP3, Http.ContentType.BIN, Http.ContentType.DDS, Http.ContentType.BASIS, Http.ContentType.GLB];
if (binTypes.indexOf(contentType) >= 0) {
return true;
}
return false;
}
_onReadyStateChange(method, url, options, xhr) {
if (xhr.readyState === 4) {
switch (xhr.status) {
case 0:
{
if (xhr.responseURL && xhr.responseURL.startsWith('file:///')) {
this._onSuccess(method, url, options, xhr);
} else {
this._onError(method, url, options, xhr);
}
break;
}
case 200:
case 201:
case 206:
case 304:
{
this._onSuccess(method, url, options, xhr);
break;
}
default:
{
this._onError(method, url, options, xhr);
break;
}
}
}
}
_onSuccess(method, url, options, xhr) {
var response;
var header;
var contentType;
var parts;
header = xhr.getResponseHeader("Content-Type");
if (header) {
parts = header.split(";");
contentType = parts[0].trim();
}
try {
if (contentType === this.ContentType.JSON || url.split('?')[0].endsWith(".json")) {
response = JSON.parse(xhr.responseText);
} else if (this._isBinaryContentType(contentType)) {
response = xhr.response;
} else {
if (contentType) {
console.warn("responseType: " + xhr.responseType + " being served with Content-Type: " + contentType);
}
if (xhr.responseType === Http.ResponseType.ARRAY_BUFFER) {
response = xhr.response;
} else if (xhr.responseType === Http.ResponseType.BLOB || xhr.responseType === Http.ResponseType.JSON) {
response = xhr.response;
} else {
if (xhr.responseType === Http.ResponseType.DOCUMENT || contentType === this.ContentType.XML) {
response = xhr.responseXML;
} else {
response = xhr.responseText;
}
}
}
options.callback(null, response);
} catch (err) {
options.callback(err);
}
}
_onError(method, url, options, xhr) {
if (options.retrying) {
return;
}
if (options.retry && options.retries < options.maxRetries) {
options.retries++;
options.retrying = true;
var retryDelay = math.clamp(Math.pow(2, options.retries) * Http.retryDelay, 0, options.maxRetryDelay || 5000);
console.log(method + ': ' + url + ' - Error ' + xhr.status + '. Retrying in ' + retryDelay + ' ms');
setTimeout(function () {
options.retrying = false;
this.request(method, url, options, options.callback);
}.bind(this), retryDelay);
} else {
options.callback(xhr.status === 0 ? 'Network error' : xhr.status, null);
}
}
}
Http.ContentType = {
FORM_URLENCODED: "application/x-www-form-urlencoded",
GIF: "image/gif",
JPEG: "image/jpeg",
DDS: "image/dds",
JSON: "application/json",
PNG: "image/png",
TEXT: "text/plain",
XML: "application/xml",
WAV: "audio/x-wav",
OGG: "audio/ogg",
MP3: "audio/mpeg",
MP4: "audio/mp4",
AAC: "audio/aac",
BIN: "application/octet-stream",
BASIS: "image/basis",
GLB: "model/gltf-binary"
};
Http.ResponseType = {
TEXT: 'text',
ARRAY_BUFFER: 'arraybuffer',
BLOB: 'blob',
DOCUMENT: 'document',
JSON: 'json'
};
Http.binaryExtensions = ['.model', '.wav', '.ogg', '.mp3', '.mp4', '.m4a', '.aac', '.dds', '.basis', '.glb'];
Http.retryDelay = 100;
const http = new Http();
const CURVE_LINEAR = 0;
const CURVE_SMOOTHSTEP = 1;
const CURVE_CATMULL = 2;
const CURVE_CARDINAL = 3;
const CURVE_SPLINE = 4;
const CURVE_STEP = 5;
class Color {
constructor(r = 0, g = 0, b = 0, a = 1) {
const length = r.length;
if (length === 3 || length === 4) {
this.r = r[0];
this.g = r[1];
this.b = r[2];
this.a = r[3] !== undefined ? r[3] : 1;
} else {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
}
clone() {
return new Color(this.r, this.g, this.b, this.a);
}
copy(rhs) {
this.r = rhs.r;
this.g = rhs.g;
this.b = rhs.b;
this.a = rhs.a;
return this;
}
equals(rhs) {
return this.r === rhs.r && this.g === rhs.g && this.b === rhs.b && this.a === rhs.a;
}
set(r, g, b, a = 1) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
return this;
}
lerp(lhs, rhs, alpha) {
this.r = lhs.r + alpha * (rhs.r - lhs.r);
this.g = lhs.g + alpha * (rhs.g - lhs.g);
this.b = lhs.b + alpha * (rhs.b - lhs.b);
this.a = lhs.a + alpha * (rhs.a - lhs.a);
return this;
}
fromString(hex) {
const i = parseInt(hex.replace('#', '0x'), 16);
let bytes;
if (hex.length > 7) {
bytes = math.intToBytes32(i);
} else {
bytes = math.intToBytes24(i);
bytes[3] = 255;
}
this.set(bytes[0] / 255, bytes[1] / 255, bytes[2] / 255, bytes[3] / 255);
return this;
}
toString(alpha) {
let s = "#" + ((1 << 24) + (Math.round(this.r * 255) << 16) + (Math.round(this.g * 255) << 8) + Math.round(this.b * 255)).toString(16).slice(1);
if (alpha === true) {
const a = Math.round(this.a * 255).toString(16);
if (this.a < 16 / 255) {
s += '0' + a;
} else {
s += a;
}
}
return s;
}
}
Color.BLACK = Object.freeze(new Color(0, 0, 0, 1));
Color.BLUE = Object.freeze(new Color(0, 0, 1, 1));
Color.CYAN = Object.freeze(new Color(0, 1, 1, 1));
Color.GRAY = Object.freeze(new Color(0.5, 0.5, 0.5, 1));
Color.GREEN = Object.freeze(new Color(0, 1, 0, 1));
Color.MAGENTA = Object.freeze(new Color(1, 0, 1, 1));
Color.RED = Object.freeze(new Color(1, 0, 0, 1));
Color.WHITE = Object.freeze(new Color(1, 1, 1, 1));
Color.YELLOW = Object.freeze(new Color(1, 1, 0, 1));
class CurveEvaluator {
constructor(curve, time) {
this._curve = curve;
this._left = -Infinity;
this._right = Infinity;
this._recip = 0;
this._p0 = 0;
this._p1 = 0;
this._m0 = 0;
this._m1 = 0;
this._reset(time || 0);
}
evaluate(time, forceReset) {
if (forceReset || time < this._left || time >= this._right) {
this._reset(time);
}
let result;
const type = this._curve.type;
if (type === CURVE_STEP) {
result = this._p0;
} else {
const t = this._recip === 0 ? 0 : (time - this._left) * this._recip;
if (type === CURVE_LINEAR) {
result = math.lerp(this._p0, this._p1, t);
} else if (type === CURVE_SMOOTHSTEP) {
result = math.lerp(this._p0, this._p1, t * t * (3 - 2 * t));
} else {
result = this._evaluateHermite(this._p0, this._p1, this._m0, this._m1, t);
}
}
return result;
}
_reset(time) {
const keys = this._curve.keys;
const len = keys.length;
if (!len) {
this._left = -Infinity;
this._right = Infinity;
this._recip = 0;
this._p0 = this._p1 = this._m0 = this._m1 = 0;
} else {
if (time < keys[0][0]) {
this._left = -Infinity;
this._right = keys[0][0];
this._recip = 0;
this._p0 = this._p1 = keys[0][1];
this._m0 = this._m1 = 0;
} else if (time >= keys[len - 1][0]) {
this._left = keys[len - 1][0];
this._right = Infinity;
this._recip = 0;
this._p0 = this._p1 = keys[len - 1][1];
this._m0 = this._m1 = 0;
} else {
let index = 0;
while (time >= keys[index + 1][0]) {
index++;
}
this._left = keys[index][0];
this._right = keys[index + 1][0];
const diff = 1.0 / (this._right - this._left);
th