UNPKG

meta-next

Version:

1,929 lines (1,869 loc) 107 kB
"use strict"; (function(scope) { scope.module = { exports: {} }; scope.modules = {}; scope.modulesCached = {}; scope.modulesPath = { }; if(scope.process) { scope.process.env = { NODE_ENV: "dev" } } else { scope.process = { env: { NODE_ENV: "dev" } } } scope._inherits = function(a, b) { var protoA = a.prototype; var proto = Object.create(b.prototype); for(var key in protoA) { var param = Object.getOwnPropertyDescriptor(protoA, key); if(param.get || param.set) { Object.defineProperty(proto, key, param); } else { proto[key] = protoA[key]; } } a.prototype = proto; a.prototype.constructor = a; a.__parent = b; if(b.__inherit === undefined) { b.__inherit = {}; } b.__inherit[a.name] = a; var parent = b.__parent; while(parent) { parent.__inherit[a.name] = a; parent = parent.__parent; } } })(window || global); "use strict"; (function() { const listeners = {}; let str_fullscreen = null; let str_fullscreenEnabled = null; let str_fullscreenElement = null; let str_onfullscreenchange = null; let str_onfullscreenerror = null; let str_exitFullscreen = null; let str_requestFullscreen = null; let str_hidden = null; let str_visibilityChange = null; let str_requestPointerLock = null; let str_exitPointerLock = null; let str_onpointerlockchange = null; let str_pointerLockElement = null; const Device = { name: "Unknown", version: "0", versionBuffer: null, vendor: "", vendors: [ "", "webkit", "moz", "ms", "o" ], supports: {}, mobile: false, portrait: false, visible: true, pointerlock: false, audioFormats: [], backingStoreRatio: 1, pointerLock: function(element) { if(!Device.supports.pointerLock) { return ; } if(element) { element[str_requestPointerLock](); } else { document[str_exitPointerLock](); } }, get pointerLockElement() { if(!Device.supports.pointerLock) { return null; } return document[str_pointerLockElement]; }, set fullscreen(element) { if(Device.fullscreenEnabled) { element[str_requestFullscreen](); } else { console.warn("Device cannot use fullscreen right now."); } }, get fullscreen() { if(Device.fullscreenEnabled) { return document[str_fullscreen]; } return false; }, get fullscreenEnabled() { if(Device.supports.fullscreen && document[str_fullscreenEnabled]) { return true; } return false; }, get fullscreenElement() { if(!Device.fullscreenEnabled) { return null; } return document[str_fullscreenElement]; }, fullscreenExit: function() { if(Device.fullscreenEnabled) { document[str_exitFullscreen](); } }, on: function(event, func) { let buffer = listeners[event]; if(buffer) { buffer.push(func); } else { buffer = [ func ]; listeners[event] = buffer; } }, off: function(event, func) { const buffer = listeners[event]; if(!buffer) { return ; } const index = buffer.indexOf(func); if(index === -1) { return ; } buffer[index] = buffer[buffer.length - 1]; buffer.pop(); }, emit: function(event, arg) { const buffer = listeners[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { buffer[n](arg); } } }; const load = function() { checkBrowser(); checkMobileAgent(); checkCanvas(); checkWebGL(); checkBackingStoreRatio(); checkAudioFormats(); checkAudioAPI(); checkPageVisibility(); checkFullscreen(); checkConsoleCSS(); checkFileAPI(); checkFileSystemAPI(); checkPointerLock(); Device.supports.onloadedmetadata = (typeof window.onloadedmetadata === "object"); Device.supports.onkeyup = (typeof window.onkeyup === "object"); Device.supports.onkeydown = (typeof window.onkeydown === "object"); Device.portrait = (window.innerHeight > window.innerWidth); polyfill(); addEventListeners(); }; const checkBrowser = function() { const regexps = { "Chrome": [ /Chrome\/(\S+)/ ], "Firefox": [ /Firefox\/(\S+)/ ], "MSIE": [ /MSIE (\S+);/ ], "Opera": [ /OPR\/(\S+)/, /Opera\/.*?Version\/(\S+)/, /Opera\/(\S+)/ ], "Safari": [ /Version\/(\S+).*?Safari\// ] }; const userAgent = navigator.userAgent; let name, currRegexp, match; let numElements = 2; for(name in regexps) { while(currRegexp = regexps[name].shift()) { if(match = userAgent.match(currRegexp)) { Device.version = match[1].match(new RegExp(("[^.]+(?:\.[^.]+){0," + --numElements) + "}"))[0]; Device.name = name; const versionBuffer = Device.version.split("."); const versionBufferLength = versionBuffer.length; Device.versionBuffer = new Array(versionBufferLength); for(let n = 0; n < versionBufferLength; n++) { Device.versionBuffer[n] = parseInt(versionBuffer[n]); } break; } } } if((Device.versionBuffer === null) || (Device.name === "unknown")) { console.warn("(Device) Could not detect browser."); } else { if(((Device.name === "Chrome") || (Device.name === "Safari")) || (Device.name === "Opera")) { Device.vendor = "webkit"; } else if(Device.name === "Firefox") { Device.vendor = "moz"; } else if(Device.name === "MSIE") { Device.vendor = "ms"; } } }; const checkMobileAgent = function() { Device.mobile = (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i).test(navigator.userAgent); }; const checkBackingStoreRatio = function() { if(!Device.supports.canvas) { return ; } const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); if(ctx.backingStorePixelRatio !== undefined) { Device.backingStoreRatio = ctx.backingStorePixelRatio; } else if(ctx[Device.vendor + "BackingStorePixelRatio"]) { Device.backingStoreRatio = ctx[Device.vendor + "BackingStorePixelRatio"]; } }; const checkCanvas = function() { Device.supports.canvas = !!window.CanvasRenderingContext2D; }; const checkWebGL = function() { const canvas = document.createElement("canvas"); const context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); Device.supports.webgl = !!context; }; const checkAudioFormats = function() { const audio = document.createElement("audio"); if(audio.canPlayType("audio/mp4")) { Device.audioFormats.push("m4a"); } if(audio.canPlayType("audio/ogg")) { Device.audioFormats.push("ogg"); } if(audio.canPlayType("audio/mpeg")) { Device.audioFormats.push("mp3"); } if(audio.canPlayType("audio/wav")) { Device.audioFormats.push("wav"); } }; const checkAudioAPI = function() { if(!window.AudioContext) { window.AudioContext = ((window.webkitAudioContext || window.mozAudioContext) || window.oAudioContext) || window.msAudioContext; } if(window.AudioContext) { Device.supports.audioAPI = true; } }; const checkPageVisibility = function() { if(document.hidden !== undefined) { str_hidden = "hidden"; str_visibilityChange = "visibilitychange"; Device.supports.pageVisibility = true; } else if(document[Device.vendor + "Hidden"] !== undefined) { str_hidden = (Device.vendor + "Hidden"); str_visibilityChange = (Device.vendor + "visibilitychange"); Device.supports.pageVisibility = true; } else { Device.supports.pageVisibility = false; } }; const checkFullscreen = function() { if(document.fullscreen !== undefined) { str_fullscreen = "fullscreen"; } else if(document[Device.vendor + "IsFullScreen"] !== undefined) { str_fullscreen = (Device.vendor + "IsFullScreen"); } else if(document[Device.vendor + "Fullscreen"] !== undefined) { str_fullscreen = (Device.vendor + "Fullscreen"); } else { Device.supports.fullscreen = false; return ; } Device.supports.fullscreen = true; if(document.fullscreenEnabled !== undefined) { str_fullscreenEnabled = "fullscreenEnabled"; } else if(document[Device.vendor + "FullscreenEnabled"] !== undefined) { str_fullscreenEnabled = (Device.vendor + "FullscreenEnabled"); } if(document.fullscreenElement !== undefined) { str_fullscreenElement = "fullscreenElement"; } else if(document[Device.vendor + "FullscreenElement"] !== undefined) { str_fullscreenElement = (Device.vendor + "FullscreenElement"); } if(document.exitFullscreen !== undefined) { str_exitFullscreen = "exitFullscreen"; } else if(document[Device.vendor + "ExitFullscreen"] !== undefined) { str_exitFullscreen = (Device.vendor + "ExitFullscreen"); } if(Element.prototype.requestFullscreen !== undefined) { str_requestFullscreen = "requestFullscreen"; } else if(Element.prototype[Device.vendor + "RequestFullscreen"] !== undefined) { str_requestFullscreen = (Device.vendor + "RequestFullscreen"); } if(document.onfullscreenchange !== undefined) { str_onfullscreenchange = "fullscreenchange"; } else if(document[("on" + Device.vendor) + "fullscreenchange"] !== undefined) { str_onfullscreenchange = (Device.vendor + "fullscreenchange"); } if(document.onfullscreenerror !== undefined) { str_onfullscreenerror = "fullscreenerror"; } else if(document[("on" + Device.vendor) + "fullscreenerror"] !== undefined) { str_onfullscreenerror = (Device.vendor + "fullscreenerror"); } }; const checkConsoleCSS = function() { if(!Device.mobile && ((Device.name === "Chrome") || (Device.name === "Opera"))) { Device.supports.consoleCSS = true; } else { Device.supports.consoleCSS = false; } }; const checkFileAPI = function() { if(((window.File && window.FileReader) && window.FileList) && window.Blob) { Device.supports.fileAPI = true; } else { Device.supports.fileAPI = false; } }; const checkFileSystemAPI = function() { if(!window.requestFileSystem) { window.requestFileSystem = ((window.webkitRequestFileSystem || window.mozRequestFileSystem) || window.oRequestFileSystem) || window.msRequestFileSystem; } if(window.requestFileSystem) { Device.supports.fileSystemAPI = true; } }; const polyfill = function() { if(!Number.MAX_SAFE_INTEGER) { Number.MAX_SAFE_INTEGER = 9007199254740991; } supportConsole(); supportRequestAnimFrame(); supportPerformanceNow(); }; const supportConsole = function() { if(!window.console) { window.console = { log: function() {}, warn: function() {}, error: function() {} }; } }; const supportRequestAnimFrame = function() { if(!window.requestAnimationFrame) { window.requestAnimationFrame = (function() { return (((window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame) || window.oRequestAnimationFrame) || window.msRequestAnimationFrame) || function(callback, element) { window.setTimeout(callback, 1000 / 60); }; }) (); } }; const supportPerformanceNow = function() { if(window.performance === undefined) { window.performance = {}; } if(window.performance.now === undefined) { window.performance.now = Date.now; } }; const addEventListeners = function() { window.addEventListener("resize", onResize, false); window.addEventListener("orientationchange", onOrientationChange, false); if(Device.supports.pageVisibility) { Device.visible = !document[str_hidden]; document.addEventListener(str_visibilityChange, onVisibilityChange); } window.addEventListener("focus", onFocus); window.addEventListener("blur", onBlur); if(Device.supports.fullscreen) { document.addEventListener(str_onfullscreenchange, onFullscreenChange); document.addEventListener(str_onfullscreenerror, onFullscreenError); } if(Device.supports.pointerLock) { document.addEventListener(str_onpointerlockchange, onPointerLockChange); } }; const onResize = function(domEvent) { Device.emit("resize", window); if(window.innerHeight > window.innerWidth) { if(!Device.portrait) { Device.portrait = true; Device.emit("portrait", true); } } else if(Device.portrait) { Device.portrait = false; Device.emit("portrait", false); } }; const onOrientationChange = function(domEvent) { Device.emit("resize", window); if(window.innerHeight > window.innerWidth) { Device.portrait = true; Device.emit("portrait", true); } else { Device.portrait = false; Device.emit("portrait", false); } }; const onFocus = function(domEvent) { Device.visible = true; Device.emit("visibile", true); }; const onBlur = function(domEvent) { Device.visible = false; Device.emit("visibile", false); }; const onVisibilityChange = function(domEvent) { Device.visible = !document[str_hidden]; Device.emit("visibile", Device.visible); }; const onFullscreenChange = function(domEvent) { Device.emit("fullscreen", Device.fullscreenElement); }; const onFullscreenError = function(domEvent) { console.error("Fullscreen denied."); }; const onPointerLockChange = function(domEvent) {}; const checkPointerLock = function() { const canvas = HTMLCanvasElement.prototype; if(canvas.requestPointerLock !== undefined) { str_requestPointerLock = "requestPointerLock"; } else if(document[Device.vendor + "RequestPointerLock"] !== undefined) { str_requestPointerLock = (Device.vendor + "RequestPointerLock"); } else { return ; } Device.supports.pointerLock = true; if(document.exitPointerLock !== undefined) { str_exitPointerLock = "exitPointerLock"; } else if(document[Device.vendor + "ExitPointerLock"] !== undefined) { str_exitPointerLock = (Device.vendor + "ExitPointerLock"); } if(document.onpointerlockchange !== undefined) { str_onpointerlockchange = "pointerlockchange"; } else if(document[("on" + Device.vendor) + "pointerlockchange"] !== undefined) { str_onpointerlockchange = (Device.vendor + "pointerlockchange"); } if(document.pointerLockElement !== undefined) { str_pointerLockElement = "pointerLockElement"; } else if(document[Device.vendor + "PointerLockElement"] !== undefined) { str_pointerLockElement = (Device.vendor + "PointerLockElement"); } }; load(); modules[1] = Device; })(); //# sourceURL=src/Device.js "use strict"; (function() { const watchers = {}; const Engine = { app: null, container: null, canvas: null, gl: null, window: null, camera: null, defaultSettings: { width: 0, height: 0, antialias: true, upscale: true }, on: function(event, func) { const buffer = watchers[event]; if(!buffer) { watchers[event] = [ func ]; } else { buffer.push(func); } }, off: function(event, func) { const buffer = watchers[event]; if(!buffer) { return ; } const index = buffer.indexOf(func); if(index === -1) { return ; } buffer[index] = buffer[buffer.length - 1]; buffer.pop(); }, emit: function(event) { const buffer = watchers[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { buffer[n](); } } }; modules[2] = Engine; })(); //# sourceURL=src/Engine.js "use strict"; (function() { let timerId = 0; function Time() { this.delta = 0; this.deltaF = 0; this.deltaRender = 0; this.deltaRenderF = 0; this.maxDelta = 250; this.scale = 1; this.fps = 0; this.current = Date.now(); this.prev = this.current; this.currentRender = this.current; this.prevRender = this.current; this.accumulator = 0; this.frameIndex = 0; this.updateFreq = (1000 / 10); this.timers = []; this.timersRemove = []; this.paused = false; this.updating = false; this._fpsCurrent = this.current; this._fps = 0; } Time.prototype = { start: function() { this.current = Date.now(); if(this.paused) { this.delta = 0; this.deltaF = 0; } else { this.delta = (this.current - this.prev); if(this.delta > this.maxDelta) { this.delta = this.maxDelta; } this.delta *= this.scale; this.deltaF = (this.delta / 1000); } this.updating = true; for(let n = 0; n < this.timers.length; n++) { this.timers[n].update(this.delta); } this.updating = false; if(this.timersRemove.length > 0) { for(let n = 0; n < this.timersRemove.length; n++) { const timerA = this.timersRemove[n]; const timerB = this.timers[this.timers.length - 1]; timerB.__index = timerA.__index; timerA.__index = -1; this.timers[timerB.__index] = timerB; this.timers.pop(); } this.timersRemove.length = 0; } }, end: function() { this.prev = this.current; }, startRender: function() { this.frameIndex++; this.currentRender = Date.now(); if(this.paused) { this.deltaRender = 0; this.deltaRenderF = 0; } else { this.deltaRender = (this.currentRender - this.prevRender); this.deltaRender *= this.scale; this.deltaRenderF = (this.deltaRender / 1000); this.accumulator += this.deltaRender; } if((this.currentRender - this._fpsCurrent) >= 1000) { this._fpsCurrent = this.currentRender; this.fps = this._fps; this._fps = 0; } }, endRender: function() { this._fps++; this.prevRender = this.currentRender; }, timer: function(func, tDelta, numTimes) { if(!func || !tDelta) { console.warn("(Timer.create) Invalid params passed"); return null; } const timer = new Timer(this, func, tDelta, numTimes); timer.play(); return timer; } }; function Timer(time, func, tDelta, numTimes) { this.time = time; this.id = timerId++; this.func = func; this.tDelta = tDelta; this.numTimes = (numTimes !== undefined ? numTimes : -1); this.initNumTimes = this.numTimes; this.onDone = null; this.tAccumulator = 0; this.tStart = Date.now(); this.paused = false; this.__index = -1; } Timer.prototype = { play: function() { if(this.__index !== -1) { return ; } this.__index = (this.time.timers.push(this) - 1); }, _stop: function() { if(this.__index === -1) { return ; } if(this.updating) { this.time.timersRemove.push(this); } else { const timers = this.time.timers; const timer = timers[timers.length - 1]; timer.__index = this.__index; timers[this.__index] = timer; timers.pop(); } this.__index = -1; }, stop: function() { this._stop(); this.paused = false; this.numTimes = 0; if(this.onDone) { this.onDone(this); } }, pause: function() { this._stop(); this.paused = true; }, resume: function() { if(!this.paused) { return ; } this.paused = false; this.tStart = Date.now(); }, reset: function() { this.tAccumulator = 0; this.numTimes = this.initNumTimes; this.paused = false; this.play(); }, update: function(tDelta) { this.tAccumulator += tDelta; while(this.tAccumulator >= this.tDelta) { this.tAccumulator -= this.tDelta; if(this.numTimes !== 0) { this.func(this); } this.tStart += this.tDelta; if(this.numTimes !== -1) { this.numTimes--; if(this.numTimes <= 0) { this.stop(); } } } } }; const instance = new Time(); modules[4] = instance; })(); //# sourceURL=src/Time.js "use strict"; (function() { function Resources() { this.resources = {}; this.watchers = {}; this.loading = false; this.numToLoad = 0; } Resources.prototype = { load: function(config) { const classes = this.Resource.__inherit; for(let key in config) { if(this.resources[key]) { console.warn("(Resources.load) There is already resource with id: " + key + ""); continue; } const itemConfig = config[key]; const cls = classes[itemConfig.type]; if(!cls) { console.warn("(Resources.load) No such resoruce type registered: " + itemConfig.type + ""); continue; } const resource = new cls(itemConfig); this.resources[key] = resource; } }, resourceLoading: function(resource) { if(this.numToLoad === 0) { this.loading = true; this.emit("loading"); } this.numToLoad++; }, resourceLoaded: function(resource) { this.numToLoad--; if(this.numToLoad === 0) { this.loading = false; this.emit("ready"); } }, get: function(id) { const resource = this.resources[id]; return resource || null; }, on: function(event, func) { const buffer = this.watchers[event]; if(buffer) { buffer.push(func); } else { this.watchers[event] = [ func ]; } }, off: function(event, func) { const buffer = this.watchers[event]; if(!buffer) { return ; } const index = buffer.indexOf(func); if(index === -1) { return ; } buffer[index] = buffer[buffer.length - 1]; buffer.pop(); }, emit: function(event) { const buffer = this.watchers[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { buffer[n](); } } }; const instance = new Resources(); modules[5] = instance; })(); //# sourceURL=src/resources/Resources.js "use strict"; (function() { var Engine = modules[2]; var Device = modules[1]; var Time = modules[4]; var Resources = modules[5]; function EngineWindow() { this.width = 0; this.height = 0; this.offsetLeft = 0; this.offsetTop = 0; this.ratio = 1; this.scaleX = 1; this.scaleY = 1; this._cursor = "auto"; } EngineWindow.prototype = { create: function() { const container = document.createElement("div"); container.style.cssText = "position:absolute; width:100%; height:100%; background: #222; display:flex; align-items:center; justify-content:center;"; const canvas = document.createElement("canvas"); const gl = canvas.getContext("webgl", { antialias: Engine.settings.antialias }); if(!gl) { console.error("Unable to initialize WebGL. Your browser or machine may not support it."); return ; } container.appendChild(canvas); document.body.appendChild(container); Engine.container = container; Engine.canvas = canvas; Engine.gl = gl; this.setupWebGL(); Engine.emit("setup"); if(Engine.app.setup) { Engine.app.setup(); } this.updateScreenSize(); Device.on("resize", this.updateScreenSize.bind(this)); this.readyFunc = this.ready.bind(this); this.updateFunc = this.update.bind(this); this.renderFunc = this.render.bind(this); Resources.on("ready", this.readyFunc); if(!Resources.loading) { this.ready(); } }, setupWebGL: function() { const gl = Engine.gl; gl.clearColor(0, 0, 0, 1); gl.clearDepth(1); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.enable(gl.BLEND); gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }, updateScreenSize: function() { const settings = Engine.settings; const container = Engine.container; const canvas = Engine.canvas; const targetWidth = settings.width ? settings.width : window.innerWidth; const targetHeight = settings.height ? settings.height : window.innerHeight; const widthRatio = window.innerWidth / targetWidth; const heightRatio = window.innerHeight / targetHeight; const currRatio = widthRatio < heightRatio ? widthRatio : heightRatio; let ratio; if(settings.upscale) { ratio = currRatio; } else { ratio = (currRatio > 1 ? 1 : currRatio); } this.width = targetWidth; this.height = targetHeight; canvas.width = targetWidth; canvas.height = targetHeight; canvas.style.width = "" + targetWidth * ratio + "px"; canvas.style.height = "" + targetHeight * ratio + "px"; Engine.gl.viewport(0, 0, targetWidth, targetHeight); this.updateOffset(); Engine.camera.updateProjectionTransform(); }, updateOffset: function() { this.offsetLeft = 0; this.offsetTop = 0; let element = Engine.container; if(element.offsetParent) { do { this.offsetLeft += element.offsetLeft; this.offsetTop += element.offsetTop; } while(element = element.offsetParent) } let rect = Engine.container.getBoundingClientRect(); this.offsetLeft += rect.left; this.offsetTop += rect.top; rect = Engine.canvas.getBoundingClientRect(); this.offsetLeft += rect.left; this.offsetTop += rect.top; }, ready: function() { Resources.off("ready", this.readyFunc); if(Engine.app.ready) { Engine.app.ready(); } this.render(); setInterval(this.updateFunc, 1 / 60); }, update: function() { Time.start(); if(Engine.app.update) { Engine.app.update(Time.deltaF); } Time.end(); }, render: function() { const gl = Engine.gl; Time.startRender(); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); if(Engine.app.render) { Engine.app.render(); } Time.endRender(); requestAnimationFrame(this.renderFunc); }, background: function(r, g, b) { const weight = 1 / 255; Engine.gl.clearColor(r * weight, g * weight, b * weight, 1); }, set cursor(type) { if(this._cursor === type) { return ; } this._cursor = type; document.body.style.cursor = type; }, get cursor() { return this._cursor; } }; modules[3] = EngineWindow; })(); //# sourceURL=src/EngineWindow.js "use strict"; (function() { const pools = {}; const create = (cls) => { const buffer = pools[cls.name]; if(buffer && (buffer.length > 0)) { return buffer.pop(); } const instance = new cls(); return instance; }; const remove = (instance) => { const buffer = pools[instance.constructor.name]; if(!buffer) { pools[instance.constructor.name] = [ instance ]; } else { buffer = [ instance ]; } }; modules[6] = { create: create, remove: remove }; })(); //# sourceURL=src/Pool.js "use strict"; (function() { const BUTTON_ENUM_OFFSET = 256; const Key = { BUTTON_ENUM_OFFSET: BUTTON_ENUM_OFFSET, BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, DELETE: 46, NUM_0: 48, NUM_1: 49, NUM_2: 50, NUM_3: 51, NUM_4: 52, NUM_5: 53, NUM_6: 54, NUM_7: 55, NUM_8: 56, NUM_9: 57, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, MULTIPLY: 106, ADD: 107, SUBTRACT: 109, DECIMAL: 110, DIVIDE: 111, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, SQUARE_BRACKET_LEFT: 91, SQUARE_BRACKET_RIGHT: 91, PARENTHESES_LEFT: 91, PARENTHESES_RIGHT: 91, BRACES_LEFT: 91, BRACES_RIGHT: 92, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, PLUS: 187, MINUS: 189, TILDE: 192, APOSTROPHE: 222, BUTTON_LEFT: 0 + BUTTON_ENUM_OFFSET, BUTTON_MIDDLE: 1 + BUTTON_ENUM_OFFSET, BUTTON_RIGHT: 2 + BUTTON_ENUM_OFFSET }; modules[8] = Key; })(); //# sourceURL=src/Key.js "use strict"; (function() { var Device = modules[1]; var Engine = modules[2]; var Key = modules[8]; const numKeys = 256; const numInputs = 10; const numTotalKeys = (numKeys + numInputs) + 1; function Subscriber(owner, func) { this.owner = owner; this.func = func; } function Input() { this.listeners = {}; this.ignoreKeys = {}; this.cmdKeys = {}; this.iframeKeys = {}; this.enable = true; this.stickyKeys = false; this.metaPressed = false; this.firstInputEvent = true; this.inputs = new Array(numTotalKeys); this.touches = []; Device.on("visible", (value) => { if(!value) { this.reset(); } }); this.x = 0; this.y = 0; this.screenX = 0; this.screenY = 0; this.prevX = 0; this.prevY = 0; this.prevScreenX = 0; this.prevScreenY = 0; loadIgnoreKeys(this); addEventListeners(this); } Input.prototype = { handleKeyDown: function(domEvent) { this.checkIgnoreKey(domEvent); if(!this.enable) { return ; } const keyCode = domEvent.keyCode; if(this.stickyKeys && this.inputs[keyCode]) { return ; } if(domEvent.keyIdentifier === "Meta") { this.metaPressed = true; } else if(this.metaPressed) { return ; } this.inputs[keyCode] = 1; const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.keyCode = keyCode; this.emit("keydown", inputEvent); }, handleKeyUp: function(domEvent) { this.checkIgnoreKey(domEvent); if(!this.enable) { return ; } const keyCode = domEvent.keyCode; this.metaPressed = false; this.inputs[keyCode] = 0; const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.keyCode = keyCode; this.emit("keyup", inputEvent); }, handleMouseDown: function(domEvent) { this.handleMouseEvent("down", domEvent); }, handleMouseUp: function(domEvent) { this.handleMouseEvent("up", domEvent); }, handleMouseDblClick: function(domEvent) { this.handleMouseEvent("dblclick", domEvent); }, handleMouseMove: function(domEvent) { if(document.activeElement === document.body) { domEvent.preventDefault(); } this.handleMouseEvent("move", domEvent); }, handleMouseWheel: function(domEvent) { if(document.activeElement === document.body) { domEvent.preventDefault(); } this.handleMouseEvent("wheel", domEvent); }, handleMouseEvent: function(eventType, domEvent) { if(!Engine.gl || (domEvent.target !== Engine.gl.canvas)) { return ; } if(!this.enable) { return ; } const wnd = Engine.window; const camera = Engine.camera; this.prevScreenX = this.screenX; this.prevScreenY = this.screenY; this.screenX = (((domEvent.pageX - wnd.offsetLeft) * wnd.scaleX) / wnd.ratio); this.screenY = (((domEvent.pageY - wnd.offsetTop) * wnd.scaleY) / wnd.ratio); this.prevX = this.x; this.prevY = this.y; this.x = ((((this.screenX * camera._scale.x) - camera.x) / wnd.ratio) | 0); this.y = ((((this.screenY * camera._scale.y) - camera.y) / wnd.ratio) | 0); const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.screenX = this.screenX; inputEvent.screenY = this.screenY; inputEvent.x = this.x; inputEvent.y = this.y; switch(eventType) { case "down": case "dblclick": case "up": { const keyCode = domEvent.button + Key.BUTTON_ENUM_OFFSET; this.inputs[keyCode] = (eventType === "up" ? 0 : 1); if(this.firstInputEvent) { inputEvent.deltaX = 0; inputEvent.deltaY = 0; this.firstInputEvent = false; } else { inputEvent.deltaX = (((this.prevScreenX - this.screenX) * camera.zoomRatio) / wnd.ratio); inputEvent.deltaY = (((this.prevScreenY - this.screenY) * camera.zoomRatio) / wnd.ratio); } inputEvent.keyCode = keyCode; } break; case "move": { if(this.firstInputEvent) { inputEvent.deltaX = 0; inputEvent.deltaY = 0; this.firstInputEvent = false; } else { inputEvent.deltaX = ((-domEvent.movementX * camera.zoomRatio) / wnd.ratio); inputEvent.deltaY = ((-domEvent.movementY * camera.zoomRatio) / wnd.ratio); } inputEvent.keyCode = 0; } break; case "wheel": { inputEvent.deltaX = Math.max(-1, Math.min(1, domEvent.wheelDelta || -domEvent.detail)); inputEvent.deltaY = inputEvent.deltaX; inputEvent.keyCode = 0; } break; } this.emit(eventType, inputEvent); }, handleTouchDown: function(domEvent) { this.handleTouchEvent(domEvent, "down"); }, handleTouchUp: function(domEvent) { this.handleTouchEvent(domEvent, "up"); }, handleTouchMove: function(domEvent) { this.handleTouchEvent(domEvent, "move"); }, handleTouchEvent: function(domEvent, eventType) { if(!Engine.gl || (domEvent.target !== Engine.gl.canvas)) { return ; } const wnd = Engine.window; const camera = Engine.camera; const changedTouches = domEvent.changedTouches; for(let n = 0; n < changedTouches.length; n++) { const touch = changedTouches[n]; let id; switch(eventType) { case "down": id = this.touches.length; this.touches.push(touch.identifier); break; case "up": id = this.getTouchID(touch.identifier); if(id === -1) { continue; }; this.touches.splice(id, 1); break; case "move": id = this.getTouchID(touch.identifier); break; } const screenX = (touch.pageX - wnd.offsetLeft) * wnd.scaleX; const screenY = (touch.pageY - wnd.offsetTop) * wnd.scaleY; const x = ((screenX * camera.zoomRatio) - camera.x) | 0; const y = ((screenY * camera.zoomRatio) - camera.y) | 0; const keyCode = id + Key.BUTTON_ENUM_OFFSET; const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.screenX = screenX; inputEvent.screenY = screenY; inputEvent.x = x; inputEvent.y = y; inputEvent.keyCode = keyCode; if(id === 0) { this.prevX = this.x; this.prevY = this.y; this.prevScreenX = this.screenX; this.prevScreenY = this.screenY; this.x = x; this.y = y; this.screenX = screenX; this.screenY = screenY; if(this.firstInputEvent) { inputEvent.deltaX = 0; inputEvent.deltaY = 0; this.firstInputEvent = false; } else { inputEvent.deltaX = (this.prevScreenX - this.screenX); inputEvent.deltaY = (this.prevScreenY - this.screenY); } } switch(eventType) { case "down": this.inputs[keyCode] = 1; break; case "up": this.inputs[keyCode] = 0; break; } this.emit(eventType, inputEvent); } }, pressed: function(keyCode) { return this.inputs[keyCode]; }, on: function(event, func, owner) { const sub = new Subscriber(owner, func); let buffer = this.listeners[event]; if(buffer) { buffer.push(sub); } else { buffer = [ sub ]; this.listeners[event] = buffer; } }, off: function(event, func, owner) { const buffer = this.listeners[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { const sub = buffer[n]; if((sub.func === func) && (sub.owner === owner)) { buffer[n] = buffer[buffer.length - 1]; buffer.pop(); return ; } } }, emit: function(event, arg) { const buffer = this.listeners[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { const sub = buffer[n]; sub.func.call(sub.owner, arg); } }, reset: function() { for(let n = 0; n < numKeys.length; n++) { if(!this.inputs[n]) { continue; } this.inputs[n] = 0; const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.keyCode = keyCode; this.emit("keyup", inputEvent); } for(let n = numKeys; n <= (numKeys + numTouches); n++) { const keyCode = n + Key.BUTTON_ENUM_OFFSET; if(!this.inputs[keyCode]) { continue; } const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.keyCode = keyCode; this.emit("up", inputEvent); } for(let n = 0; n < this.touches.length; n++) { if(!this.touches[n]) { continue; } const keyCode = n + Key.BUTTON_ENUM_OFFSET; if(!this.inputs[keyCode]) { continue; } const inputEvent = new Input.Event(); inputEvent.domEvent = domEvent; inputEvent.keyCode = keyCode; this.emit("up", inputEvent); } }, getTouchID: function(eventTouchID) { for(let n = 0; n < this.touches.length; n++) { if(this.touches[n] === eventTouchID) { return n; } } return -1; }, checkIgnoreKey: function(domEvent) { const keyCode = domEvent.keyCode; if(document.activeElement === document.body) { if(window.top && this.iframeKeys[keyCode]) { domEvent.preventDefault(); } if(this.cmdKeys[keyCode] !== undefined) { this.numCmdKeysPressed++; } if((this.ignoreKeys[keyCode] !== undefined) && (this.numCmdKeysPressed <= 0)) { domEvent.preventDefault(); } } }, set ignoreFKeys(flag) { if(flag) { ignoreFKeys(this, 1); } else { ignoreFKeys(this, 0); } }, get ignoreFKeys() { return !!this.ignoreKeys[112]; } }; const addEventListeners = (input) => { window.addEventListener("mousedown", input.handleMouseDown.bind(input)); window.addEventListener("mouseup", input.handleMouseUp.bind(input)); window.addEventListener("mousemove", input.handleMouseMove.bind(input)); window.addEventListener("mousewheel", input.handleMouseWheel.bind(input)); window.addEventListener("dblclick", input.handleMouseDblClick.bind(input)); window.addEventListener("touchstart", input.handleTouchDown.bind(input)); window.addEventListener("touchend", input.handleTouchUp.bind(input)); window.addEventListener("touchmove", input.handleTouchMove.bind(input)); window.addEventListener("touchcancel", input.handleTouchUp.bind(input)); window.addEventListener("touchleave", input.handleTouchUp.bind(input)); if(Device.supports.onkeydown) { window.addEventListener("keydown", input.handleKeyDown.bind(input)); } if(Device.supports.onkeyup) { window.addEventListener("keyup", input.handleKeyUp.bind(input)); } }; const loadIgnoreKeys = (input) => { input.ignoreKeys = {}; input.ignoreKeys[8] = 1; input.ignoreKeys[9] = 1; input.ignoreKeys[13] = 1; input.ignoreKeys[17] = 1; input.ignoreKeys[91] = 1; input.ignoreKeys[38] = 1; input.ignoreKeys[39] = 1; input.ignoreKeys[40] = 1; input.ignoreKeys[37] = 1; input.ignoreKeys[124] = 1; input.ignoreKeys[125] = 1; input.ignoreKeys[126] = 1; input.cmdKeys[91] = 1; input.cmdKeys[17] = 1; input.iframeKeys[37] = 1; input.iframeKeys[38] = 1; input.iframeKeys[39] = 1; input.iframeKeys[40] = 1; }; const ignoreFKeys = (input, value) => { for(let n = 112; n <= 123; n++) { input.ignoreKeys[n] = value; } }; Input.Event = function() { this.domEvent = null; this.x = 0; this.y = 0; this.deltaX = 0; this.deltaY = 0; this.screenX = 0; this.screenY = 0; this.keyCode = 0; this.entity = null; }; const instance = new Input(); modules[7] = instance; })(); //# sourceURL=src/Input.js "use strict"; (function() { var Device = modules[1]; function GamepadInfo(gamepad) { const buttons = gamepad.buttons; this.buttons = new Array(buttons.length); for(let n = 0; n < buttons.length; n++) { this.buttons[n] = buttons[n].pressed; } this.axes = gamepad.axes; this.axesDefault = gamepad.axes; } function Gamepad() { this.watchers = {}; this.gamepads = new Array(null, null, null, null); this.updateFunc = this.update.bind(this); window.addEventListener("gamepadconnected", this.handleConnected.bind(this)); window.addEventListener("gamepaddisconnected", this.handleDisconnected.bind(this)); requestAnimationFrame(this.updateFunc); } Gamepad.prototype = { update: function() { const gamepads = navigator.getGamepads(); for(let n = 0; n < gamepads.length; n++) { const gamepad = gamepads[n]; if(!gamepad) { continue; } const gamepadInfo = this.gamepads[n]; if(!gamepadInfo) { this.gamepads[n] = new GamepadInfo(gamepad); this.emit("connected", n); } else { if(!Device.visible) { continue; } const prevButtons = gamepadInfo.buttons; const currButtons = gamepad.buttons; for(let i = 0; i < currButtons.length; i++) { const currButton = currButtons[i]; if(prevButtons[i] !== currButton.pressed) { prevButtons[i] = currButton.pressed; if(currButton.pressed) { this.emit("down", i, n); } else { this.emit("up", i, n); } } } let changed = false; const axes = gamepad.axes; const prevAxes = gamepadInfo.axes; const defaultAxes = gamepadInfo.axesDefault; for(let i = 0; i < axes.length; i++) { let value = axes[i]; const defaultValue = defaultAxes[i]; if(value === defaultValue) { value = 0; } if(prevAxes[i] !== value) { prevAxes[i] = value; this.emit("axis", i, n, value); changed = true; } } if(changed) { this.emit("axes", prevAxes, n); } } } requestAnimationFrame(this.updateFunc); }, handleConnected: function(event) { const gamepad = event.gamepad; if(!this.gamepads[gamepad.index]) { this.gamepads[gamepad.index] = new GamepadInfo(gamepad); this.emit("connected", gamepad.index); } }, handleDisconnected: function(event) { const index = event.gamepad.index; this.gamepads[index] = null; this.emit("disconnected", index); }, on: function(event, func) { const buffer = this.watchers[event]; if(!buffer) { this.watchers[event] = [ func ]; } else { buffer.push(func); } }, off: function(event, func) { const buffer = this.watchers[event]; if(!buffer) { return ; } const index = buffer.indexOf(func); buffer[index] = buffer[buffer.length - 1]; buffer.pop(); }, emit: function(event, key, index, value) { const buffer = this.watchers[event]; if(!buffer) { return ; } for(let n = 0; n < buffer.length; n++) { buffer[n](key, index, value); } }, pressed: function(key, index) { const gamepad = this.gamepads[index]; return gamepad ? gamepad.buttons[key].pressed : false; }, axis: function(key, index) { const gamepad = this.gamepads[index]; if(!gamepad) { return 0; } const value = gamepad.axes[key]; if(value === gamepad.axesDefault[key]) { return 0; } return value; } }; const instance = new Gamepad(); modules[9] = instance; })(); //# sourceURL=src/Gamepad.js "use strict"; (function() { var Resources = modules[5]; function Resource(config) { this._loaded = false; this._loading = false; if(config) { this.setup(config); } } Resource.prototype = { set loaded(value) { if(value) { if(!this._loaded) { this._loaded = true; this.loading = false; } } else { if(this._loaded) { this._loaded = false; } } }, get loaded() { return this._loaded; }, set loading(value) { if(value) { if(!this._loading) { this._loading = true; this._loaded = false; Resources.resourceLoading(this); } } else { if(this._loading) { this._loading = false; this._loaded = true; Resources.resourceLoaded(this); } } }, get loading() { return this._loading; } }; Resources.Resource = Resource; modules[10] = Resource; })(); //# sourceURL=src/resources/Resource.js "use strict"; (function() { const isPowerOf2 = (value) => { return (value & (value - 1)) == 0; }; const onDomLoad = (func) => { if((document.readyState === "interactive") || (document.readyState === "complete")) { func(); return ; } const callbackFunc = (event) => { func(); window.removeEventListener("DOMContentLoaded", callbackFunc); }; window.addEventListener("DOMContentLoaded", callbackFunc); }; modules[12] = { isPowerOf2: isPowerOf2, onDomLoad: onDomLoad }; })(); //# sourceURL=src/Utils.js "use strict"; (function() { var Engine = modules[2]; var isPowerOf2 = modules[12].isPowerOf2; var Resource = modules[10]; const greenPixel = new Uint8Array([ 0, 255, 0, 255 ]); function Texture(config) { Resource.call(this, config); this.instance = Engine.gl.createTexture(); this.width = 0; this.height = 0; if(config) { this.setup(config); } } Texture.prototype = { setup: function(config) { this.loadFromPath(config.path); }, loadFromPath: function(path) { this.loading = true; const image = new Image(); image.onload = () => { this.loadFromImage(image); }; image.onfailed = () => { this.loadEmpty(); }; image.src = path; }, loadEmpty: function() { const gl = Engine.gl; this.width = 1; this.height = 1; gl.bindTexture(gl.TEXTURE_2D, this.instance); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, greenPixel); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); this.loaded = true; }, loadFromImage: function(image) { const gl = Engine.gl; this.width = image.width; this.height = image.height; gl.bindTexture(gl.TEXTURE_2D, this.instance); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); if(isPowerOf2(image.width) && isPowerOf2(image.height)) { gl.generateMipmap(gl.TEXTURE_2D); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); } this.loading = false; }, loadFromCanvas: function(canvas) { const gl = Engine.gl; this.width = canvas.width; this.height = canvas.height; gl.bindTexture(gl.TEXTURE_2D, this.instance); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas); if(isPowerOf2(canvas.width) && isPowerOf2(canvas.height)) { gl.generateMipmap(gl.TEXTURE_2D); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); } this.loaded = true; }, frame: function() { return this.frameData; }, use: function() { const gl = Engine.gl; gl.bindTexture(gl.TEXTURE_2D, this.instance); } }; _inherits(Texture, Resource); modules[11] = Texture; })(); //# sourceURL=src/resources/Texture.js "use strict"; (function() { var Engine = modules[2]; var Resource = modules[10]; const requestTextFunc = (request) => { return request.text(); }; function Attrib(name, loc) { this.name = name; this.loc = loc; } function Uniform(name, loc, type) { this.name = name; this.loc = loc; this.type = type; } function Material(config) { Resource.call(this, config); this.program = null; this.attrib = null; this.attribData = null; this.uniform = null; this.uniformData = null; this.numAttribs = 0; this.uniforms = {}; if(config) { this.setup(config); } } Material.prototype = { setup: function(config) { if(config.vertexSourcePath) { this.loading = true; fetch(config.vertexSourcePath).then(requestTextFunc).then((vertexSource) => { if(config.fragmentSourcePath) { fetch(config.vertexSourcePath).then(requestTextFunc).then((fragmentSource) => { this.createProgram(vertexSource, fragmentSource); }); } else { this.createProgram(vertexSource, config.fragmentSource); } }); } else if(config.fragmentSourcePath) { fetch(config.vertexSourcePath).then(requestTextFunc).then((fragmentSource) => { this.createProgram(config.vertexSource, fragmentSource); }); } else { this.createProgram(config.vertexSource, config.fragmentSource); } }, loadShader: function(type, source) { const gl = Engine.gl; const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { let shaderType; switch(type) { case gl.VERTEX_SHADER: shaderType = "VERTEX_SHADER"; break; case gl.FRAGMENT_SHADER: shaderType = "FRAGMENT_SHADER"; break; } console.error("" + shaderType + ": " + gl.getShaderInfoLog(shader) + ""); gl.deleteShader(shader); return null; } return shader; }, createProgram: function(vertexSource, fragmentSource) { const gl = Engine.gl; const vertexShader = this.loadShader(gl.VERTEX_SHADER, vertexSource); const fragmentShader = this.loadShader(gl.FRAGMENT_SHADER, fragmentSource); this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); gl.linkProgram(this.program); if(!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(this.program) + ""); gl.deleteProgram(this.program); gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); return ; } this.extractAttribs(); this.extractUniforms(); this.loading = false; }, extractAttribs: function() { this.attrib = {}; this.attribData = []; const gl = Engine.gl; this.numAttribs = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); for(let n = 0; n < this.numAttribs; n++) { const attrib = gl.getActiveAttrib(this.program, n); const attribLoc = gl.getAttribLocation(this.program, attrib.name); this.attrib[attrib.name] = attribLoc; this.attribData.push(new Attrib(attrib.name, attribLoc)); } }, extractUniforms: function() { this.uniform = {}; this.uniformData = []; const gl = Engine.gl; const num = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); for(let n = 0; n < num; n++) { const uniform = gl.getActiveUniform(this.program, n); const name = uniform.name.replace("[0]", ""); const loc = gl.getUniformLocation(this.program, name); this.uniform[name] = loc; this.uniformData.push(new Uniform(name, loc, uniform.type)); } } }; _inherits(Material, Resource); modules[13] = Material; })(); //# sourceURL=src/resources/Material.js "use strict"; (function() { const radians = (degrees) => { return (degrees * Math.PI) / 180; }; const degrees = (ra