diff --git a/docs/test/bundle.js b/docs/test/bundle.js deleted file mode 100644 index a7ea9c94..00000000 --- a/docs/test/bundle.js +++ /dev/null @@ -1,1418 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./index.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "../../node_modules/aabb-3d/index.js": -/*!**************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/aabb-3d/index.js ***! - \**************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("module.exports = AABB\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\nfunction AABB(pos, vec) {\n\n if(!(this instanceof AABB)) {\n return new AABB(pos, vec)\n }\n\n var pos2 = vec3.create()\n vec3.add(pos2, pos, vec)\n \n this.base = vec3.min(vec3.create(), pos, pos2)\n this.vec = vec3.clone(vec)\n this.max = vec3.max(vec3.create(), pos, pos2)\n\n this.mag = vec3.length(this.vec)\n\n}\n\nvar cons = AABB\n , proto = cons.prototype\n\nproto.width = function() {\n return this.vec[0]\n}\n\nproto.height = function() {\n return this.vec[1]\n}\n\nproto.depth = function() {\n return this.vec[2]\n}\n\nproto.x0 = function() {\n return this.base[0]\n}\n\nproto.y0 = function() {\n return this.base[1]\n}\n\nproto.z0 = function() {\n return this.base[2]\n}\n\nproto.x1 = function() {\n return this.max[0]\n}\n\nproto.y1 = function() {\n return this.max[1]\n}\n\nproto.z1 = function() {\n return this.max[2]\n}\n\nproto.translate = function(by) {\n vec3.add(this.max, this.max, by)\n vec3.add(this.base, this.base, by)\n return this\n}\n\nproto.setPosition = function(pos) {\n vec3.add(this.max, pos, this.vec)\n vec3.copy(this.base, pos)\n return this\n}\n\nproto.expand = function(aabb) {\n var max = vec3.create()\n , min = vec3.create()\n\n vec3.max(max, aabb.max, this.max)\n vec3.min(min, aabb.base, this.base)\n vec3.subtract(max, max, min)\n\n return new AABB(min, max)\n}\n\nproto.intersects = function(aabb) {\n if(aabb.base[0] > this.max[0]) return false\n if(aabb.base[1] > this.max[1]) return false\n if(aabb.base[2] > this.max[2]) return false\n if(aabb.max[0] < this.base[0]) return false\n if(aabb.max[1] < this.base[1]) return false\n if(aabb.max[2] < this.base[2]) return false\n\n return true\n}\n\nproto.touches = function(aabb) {\n\n var intersection = this.union(aabb);\n\n return (intersection !== null) &&\n ((intersection.width() == 0) ||\n (intersection.height() == 0) || \n (intersection.depth() == 0))\n\n}\n\nproto.union = function(aabb) {\n if(!this.intersects(aabb)) return null\n\n var base_x = Math.max(aabb.base[0], this.base[0])\n , base_y = Math.max(aabb.base[1], this.base[1])\n , base_z = Math.max(aabb.base[2], this.base[2])\n , max_x = Math.min(aabb.max[0], this.max[0])\n , max_y = Math.min(aabb.max[1], this.max[1])\n , max_z = Math.min(aabb.max[2], this.max[2])\n\n return new AABB([base_x, base_y, base_z], [max_x - base_x, max_y - base_y, max_z - base_z])\n}\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/aabb-3d/index.js?"); - -/***/ }), - -/***/ "../../node_modules/base64-js/index.js": -/*!****************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/base64-js/index.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i]\n revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n var len = b64.length\n\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4')\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=')\n if (validLen === -1) validLen = len\n\n var placeHoldersLen = validLen === len\n ? 0\n : 4 - (validLen % 4)\n\n return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n var tmp\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n var curByte = 0\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0\n ? validLen - 4\n : validLen\n\n for (var i = 0; i < len; i += 4) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 18) |\n (revLookup[b64.charCodeAt(i + 1)] << 12) |\n (revLookup[b64.charCodeAt(i + 2)] << 6) |\n revLookup[b64.charCodeAt(i + 3)]\n arr[curByte++] = (tmp >> 16) & 0xFF\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 2) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 2) |\n (revLookup[b64.charCodeAt(i + 1)] >> 4)\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 1) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 10) |\n (revLookup[b64.charCodeAt(i + 1)] << 4) |\n (revLookup[b64.charCodeAt(i + 2)] >> 2)\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n return arr\n}\n\nfunction tripletToBase64 (num) {\n return lookup[num >> 18 & 0x3F] +\n lookup[num >> 12 & 0x3F] +\n lookup[num >> 6 & 0x3F] +\n lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n var tmp\n var output = []\n for (var i = start; i < end; i += 3) {\n tmp =\n ((uint8[i] << 16) & 0xFF0000) +\n ((uint8[i + 1] << 8) & 0xFF00) +\n (uint8[i + 2] & 0xFF)\n output.push(tripletToBase64(tmp))\n }\n return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n var tmp\n var len = uint8.length\n var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n var parts = []\n var maxChunkLength = 16383 // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(\n uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n ))\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1]\n parts.push(\n lookup[tmp >> 2] +\n lookup[(tmp << 4) & 0x3F] +\n '=='\n )\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n parts.push(\n lookup[tmp >> 10] +\n lookup[(tmp >> 4) & 0x3F] +\n lookup[(tmp << 2) & 0x3F] +\n '='\n )\n }\n\n return parts.join('')\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/base64-js/index.js?"); - -/***/ }), - -/***/ "../../node_modules/binary-search-bounds/search-bounds.js": -/*!***********************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/binary-search-bounds/search-bounds.js ***! - \***********************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nfunction compileSearch(funcName, predicate, reversed, extraArgs, useNdarray, earlyOut) {\n var code = [\n \"function \", funcName, \"(a,l,h,\", extraArgs.join(\",\"), \"){\",\nearlyOut ? \"\" : \"var i=\", (reversed ? \"l-1\" : \"h+1\"),\n\";while(l<=h){\\\nvar m=(l+h)>>>1,x=a\", useNdarray ? \".get(m)\" : \"[m]\"]\n if(earlyOut) {\n if(predicate.indexOf(\"c\") < 0) {\n code.push(\";if(x===y){return m}else if(x<=y){\")\n } else {\n code.push(\";var p=c(x,y);if(p===0){return m}else if(p<=0){\")\n }\n } else {\n code.push(\";if(\", predicate, \"){i=m;\")\n }\n if(reversed) {\n code.push(\"l=m+1}else{h=m-1}\")\n } else {\n code.push(\"h=m-1}else{l=m+1}\")\n }\n code.push(\"}\")\n if(earlyOut) {\n code.push(\"return -1};\")\n } else {\n code.push(\"return i};\")\n }\n return code.join(\"\")\n}\n\nfunction compileBoundsSearch(predicate, reversed, suffix, earlyOut) {\n var result = new Function([\n compileSearch(\"A\", \"x\" + predicate + \"y\", reversed, [\"y\"], false, earlyOut),\n compileSearch(\"B\", \"x\" + predicate + \"y\", reversed, [\"y\"], true, earlyOut),\n compileSearch(\"P\", \"c(x,y)\" + predicate + \"0\", reversed, [\"y\", \"c\"], false, earlyOut),\n compileSearch(\"Q\", \"c(x,y)\" + predicate + \"0\", reversed, [\"y\", \"c\"], true, earlyOut),\n\"function dispatchBsearch\", suffix, \"(a,y,c,l,h){\\\nif(a.shape){\\\nif(typeof(c)==='function'){\\\nreturn Q(a,(l===undefined)?0:l|0,(h===undefined)?a.shape[0]-1:h|0,y,c)\\\n}else{\\\nreturn B(a,(c===undefined)?0:c|0,(l===undefined)?a.shape[0]-1:l|0,y)\\\n}}else{\\\nif(typeof(c)==='function'){\\\nreturn P(a,(l===undefined)?0:l|0,(h===undefined)?a.length-1:h|0,y,c)\\\n}else{\\\nreturn A(a,(c===undefined)?0:c|0,(l===undefined)?a.length-1:l|0,y)\\\n}}}\\\nreturn dispatchBsearch\", suffix].join(\"\"))\n return result()\n}\n\nmodule.exports = {\n ge: compileBoundsSearch(\">=\", false, \"GE\"),\n gt: compileBoundsSearch(\">\", false, \"GT\"),\n lt: compileBoundsSearch(\"<\", true, \"LT\"),\n le: compileBoundsSearch(\"<=\", true, \"LE\"),\n eq: compileBoundsSearch(\"-\", true, \"EQ\", true)\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/binary-search-bounds/search-bounds.js?"); - -/***/ }), - -/***/ "../../node_modules/bit-twiddle/twiddle.js": -/*!********************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/bit-twiddle/twiddle.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/**\n * Bit twiddling hacks for JavaScript.\n *\n * Author: Mikola Lysenko\n *\n * Ported from Stanford bit twiddling hack library:\n * http://graphics.stanford.edu/~seander/bithacks.html\n */\n\n \"use restrict\";\n\n//Number of bits in an integer\nvar INT_BITS = 32;\n\n//Constants\nexports.INT_BITS = INT_BITS;\nexports.INT_MAX = 0x7fffffff;\nexports.INT_MIN = -1<<(INT_BITS-1);\n\n//Returns -1, 0, +1 depending on sign of x\nexports.sign = function(v) {\n return (v > 0) - (v < 0);\n}\n\n//Computes absolute value of integer\nexports.abs = function(v) {\n var mask = v >> (INT_BITS-1);\n return (v ^ mask) - mask;\n}\n\n//Computes minimum of integers x and y\nexports.min = function(x, y) {\n return y ^ ((x ^ y) & -(x < y));\n}\n\n//Computes maximum of integers x and y\nexports.max = function(x, y) {\n return x ^ ((x ^ y) & -(x < y));\n}\n\n//Checks if a number is a power of two\nexports.isPow2 = function(v) {\n return !(v & (v-1)) && (!!v);\n}\n\n//Computes log base 2 of v\nexports.log2 = function(v) {\n var r, shift;\n r = (v > 0xFFFF) << 4; v >>>= r;\n shift = (v > 0xFF ) << 3; v >>>= shift; r |= shift;\n shift = (v > 0xF ) << 2; v >>>= shift; r |= shift;\n shift = (v > 0x3 ) << 1; v >>>= shift; r |= shift;\n return r | (v >> 1);\n}\n\n//Computes log base 10 of v\nexports.log10 = function(v) {\n return (v >= 1000000000) ? 9 : (v >= 100000000) ? 8 : (v >= 10000000) ? 7 :\n (v >= 1000000) ? 6 : (v >= 100000) ? 5 : (v >= 10000) ? 4 :\n (v >= 1000) ? 3 : (v >= 100) ? 2 : (v >= 10) ? 1 : 0;\n}\n\n//Counts number of bits\nexports.popCount = function(v) {\n v = v - ((v >>> 1) & 0x55555555);\n v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);\n return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;\n}\n\n//Counts number of trailing zeros\nfunction countTrailingZeros(v) {\n var c = 32;\n v &= -v;\n if (v) c--;\n if (v & 0x0000FFFF) c -= 16;\n if (v & 0x00FF00FF) c -= 8;\n if (v & 0x0F0F0F0F) c -= 4;\n if (v & 0x33333333) c -= 2;\n if (v & 0x55555555) c -= 1;\n return c;\n}\nexports.countTrailingZeros = countTrailingZeros;\n\n//Rounds to next power of 2\nexports.nextPow2 = function(v) {\n v += v === 0;\n --v;\n v |= v >>> 1;\n v |= v >>> 2;\n v |= v >>> 4;\n v |= v >>> 8;\n v |= v >>> 16;\n return v + 1;\n}\n\n//Rounds down to previous power of 2\nexports.prevPow2 = function(v) {\n v |= v >>> 1;\n v |= v >>> 2;\n v |= v >>> 4;\n v |= v >>> 8;\n v |= v >>> 16;\n return v - (v>>>1);\n}\n\n//Computes parity of word\nexports.parity = function(v) {\n v ^= v >>> 16;\n v ^= v >>> 8;\n v ^= v >>> 4;\n v &= 0xf;\n return (0x6996 >>> v) & 1;\n}\n\nvar REVERSE_TABLE = new Array(256);\n\n(function(tab) {\n for(var i=0; i<256; ++i) {\n var v = i, r = i, s = 7;\n for (v >>>= 1; v; v >>>= 1) {\n r <<= 1;\n r |= v & 1;\n --s;\n }\n tab[i] = (r << s) & 0xff;\n }\n})(REVERSE_TABLE);\n\n//Reverse bits in a 32 bit word\nexports.reverse = function(v) {\n return (REVERSE_TABLE[ v & 0xff] << 24) |\n (REVERSE_TABLE[(v >>> 8) & 0xff] << 16) |\n (REVERSE_TABLE[(v >>> 16) & 0xff] << 8) |\n REVERSE_TABLE[(v >>> 24) & 0xff];\n}\n\n//Interleave bits of 2 coordinates with 16 bits. Useful for fast quadtree codes\nexports.interleave2 = function(x, y) {\n x &= 0xFFFF;\n x = (x | (x << 8)) & 0x00FF00FF;\n x = (x | (x << 4)) & 0x0F0F0F0F;\n x = (x | (x << 2)) & 0x33333333;\n x = (x | (x << 1)) & 0x55555555;\n\n y &= 0xFFFF;\n y = (y | (y << 8)) & 0x00FF00FF;\n y = (y | (y << 4)) & 0x0F0F0F0F;\n y = (y | (y << 2)) & 0x33333333;\n y = (y | (y << 1)) & 0x55555555;\n\n return x | (y << 1);\n}\n\n//Extracts the nth interleaved component\nexports.deinterleave2 = function(v, n) {\n v = (v >>> n) & 0x55555555;\n v = (v | (v >>> 1)) & 0x33333333;\n v = (v | (v >>> 2)) & 0x0F0F0F0F;\n v = (v | (v >>> 4)) & 0x00FF00FF;\n v = (v | (v >>> 16)) & 0x000FFFF;\n return (v << 16) >> 16;\n}\n\n\n//Interleave bits of 3 coordinates, each with 10 bits. Useful for fast octree codes\nexports.interleave3 = function(x, y, z) {\n x &= 0x3FF;\n x = (x | (x<<16)) & 4278190335;\n x = (x | (x<<8)) & 251719695;\n x = (x | (x<<4)) & 3272356035;\n x = (x | (x<<2)) & 1227133513;\n\n y &= 0x3FF;\n y = (y | (y<<16)) & 4278190335;\n y = (y | (y<<8)) & 251719695;\n y = (y | (y<<4)) & 3272356035;\n y = (y | (y<<2)) & 1227133513;\n x |= (y << 1);\n \n z &= 0x3FF;\n z = (z | (z<<16)) & 4278190335;\n z = (z | (z<<8)) & 251719695;\n z = (z | (z<<4)) & 3272356035;\n z = (z | (z<<2)) & 1227133513;\n \n return x | (z << 2);\n}\n\n//Extracts nth interleaved component of a 3-tuple\nexports.deinterleave3 = function(v, n) {\n v = (v >>> n) & 1227133513;\n v = (v | (v>>>2)) & 3272356035;\n v = (v | (v>>>4)) & 251719695;\n v = (v | (v>>>8)) & 4278190335;\n v = (v | (v>>>16)) & 0x3FF;\n return (v<<22)>>22;\n}\n\n//Computes next combination in colexicographic order (this is mistakenly called nextPermutation on the bit twiddling hacks page)\nexports.nextCombination = function(v) {\n var t = v | (v - 1);\n return (t + 1) | (((~t & -~t) - 1) >>> (countTrailingZeros(v) + 1));\n}\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/bit-twiddle/twiddle.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/index.js": -/*!********************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/index.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = boxIntersectWrapper\n\nvar pool = __webpack_require__(/*! typedarray-pool */ \"../../node_modules/typedarray-pool/pool.js\")\nvar sweep = __webpack_require__(/*! ./lib/sweep */ \"../../node_modules/box-intersect/lib/sweep.js\")\nvar boxIntersectIter = __webpack_require__(/*! ./lib/intersect */ \"../../node_modules/box-intersect/lib/intersect.js\")\n\nfunction boxEmpty(d, box) {\n for(var j=0; j>>1\n if(d <= 0) {\n return\n }\n\n var retval\n\n //Convert red boxes\n var redList = pool.mallocDouble(2*d*n)\n var redIds = pool.mallocInt32(n)\n n = convertBoxes(red, d, redList, redIds)\n\n if(n > 0) {\n if(d === 1 && full) {\n //Special case: 1d complete\n sweep.init(n)\n retval = sweep.sweepComplete(\n d, visit, \n 0, n, redList, redIds,\n 0, n, redList, redIds)\n } else {\n\n //Convert blue boxes\n var blueList = pool.mallocDouble(2*d*m)\n var blueIds = pool.mallocInt32(m)\n m = convertBoxes(blue, d, blueList, blueIds)\n\n if(m > 0) {\n sweep.init(n+m)\n\n if(d === 1) {\n //Special case: 1d bipartite\n retval = sweep.sweepBipartite(\n d, visit, \n 0, n, redList, redIds,\n 0, m, blueList, blueIds)\n } else {\n //General case: d>1\n retval = boxIntersectIter(\n d, visit, full,\n n, redList, redIds,\n m, blueList, blueIds)\n }\n\n pool.free(blueList)\n pool.free(blueIds)\n }\n }\n\n pool.free(redList)\n pool.free(redIds)\n }\n\n return retval\n}\n\n\nvar RESULT\n\nfunction appendItem(i,j) {\n RESULT.push([i,j])\n}\n\nfunction intersectFullArray(x) {\n RESULT = []\n boxIntersect(x, x, appendItem, true)\n return RESULT\n}\n\nfunction intersectBipartiteArray(x, y) {\n RESULT = []\n boxIntersect(x, y, appendItem, false)\n return RESULT\n}\n\n//User-friendly wrapper, handle full input and no-visitor cases\nfunction boxIntersectWrapper(arg0, arg1, arg2) {\n var result\n switch(arguments.length) {\n case 1:\n return intersectFullArray(arg0)\n case 2:\n if(typeof arg1 === 'function') {\n return boxIntersect(arg0, arg0, arg1, true)\n } else {\n return intersectBipartiteArray(arg0, arg1)\n }\n case 3:\n return boxIntersect(arg0, arg1, arg2, false)\n default:\n throw new Error('box-intersect: Invalid arguments')\n }\n}\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/box-intersect/index.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/lib/brute.js": -/*!************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/lib/brute.js ***! - \************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar DIMENSION = 'd'\nvar AXIS = 'ax'\nvar VISIT = 'vv'\nvar FLIP = 'fp'\n\nvar ELEM_SIZE = 'es'\n\nvar RED_START = 'rs'\nvar RED_END = 're'\nvar RED_BOXES = 'rb'\nvar RED_INDEX = 'ri'\nvar RED_PTR = 'rp'\n\nvar BLUE_START = 'bs'\nvar BLUE_END = 'be'\nvar BLUE_BOXES = 'bb'\nvar BLUE_INDEX = 'bi'\nvar BLUE_PTR = 'bp'\n\nvar RETVAL = 'rv'\n\nvar INNER_LABEL = 'Q'\n\nvar ARGS = [\n DIMENSION,\n AXIS,\n VISIT,\n RED_START,\n RED_END,\n RED_BOXES,\n RED_INDEX,\n BLUE_START,\n BLUE_END,\n BLUE_BOXES,\n BLUE_INDEX\n]\n\nfunction generateBruteForce(redMajor, flip, full) {\n var funcName = 'bruteForce' + \n (redMajor ? 'Red' : 'Blue') + \n (flip ? 'Flip' : '') +\n (full ? 'Full' : '')\n\n var code = ['function ', funcName, '(', ARGS.join(), '){',\n 'var ', ELEM_SIZE, '=2*', DIMENSION, ';']\n\n var redLoop = \n 'for(var i=' + RED_START + ',' + RED_PTR + '=' + ELEM_SIZE + '*' + RED_START + ';' +\n 'i<' + RED_END +';' +\n '++i,' + RED_PTR + '+=' + ELEM_SIZE + '){' +\n 'var x0=' + RED_BOXES + '[' + AXIS + '+' + RED_PTR + '],' +\n 'x1=' + RED_BOXES + '[' + AXIS + '+' + RED_PTR + '+' + DIMENSION + '],' +\n 'xi=' + RED_INDEX + '[i];'\n\n var blueLoop = \n 'for(var j=' + BLUE_START + ',' + BLUE_PTR + '=' + ELEM_SIZE + '*' + BLUE_START + ';' +\n 'j<' + BLUE_END + ';' +\n '++j,' + BLUE_PTR + '+=' + ELEM_SIZE + '){' +\n 'var y0=' + BLUE_BOXES + '[' + AXIS + '+' + BLUE_PTR + '],' +\n (full ? 'y1=' + BLUE_BOXES + '[' + AXIS + '+' + BLUE_PTR + '+' + DIMENSION + '],' : '') +\n 'yi=' + BLUE_INDEX + '[j];'\n\n if(redMajor) {\n code.push(redLoop, INNER_LABEL, ':', blueLoop)\n } else {\n code.push(blueLoop, INNER_LABEL, ':', redLoop)\n }\n\n if(full) {\n code.push('if(y1' +\n BLUE_END + '-' + BLUE_START + '){')\n\n if(full) {\n invoke(true, false)\n code.push('}else{')\n invoke(false, false)\n } else {\n code.push('if(' + FLIP + '){')\n invoke(true, true)\n code.push('}else{')\n invoke(true, false)\n code.push('}}else{if(' + FLIP + '){')\n invoke(false, true)\n code.push('}else{')\n invoke(false, false)\n code.push('}')\n }\n code.push('}}return ' + funcName)\n\n var codeStr = prefix.join('') + code.join('')\n var proc = new Function(codeStr)\n return proc()\n}\n\n\nexports.partial = bruteForcePlanner(false)\nexports.full = bruteForcePlanner(true)\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/box-intersect/lib/brute.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/lib/intersect.js": -/*!****************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/lib/intersect.js ***! - \****************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = boxIntersectIter\n\nvar pool = __webpack_require__(/*! typedarray-pool */ \"../../node_modules/typedarray-pool/pool.js\")\nvar bits = __webpack_require__(/*! bit-twiddle */ \"../../node_modules/bit-twiddle/twiddle.js\")\nvar bruteForce = __webpack_require__(/*! ./brute */ \"../../node_modules/box-intersect/lib/brute.js\")\nvar bruteForcePartial = bruteForce.partial\nvar bruteForceFull = bruteForce.full\nvar sweep = __webpack_require__(/*! ./sweep */ \"../../node_modules/box-intersect/lib/sweep.js\")\nvar findMedian = __webpack_require__(/*! ./median */ \"../../node_modules/box-intersect/lib/median.js\")\nvar genPartition = __webpack_require__(/*! ./partition */ \"../../node_modules/box-intersect/lib/partition.js\")\n\n//Twiddle parameters\nvar BRUTE_FORCE_CUTOFF = 128 //Cut off for brute force search\nvar SCAN_CUTOFF = (1<<22) //Cut off for two way scan\nvar SCAN_COMPLETE_CUTOFF = (1<<22) \n\n//Partition functions\nvar partitionInteriorContainsInterval = genPartition(\n '!(lo>=p0)&&!(p1>=hi)', \n ['p0', 'p1'])\n\nvar partitionStartEqual = genPartition(\n 'lo===p0',\n ['p0'])\n\nvar partitionStartLessThan = genPartition(\n 'lo 0) {\n top -= 1\n\n var iptr = top * IFRAME_SIZE\n var axis = BOX_ISTACK[iptr]\n var redStart = BOX_ISTACK[iptr+1]\n var redEnd = BOX_ISTACK[iptr+2]\n var blueStart = BOX_ISTACK[iptr+3]\n var blueEnd = BOX_ISTACK[iptr+4]\n var state = BOX_ISTACK[iptr+5]\n\n var dptr = top * DFRAME_SIZE\n var lo = BOX_DSTACK[dptr]\n var hi = BOX_DSTACK[dptr+1]\n\n //Unpack state info\n var flip = (state & 1)\n var full = !!(state & 16)\n\n //Unpack indices\n var red = xBoxes\n var redIndex = xIndex\n var blue = yBoxes\n var blueIndex = yIndex\n if(flip) {\n red = yBoxes\n redIndex = yIndex\n blue = xBoxes\n blueIndex = xIndex\n }\n\n if(state & 2) {\n redEnd = partitionStartLessThan(\n d, axis,\n redStart, redEnd, red, redIndex,\n hi)\n if(redStart >= redEnd) {\n continue\n }\n }\n if(state & 4) {\n redStart = partitionEndLessThanEqual(\n d, axis,\n redStart, redEnd, red, redIndex,\n lo)\n if(redStart >= redEnd) {\n continue\n }\n }\n \n var redCount = redEnd - redStart\n var blueCount = blueEnd - blueStart\n\n if(full) {\n if(d * redCount * (redCount + blueCount) < SCAN_COMPLETE_CUTOFF) {\n retval = sweep.scanComplete(\n d, axis, visit, \n redStart, redEnd, red, redIndex,\n blueStart, blueEnd, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n continue\n }\n } else {\n if(d * Math.min(redCount, blueCount) < BRUTE_FORCE_CUTOFF) {\n //If input small, then use brute force\n retval = bruteForcePartial(\n d, axis, visit, flip,\n redStart, redEnd, red, redIndex,\n blueStart, blueEnd, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n continue\n } else if(d * redCount * blueCount < SCAN_CUTOFF) {\n //If input medium sized, then use sweep and prune\n retval = sweep.scanBipartite(\n d, axis, visit, flip, \n redStart, redEnd, red, redIndex,\n blueStart, blueEnd, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n continue\n }\n }\n \n //First, find all red intervals whose interior contains (lo,hi)\n var red0 = partitionInteriorContainsInterval(\n d, axis, \n redStart, redEnd, red, redIndex,\n lo, hi)\n\n //Lower dimensional case\n if(redStart < red0) {\n\n if(d * (red0 - redStart) < BRUTE_FORCE_CUTOFF) {\n //Special case for small inputs: use brute force\n retval = bruteForceFull(\n d, axis+1, visit,\n redStart, red0, red, redIndex,\n blueStart, blueEnd, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n } else if(axis === d-2) {\n if(flip) {\n retval = sweep.sweepBipartite(\n d, visit,\n blueStart, blueEnd, blue, blueIndex,\n redStart, red0, red, redIndex)\n } else {\n retval = sweep.sweepBipartite(\n d, visit,\n redStart, red0, red, redIndex,\n blueStart, blueEnd, blue, blueIndex)\n }\n if(retval !== void 0) {\n return retval\n }\n } else {\n iterPush(top++,\n axis+1,\n redStart, red0,\n blueStart, blueEnd,\n flip,\n -Infinity, Infinity)\n iterPush(top++,\n axis+1,\n blueStart, blueEnd,\n redStart, red0,\n flip^1,\n -Infinity, Infinity)\n }\n }\n\n //Divide and conquer phase\n if(red0 < redEnd) {\n\n //Cut blue into 3 parts:\n //\n // Points < mid point\n // Points = mid point\n // Points > mid point\n //\n var blue0 = findMedian(\n d, axis, \n blueStart, blueEnd, blue, blueIndex)\n var mid = blue[elemSize * blue0 + axis]\n var blue1 = partitionStartEqual(\n d, axis,\n blue0, blueEnd, blue, blueIndex,\n mid)\n\n //Right case\n if(blue1 < blueEnd) {\n iterPush(top++,\n axis,\n red0, redEnd,\n blue1, blueEnd,\n (flip|4) + (full ? 16 : 0),\n mid, hi)\n }\n\n //Left case\n if(blueStart < blue0) {\n iterPush(top++,\n axis,\n red0, redEnd,\n blueStart, blue0,\n (flip|2) + (full ? 16 : 0),\n lo, mid)\n }\n\n //Center case (the hard part)\n if(blue0 + 1 === blue1) {\n //Optimization: Range with exactly 1 point, use a brute force scan\n if(full) {\n retval = onePointFull(\n d, axis, visit,\n red0, redEnd, red, redIndex,\n blue0, blue, blueIndex[blue0])\n } else {\n retval = onePointPartial(\n d, axis, visit, flip,\n red0, redEnd, red, redIndex,\n blue0, blue, blueIndex[blue0])\n }\n if(retval !== void 0) {\n return retval\n }\n } else if(blue0 < blue1) {\n var red1\n if(full) {\n //If full intersection, need to handle special case\n red1 = partitionContainsPoint(\n d, axis,\n red0, redEnd, red, redIndex,\n mid)\n if(red0 < red1) {\n var redX = partitionStartEqual(\n d, axis,\n red0, red1, red, redIndex,\n mid)\n if(axis === d-2) {\n //Degenerate sweep intersection:\n // [red0, redX] with [blue0, blue1]\n if(red0 < redX) {\n retval = sweep.sweepComplete(\n d, visit,\n red0, redX, red, redIndex,\n blue0, blue1, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n }\n\n //Normal sweep intersection:\n // [redX, red1] with [blue0, blue1]\n if(redX < red1) {\n retval = sweep.sweepBipartite(\n d, visit,\n redX, red1, red, redIndex,\n blue0, blue1, blue, blueIndex)\n if(retval !== void 0) {\n return retval\n }\n }\n } else {\n if(red0 < redX) {\n iterPush(top++,\n axis+1,\n red0, redX,\n blue0, blue1,\n 16,\n -Infinity, Infinity)\n }\n if(redX < red1) {\n iterPush(top++,\n axis+1,\n redX, red1,\n blue0, blue1,\n 0,\n -Infinity, Infinity)\n iterPush(top++,\n axis+1,\n blue0, blue1,\n redX, red1,\n 1,\n -Infinity, Infinity)\n }\n }\n }\n } else {\n if(flip) {\n red1 = partitionContainsPointProper(\n d, axis,\n red0, redEnd, red, redIndex,\n mid)\n } else {\n red1 = partitionContainsPoint(\n d, axis,\n red0, redEnd, red, redIndex,\n mid)\n }\n if(red0 < red1) {\n if(axis === d-2) {\n if(flip) {\n retval = sweep.sweepBipartite(\n d, visit,\n blue0, blue1, blue, blueIndex,\n red0, red1, red, redIndex)\n } else {\n retval = sweep.sweepBipartite(\n d, visit,\n red0, red1, red, redIndex,\n blue0, blue1, blue, blueIndex)\n }\n } else {\n iterPush(top++,\n axis+1,\n red0, red1,\n blue0, blue1,\n flip,\n -Infinity, Infinity)\n iterPush(top++,\n axis+1,\n blue0, blue1,\n red0, red1,\n flip^1,\n -Infinity, Infinity)\n }\n }\n }\n }\n }\n }\n}\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/box-intersect/lib/intersect.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/lib/median.js": -/*!*************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/lib/median.js ***! - \*************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = findMedian\n\nvar genPartition = __webpack_require__(/*! ./partition */ \"../../node_modules/box-intersect/lib/partition.js\")\n\nvar partitionStartLessThan = genPartition('lostart && boxes[ptr+axis] > x; \n --j, ptr-=elemSize) {\n //Swap\n var aPtr = ptr\n var bPtr = ptr+elemSize\n for(var k=0; k>> 1)\n var elemSize = 2*d\n var pivot = mid\n var value = boxes[elemSize*mid+axis]\n \n while(lo < hi) {\n if(hi - lo < PARTITION_THRESHOLD) {\n insertionSort(d, axis, lo, hi, boxes, ids)\n value = boxes[elemSize*mid+axis]\n break\n }\n \n //Select pivot using median-of-3\n var count = hi - lo\n var pivot0 = (Math.random()*count+lo)|0\n var value0 = boxes[elemSize*pivot0 + axis]\n var pivot1 = (Math.random()*count+lo)|0\n var value1 = boxes[elemSize*pivot1 + axis]\n var pivot2 = (Math.random()*count+lo)|0\n var value2 = boxes[elemSize*pivot2 + axis]\n if(value0 <= value1) {\n if(value2 >= value1) {\n pivot = pivot1\n value = value1\n } else if(value0 >= value2) {\n pivot = pivot0\n value = value0\n } else {\n pivot = pivot2\n value = value2\n }\n } else {\n if(value1 >= value2) {\n pivot = pivot1\n value = value1\n } else if(value2 >= value0) {\n pivot = pivot0\n value = value0\n } else {\n pivot = pivot2\n value = value2\n }\n }\n\n //Swap pivot to end of array\n var aPtr = elemSize * (hi-1)\n var bPtr = elemSize * pivot\n for(var i=0; i= 0) {\n reads.push('lo=e[k+n]')\n }\n if(predicate.indexOf('hi') >= 0) {\n reads.push('hi=e[k+o]')\n }\n fargs.push(\n code.replace('_', reads.join())\n .replace('$', predicate))\n return Function.apply(void 0, fargs)\n}\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/box-intersect/lib/partition.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/lib/sort.js": -/*!***********************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/lib/sort.js ***! - \***********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n//This code is extracted from ndarray-sort\n//It is inlined here as a temporary workaround\n\nmodule.exports = wrapper;\n\nvar INSERT_SORT_CUTOFF = 32\n\nfunction wrapper(data, n0) {\n if (n0 <= 4*INSERT_SORT_CUTOFF) {\n insertionSort(0, n0 - 1, data);\n } else {\n quickSort(0, n0 - 1, data);\n }\n}\n\nfunction insertionSort(left, right, data) {\n var ptr = 2*(left+1)\n for(var i=left+1; i<=right; ++i) {\n var a = data[ptr++]\n var b = data[ptr++]\n var j = i\n var jptr = ptr-2\n while(j-- > left) {\n var x = data[jptr-2]\n var y = data[jptr-1]\n if(x < a) {\n break\n } else if(x === a && y < b) {\n break\n }\n data[jptr] = x\n data[jptr+1] = y\n jptr -= 2\n }\n data[jptr] = a\n data[jptr+1] = b\n }\n}\n\nfunction swap(i, j, data) {\n i *= 2\n j *= 2\n var x = data[i]\n var y = data[i+1]\n data[i] = data[j]\n data[i+1] = data[j+1]\n data[j] = x\n data[j+1] = y\n}\n\nfunction move(i, j, data) {\n i *= 2\n j *= 2\n data[i] = data[j]\n data[i+1] = data[j+1]\n}\n\nfunction rotate(i, j, k, data) {\n i *= 2\n j *= 2\n k *= 2\n var x = data[i]\n var y = data[i+1]\n data[i] = data[j]\n data[i+1] = data[j+1]\n data[j] = data[k]\n data[j+1] = data[k+1]\n data[k] = x\n data[k+1] = y\n}\n\nfunction shufflePivot(i, j, px, py, data) {\n i *= 2\n j *= 2\n data[i] = data[j]\n data[j] = px\n data[i+1] = data[j+1]\n data[j+1] = py\n}\n\nfunction compare(i, j, data) {\n i *= 2\n j *= 2\n var x = data[i],\n y = data[j]\n if(x < y) {\n return false\n } else if(x === y) {\n return data[i+1] > data[j+1]\n }\n return true\n}\n\nfunction comparePivot(i, y, b, data) {\n i *= 2\n var x = data[i]\n if(x < y) {\n return true\n } else if(x === y) {\n return data[i+1] < b\n }\n return false\n}\n\nfunction quickSort(left, right, data) {\n var sixth = (right - left + 1) / 6 | 0, \n index1 = left + sixth, \n index5 = right - sixth, \n index3 = left + right >> 1, \n index2 = index3 - sixth, \n index4 = index3 + sixth, \n el1 = index1, \n el2 = index2, \n el3 = index3, \n el4 = index4, \n el5 = index5, \n less = left + 1, \n great = right - 1, \n tmp = 0\n if(compare(el1, el2, data)) {\n tmp = el1\n el1 = el2\n el2 = tmp\n }\n if(compare(el4, el5, data)) {\n tmp = el4\n el4 = el5\n el5 = tmp\n }\n if(compare(el1, el3, data)) {\n tmp = el1\n el1 = el3\n el3 = tmp\n }\n if(compare(el2, el3, data)) {\n tmp = el2\n el2 = el3\n el3 = tmp\n }\n if(compare(el1, el4, data)) {\n tmp = el1\n el1 = el4\n el4 = tmp\n }\n if(compare(el3, el4, data)) {\n tmp = el3\n el3 = el4\n el4 = tmp\n }\n if(compare(el2, el5, data)) {\n tmp = el2\n el2 = el5\n el5 = tmp\n }\n if(compare(el2, el3, data)) {\n tmp = el2\n el2 = el3\n el3 = tmp\n }\n if(compare(el4, el5, data)) {\n tmp = el4\n el4 = el5\n el5 = tmp\n }\n\n var pivot1X = data[2*el2]\n var pivot1Y = data[2*el2+1]\n var pivot2X = data[2*el4]\n var pivot2Y = data[2*el4+1]\n\n var ptr0 = 2 * el1;\n var ptr2 = 2 * el3;\n var ptr4 = 2 * el5;\n var ptr5 = 2 * index1;\n var ptr6 = 2 * index3;\n var ptr7 = 2 * index5;\n for (var i1 = 0; i1 < 2; ++i1) {\n var x = data[ptr0+i1];\n var y = data[ptr2+i1];\n var z = data[ptr4+i1];\n data[ptr5+i1] = x;\n data[ptr6+i1] = y;\n data[ptr7+i1] = z;\n }\n\n move(index2, left, data)\n move(index4, right, data)\n for (var k = less; k <= great; ++k) {\n if (comparePivot(k, pivot1X, pivot1Y, data)) {\n if (k !== less) {\n swap(k, less, data)\n }\n ++less;\n } else {\n if (!comparePivot(k, pivot2X, pivot2Y, data)) {\n while (true) {\n if (!comparePivot(great, pivot2X, pivot2Y, data)) {\n if (--great < k) {\n break;\n }\n continue;\n } else {\n if (comparePivot(great, pivot1X, pivot1Y, data)) {\n rotate(k, less, great, data)\n ++less;\n --great;\n } else {\n swap(k, great, data)\n --great;\n }\n break;\n }\n }\n }\n }\n }\n shufflePivot(left, less-1, pivot1X, pivot1Y, data)\n shufflePivot(right, great+1, pivot2X, pivot2Y, data)\n if (less - 2 - left <= INSERT_SORT_CUTOFF) {\n insertionSort(left, less - 2, data);\n } else {\n quickSort(left, less - 2, data);\n }\n if (right - (great + 2) <= INSERT_SORT_CUTOFF) {\n insertionSort(great + 2, right, data);\n } else {\n quickSort(great + 2, right, data);\n }\n if (great - less <= INSERT_SORT_CUTOFF) {\n insertionSort(less, great, data);\n } else {\n quickSort(less, great, data);\n }\n}\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/box-intersect/lib/sort.js?"); - -/***/ }), - -/***/ "../../node_modules/box-intersect/lib/sweep.js": -/*!************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/box-intersect/lib/sweep.js ***! - \************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = {\n init: sqInit,\n sweepBipartite: sweepBipartite,\n sweepComplete: sweepComplete,\n scanBipartite: scanBipartite,\n scanComplete: scanComplete\n}\n\nvar pool = __webpack_require__(/*! typedarray-pool */ \"../../node_modules/typedarray-pool/pool.js\")\nvar bits = __webpack_require__(/*! bit-twiddle */ \"../../node_modules/bit-twiddle/twiddle.js\")\nvar isort = __webpack_require__(/*! ./sort */ \"../../node_modules/box-intersect/lib/sort.js\")\n\n//Flag for blue\nvar BLUE_FLAG = (1<<28)\n\n//1D sweep event queue stuff (use pool to save space)\nvar INIT_CAPACITY = 1024\nvar RED_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)\nvar RED_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)\nvar BLUE_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)\nvar BLUE_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)\nvar COMMON_SWEEP_QUEUE = pool.mallocInt32(INIT_CAPACITY)\nvar COMMON_SWEEP_INDEX = pool.mallocInt32(INIT_CAPACITY)\nvar SWEEP_EVENTS = pool.mallocDouble(INIT_CAPACITY * 8)\n\n//Reserves memory for the 1D sweep data structures\nfunction sqInit(count) {\n var rcount = bits.nextPow2(count)\n if(RED_SWEEP_QUEUE.length < rcount) {\n pool.free(RED_SWEEP_QUEUE)\n RED_SWEEP_QUEUE = pool.mallocInt32(rcount)\n }\n if(RED_SWEEP_INDEX.length < rcount) {\n pool.free(RED_SWEEP_INDEX)\n RED_SWEEP_INDEX = pool.mallocInt32(rcount)\n }\n if(BLUE_SWEEP_QUEUE.length < rcount) {\n pool.free(BLUE_SWEEP_QUEUE)\n BLUE_SWEEP_QUEUE = pool.mallocInt32(rcount)\n }\n if(BLUE_SWEEP_INDEX.length < rcount) {\n pool.free(BLUE_SWEEP_INDEX)\n BLUE_SWEEP_INDEX = pool.mallocInt32(rcount)\n }\n if(COMMON_SWEEP_QUEUE.length < rcount) {\n pool.free(COMMON_SWEEP_QUEUE)\n COMMON_SWEEP_QUEUE = pool.mallocInt32(rcount)\n }\n if(COMMON_SWEEP_INDEX.length < rcount) {\n pool.free(COMMON_SWEEP_INDEX)\n COMMON_SWEEP_INDEX = pool.mallocInt32(rcount)\n }\n var eventLength = 8 * rcount\n if(SWEEP_EVENTS.length < eventLength) {\n pool.free(SWEEP_EVENTS)\n SWEEP_EVENTS = pool.mallocDouble(eventLength)\n }\n}\n\n//Remove an item from the active queue in O(1)\nfunction sqPop(queue, index, count, item) {\n var idx = index[item]\n var top = queue[count-1]\n queue[idx] = top\n index[top] = idx\n}\n\n//Insert an item into the active queue in O(1)\nfunction sqPush(queue, index, count, item) {\n queue[count] = item\n index[item] = count\n}\n\n//Recursion base case: use 1D sweep algorithm\nfunction sweepBipartite(\n d, visit,\n redStart, redEnd, red, redIndex,\n blueStart, blueEnd, blue, blueIndex) {\n\n //store events as pairs [coordinate, idx]\n //\n // red create: -(idx+1)\n // red destroy: idx\n // blue create: -(idx+BLUE_FLAG)\n // blue destroy: idx+BLUE_FLAG\n //\n var ptr = 0\n var elemSize = 2*d\n var istart = d-1\n var iend = elemSize-1\n\n for(var i=redStart; iright\n var n = ptr >>> 1\n isort(SWEEP_EVENTS, n)\n \n var redActive = 0\n var blueActive = 0\n for(var i=0; i= BLUE_FLAG) {\n //blue destroy event\n e = (e-BLUE_FLAG)|0\n sqPop(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive--, e)\n } else if(e >= 0) {\n //red destroy event\n sqPop(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive--, e)\n } else if(e <= -BLUE_FLAG) {\n //blue create event\n e = (-e-BLUE_FLAG)|0\n for(var j=0; jright\n var n = ptr >>> 1\n isort(SWEEP_EVENTS, n)\n \n var redActive = 0\n var blueActive = 0\n var commonActive = 0\n for(var i=0; i>1) === (SWEEP_EVENTS[2*i+3]>>1)) {\n color = 2\n i += 1\n }\n \n if(e < 0) {\n //Create event\n var id = -(e>>1) - 1\n\n //Intersect with common\n for(var j=0; j>1) - 1\n if(color === 0) {\n //Red\n sqPop(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive--, id)\n } else if(color === 1) {\n //Blue\n sqPop(BLUE_SWEEP_QUEUE, BLUE_SWEEP_INDEX, blueActive--, id)\n } else if(color === 2) {\n //Both\n sqPop(COMMON_SWEEP_QUEUE, COMMON_SWEEP_INDEX, commonActive--, id)\n }\n }\n }\n}\n\n//Sweep and prune/scanline algorithm:\n// Scan along axis, detect intersections\n// Brute force all boxes along axis\nfunction scanBipartite(\n d, axis, visit, flip,\n redStart, redEnd, red, redIndex,\n blueStart, blueEnd, blue, blueIndex) {\n \n var ptr = 0\n var elemSize = 2*d\n var istart = axis\n var iend = axis+d\n\n var redShift = 1\n var blueShift = 1\n if(flip) {\n blueShift = BLUE_FLAG\n } else {\n redShift = BLUE_FLAG\n }\n\n for(var i=redStart; iright\n var n = ptr >>> 1\n isort(SWEEP_EVENTS, n)\n \n var redActive = 0\n for(var i=0; i= BLUE_FLAG) {\n isRed = !flip\n idx -= BLUE_FLAG \n } else {\n isRed = !!flip\n idx -= 1\n }\n if(isRed) {\n sqPush(RED_SWEEP_QUEUE, RED_SWEEP_INDEX, redActive++, idx)\n } else {\n var blueId = blueIndex[idx]\n var bluePtr = elemSize * idx\n \n var b0 = blue[bluePtr+axis+1]\n var b1 = blue[bluePtr+axis+1+d]\n\nred_loop:\n for(var j=0; jright\n var n = ptr >>> 1\n isort(SWEEP_EVENTS, n)\n \n var redActive = 0\n for(var i=0; i= BLUE_FLAG) {\n RED_SWEEP_QUEUE[redActive++] = idx - BLUE_FLAG\n } else {\n idx -= 1\n var blueId = blueIndex[idx]\n var bluePtr = elemSize * idx\n\n var b0 = blue[bluePtr+axis+1]\n var b1 = blue[bluePtr+axis+1+d]\n\nred_loop:\n for(var j=0; j=0; --j) {\n if(RED_SWEEP_QUEUE[j] === idx) {\n for(var k=j+1; k \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n\n\nvar base64 = __webpack_require__(/*! base64-js */ \"../../node_modules/base64-js/index.js\")\nvar ieee754 = __webpack_require__(/*! ieee754 */ \"../../node_modules/ieee754/index.js\")\nvar isArray = __webpack_require__(/*! isarray */ \"../../node_modules/isarray/index.js\")\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Use Object implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * Due to various browser bugs, sometimes the Object implementation will be used even\n * when the browser supports typed arrays.\n *\n * Note:\n *\n * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,\n * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.\n *\n * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.\n *\n * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of\n * incorrect length in some situations.\n\n * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they\n * get the Object implementation, which is slower but behaves correctly.\n */\nBuffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined\n ? global.TYPED_ARRAY_SUPPORT\n : typedArraySupport()\n\n/*\n * Export kMaxLength after typed array support is determined.\n */\nexports.kMaxLength = kMaxLength()\n\nfunction typedArraySupport () {\n try {\n var arr = new Uint8Array(1)\n arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}\n return arr.foo() === 42 && // typed array instances can be augmented\n typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`\n arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`\n } catch (e) {\n return false\n }\n}\n\nfunction kMaxLength () {\n return Buffer.TYPED_ARRAY_SUPPORT\n ? 0x7fffffff\n : 0x3fffffff\n}\n\nfunction createBuffer (that, length) {\n if (kMaxLength() < length) {\n throw new RangeError('Invalid typed array length')\n }\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = new Uint8Array(length)\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n if (that === null) {\n that = new Buffer(length)\n }\n that.length = length\n }\n\n return that\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {\n return new Buffer(arg, encodingOrOffset, length)\n }\n\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new Error(\n 'If encoding is specified then the first argument must be a string'\n )\n }\n return allocUnsafe(this, arg)\n }\n return from(this, arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\n// TODO: Legacy, not needed anymore. Remove in next major version.\nBuffer._augment = function (arr) {\n arr.__proto__ = Buffer.prototype\n return arr\n}\n\nfunction from (that, value, encodingOrOffset, length) {\n if (typeof value === 'number') {\n throw new TypeError('\"value\" argument must not be a number')\n }\n\n if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {\n return fromArrayBuffer(that, value, encodingOrOffset, length)\n }\n\n if (typeof value === 'string') {\n return fromString(that, value, encodingOrOffset)\n }\n\n return fromObject(that, value)\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(null, value, encodingOrOffset, length)\n}\n\nif (Buffer.TYPED_ARRAY_SUPPORT) {\n Buffer.prototype.__proto__ = Uint8Array.prototype\n Buffer.__proto__ = Uint8Array\n if (typeof Symbol !== 'undefined' && Symbol.species &&\n Buffer[Symbol.species] === Buffer) {\n // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97\n Object.defineProperty(Buffer, Symbol.species, {\n value: null,\n configurable: true\n })\n }\n}\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be a number')\n } else if (size < 0) {\n throw new RangeError('\"size\" argument must not be negative')\n }\n}\n\nfunction alloc (that, size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(that, size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpretted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(that, size).fill(fill, encoding)\n : createBuffer(that, size).fill(fill)\n }\n return createBuffer(that, size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(null, size, fill, encoding)\n}\n\nfunction allocUnsafe (that, size) {\n assertSize(size)\n that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) {\n for (var i = 0; i < size; ++i) {\n that[i] = 0\n }\n }\n return that\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(null, size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(null, size)\n}\n\nfunction fromString (that, string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('\"encoding\" must be a valid string encoding')\n }\n\n var length = byteLength(string, encoding) | 0\n that = createBuffer(that, length)\n\n var actual = that.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n that = that.slice(0, actual)\n }\n\n return that\n}\n\nfunction fromArrayLike (that, array) {\n var length = array.length < 0 ? 0 : checked(array.length) | 0\n that = createBuffer(that, length)\n for (var i = 0; i < length; i += 1) {\n that[i] = array[i] & 255\n }\n return that\n}\n\nfunction fromArrayBuffer (that, array, byteOffset, length) {\n array.byteLength // this throws if `array` is not a valid ArrayBuffer\n\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\\'offset\\' is out of bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\\'length\\' is out of bounds')\n }\n\n if (byteOffset === undefined && length === undefined) {\n array = new Uint8Array(array)\n } else if (length === undefined) {\n array = new Uint8Array(array, byteOffset)\n } else {\n array = new Uint8Array(array, byteOffset, length)\n }\n\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = array\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n that = fromArrayLike(that, array)\n }\n return that\n}\n\nfunction fromObject (that, obj) {\n if (Buffer.isBuffer(obj)) {\n var len = checked(obj.length) | 0\n that = createBuffer(that, len)\n\n if (that.length === 0) {\n return that\n }\n\n obj.copy(that, 0, 0, len)\n return that\n }\n\n if (obj) {\n if ((typeof ArrayBuffer !== 'undefined' &&\n obj.buffer instanceof ArrayBuffer) || 'length' in obj) {\n if (typeof obj.length !== 'number' || isnan(obj.length)) {\n return createBuffer(that, 0)\n }\n return fromArrayLike(that, obj)\n }\n\n if (obj.type === 'Buffer' && isArray(obj.data)) {\n return fromArrayLike(that, obj.data)\n }\n }\n\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')\n}\n\nfunction checked (length) {\n // Note: cannot use `length < kMaxLength()` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= kMaxLength()) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + kMaxLength().toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return !!(b != null && b._isBuffer)\n}\n\nBuffer.compare = function compare (a, b) {\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError('Arguments must be Buffers')\n }\n\n if (a === b) return 0\n\n var x = a.length\n var y = b.length\n\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n var i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n var buffer = Buffer.allocUnsafe(length)\n var pos = 0\n for (i = 0; i < list.length; ++i) {\n var buf = list[i]\n if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n buf.copy(buffer, pos)\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&\n (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n string = '' + string\n }\n\n var len = string.length\n if (len === 0) return 0\n\n // Use a for loop to avoid recursion\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n case undefined:\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) return utf8ToBytes(string).length // assume utf8\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n var loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coersion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect\n// Buffer instances.\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n var i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n var len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (var i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n var len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (var i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n var len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (var i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n var length = this.length | 0\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n var str = ''\n var max = exports.INSPECT_MAX_BYTES\n if (this.length > 0) {\n str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')\n if (this.length > max) str += ' ... '\n }\n return ''\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (!Buffer.isBuffer(target)) {\n throw new TypeError('Argument must be a Buffer')\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n var x = thisEnd - thisStart\n var y = end - start\n var len = Math.min(x, y)\n\n var thisCopy = this.slice(thisStart, thisEnd)\n var targetCopy = target.slice(start, end)\n\n for (var i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (isNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (Buffer.TYPED_ARRAY_SUPPORT &&\n typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n var indexSize = 1\n var arrLength = arr.length\n var valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n var i\n if (dir) {\n var foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n var found = true\n for (var j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n var remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n // must be an even number of digits\n var strLen = string.length\n if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n for (var i = 0; i < length; ++i) {\n var parsed = parseInt(string.substr(i * 2, 2), 16)\n if (isNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction latin1Write (buf, string, offset, length) {\n return asciiWrite(buf, string, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset | 0\n if (isFinite(length)) {\n length = length | 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n // legacy write(string, encoding, offset, length) - remove in v0.13\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n var remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n return asciiWrite(this, string, offset, length)\n\n case 'latin1':\n case 'binary':\n return latin1Write(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n var res = []\n\n var i = start\n while (i < end) {\n var firstByte = buf[i]\n var codePoint = null\n var bytesPerSequence = (firstByte > 0xEF) ? 4\n : (firstByte > 0xDF) ? 3\n : (firstByte > 0xBF) ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n var secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n var len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n var res = ''\n var i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n var len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n var out = ''\n for (var i = start; i < end; ++i) {\n out += toHex(buf[i])\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n var bytes = buf.slice(start, end)\n var res = ''\n for (var i = 0; i < bytes.length; i += 2) {\n res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n var len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n var newBuf\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n newBuf = this.subarray(start, end)\n newBuf.__proto__ = Buffer.prototype\n } else {\n var sliceLen = end - start\n newBuf = new Buffer(sliceLen, undefined)\n for (var i = 0; i < sliceLen; ++i) {\n newBuf[i] = this[i + start]\n }\n }\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n var val = this[offset + --byteLength]\n var mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var i = byteLength\n var mul = 1\n var val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var mul = 1\n var i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var i = byteLength - 1\n var mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nfunction objectWriteUInt16 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {\n buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>\n (littleEndian ? i : 1 - i) * 8\n }\n}\n\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nfunction objectWriteUInt32 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffffffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {\n buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff\n }\n}\n\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = 0\n var mul = 1\n var sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = byteLength - 1\n var mul = 1\n var sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n var len = end - start\n var i\n\n if (this === target && start < targetStart && targetStart < end) {\n // descending copy from end\n for (i = len - 1; i >= 0; --i) {\n target[i + targetStart] = this[i + start]\n }\n } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {\n // ascending copy from start\n for (i = 0; i < len; ++i) {\n target[i + targetStart] = this[i + start]\n }\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, start + len),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (val.length === 1) {\n var code = val.charCodeAt(0)\n if (code < 256) {\n val = code\n }\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n } else if (typeof val === 'number') {\n val = val & 255\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n var i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n var bytes = Buffer.isBuffer(val)\n ? val\n : utf8ToBytes(new Buffer(val, encoding).toString())\n var len = bytes.length\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+\\/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = stringtrim(str).replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction stringtrim (str) {\n if (str.trim) return str.trim()\n return str.replace(/^\\s+|\\s+$/g, '')\n}\n\nfunction toHex (n) {\n if (n < 16) return '0' + n.toString(16)\n return n.toString(16)\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n var codePoint\n var length = string.length\n var leadSurrogate = null\n var bytes = []\n\n for (var i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n var c, hi, lo\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n for (var i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\nfunction isnan (val) {\n return val !== val // eslint-disable-line no-self-compare\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ \"../../node_modules/webpack/buildin/global.js\")))\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/buffer/index.js?"); - -/***/ }), - -/***/ "../../node_modules/domready/ready.js": -/*!***************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/domready/ready.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("/*!\n * domready (c) Dustin Diaz 2014 - License MIT\n */\n!function (name, definition) {\n\n if (true) module.exports = definition()\n else {}\n\n}('domready', function () {\n\n var fns = [], listener\n , doc = document\n , hack = doc.documentElement.doScroll\n , domContentLoaded = 'DOMContentLoaded'\n , loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState)\n\n\n if (!loaded)\n doc.addEventListener(domContentLoaded, listener = function () {\n doc.removeEventListener(domContentLoaded, listener)\n loaded = 1\n while (listener = fns.shift()) listener()\n })\n\n return function (fn) {\n loaded ? setTimeout(fn, 0) : fns.push(fn)\n }\n\n});\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/domready/ready.js?"); - -/***/ }), - -/***/ "../../node_modules/dup/dup.js": -/*!********************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/dup/dup.js ***! - \********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nfunction dupe_array(count, value, i) {\n var c = count[i]|0\n if(c <= 0) {\n return []\n }\n var result = new Array(c), j\n if(i === count.length-1) {\n for(j=0; j 0) {\n return dupe_number(count|0, value)\n }\n break\n case \"object\":\n if(typeof (count.length) === \"number\") {\n return dupe_array(count, value, 0)\n }\n break\n }\n return []\n}\n\nmodule.exports = dupe\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/dup/dup.js?"); - -/***/ }), - -/***/ "../../node_modules/ent-comp/src/ECS.js": -/*!*****************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/ent-comp/src/ECS.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = ECS\nvar DataStore = __webpack_require__(/*! ./dataStore */ \"../../node_modules/ent-comp/src/dataStore.js\")\n\n\n\n/**\n * \n * # ent-comp API Documentation:\n * \n*/\n\n\n\n/**\n * @class ECS\n * \n * Creates a new entity-component-system manager.\n * \n * ```js\n * var ECS = require('ent-comp')\n * var ecs = new ECS()\n * ```\n*/\n\nfunction ECS() {\n\tvar self = this\n\n\t/** \n\t * Hash of component definitions. Also aliased to `comps`.\n\t * \n\t * ```js\n\t * var comp = { name: 'foo' }\n\t * ecs.createComponent(comp)\n\t * ecs.components['foo'] === comp // true\n\t * ecs.comps['foo'] // same\n\t * ```\n\t*/\n\tthis.components = {}\n\tthis.comps = this.components\n\n\n\n\t/*\n\t * \n\t * \t\tinternals:\n\t * \n\t*/\n\n\tvar components = this.components\n\n\t// counter for entity IDs\n\tvar UID = 1\n\n\t// Storage for all component state data:\n\t// storage['component-name'] = { hash:{}, list:[] }\n\tvar storage = {}\n\n\t// flat arrays of names of components with systems\n\tvar systems = []\n\tvar renderSystems = []\n\n\t// queues for deferred operations\n\tvar deferredEntityRemovals = []\n\tvar deferredCompRemovals = []\n\tvar deferredMultiCompRemovals = []\n\n\n\n\t/*\n\t * \n\t * \n\t * \t\t\t\tPublic API\n\t * \n\t * \n\t*/\n\n\n\n\n\t/**\n\t * Creates a new entity id (currently just an incrementing integer).\n\t * \n\t * Optionally takes a list of component names to add to the entity (with default state data).\n\t * \n\t * ```js\n\t * var id1 = ecs.createEntity()\n\t * var id2 = ecs.createEntity([ 'some-component', 'other-component' ])\n\t * ```\n\t*/\n\tthis.createEntity = function (compList) {\n\t\tvar id = UID++\n\t\tif (compList && compList.length) {\n\t\t\tcompList.forEach(compName => self.addComponent(id, compName))\n\t\t}\n\t\treturn id\n\t}\n\n\n\n\t/**\n\t * Deletes an entity, which in practice just means removing all its components.\n\t * By default the actual removal is deferred (since entities will tend to \n\t * call this on themselves during event handlers, etc).\n\t * Pass a truthy second parameter to force immediate removal.\n\t * \n\t * ```js\n\t * ecs.deleteEntity(id)\n\t * ecs.deleteEntity(id2, true) // deletes immediately\n\t * ```\n\t * \n\t * Note that if you need to delete large numbers of entities at once,\n\t * and you know which components they have, this method will be a bit \n\t * slower than removing the components manually.\n\t*/\n\tthis.deleteEntity = function (entID, immediately) {\n\t\tif (immediately) {\n\t\t\tdeleteEntityNow(entID)\n\t\t} else {\n\t\t\tdeferredEntityRemovals.push(entID)\n\t\t\tmakeDeferralTimeout()\n\t\t}\n\t\treturn self\n\t}\n\n\n\n\n\n\n\n\t/**\n\t * Creates a new component from a definition object. \n\t * The definition must have a `name`; all other properties are optional.\n\t * \n\t * Returns the component name, to make it easy to grab when the component\n\t * is being `require`d from a module.\n\t * \n\t * ```js\n\t * var comp = {\n\t * \t name: 'some-unique-string',\n\t * \t state: {},\n\t * \t onAdd: function(id, state){ },\n\t * \t onRemove: function(id, state){ },\n\t * \t system: function(dt, states){ },\n\t * \t renderSystem: function(dt, states){ },\n\t * \t multi: false,\n\t * }\n\t * \n\t * var name = ecs.createComponent( comp )\n\t * // name == 'a-unique-string'\n\t * ```\n\t * \n\t * Note the `multi` flag - for components where this is true, a given \n\t * entity can have multiple state objects for that component.\n\t * For multi-components, APIs that would normally return a state object \n\t * (like `getState`) will instead return an array of them.\n\t*/\n\tthis.createComponent = function (compDefn) {\n\t\tif (!compDefn) throw 'Missing component definition'\n\t\tvar name = compDefn.name\n\t\tif (!name) throw 'Component definition must have a name property.'\n\t\tif (typeof name !== 'string') throw 'Component name must be a string.'\n\t\tif (name === '') throw 'Component name must be a non-empty string.'\n\t\tif (storage[name]) throw `Component ${name} already exists.`\n\n\t\t// rebuild definition object for monomorphism\n\t\tvar internalDef = {}\n\t\tinternalDef.name = name\n\t\tinternalDef.state = compDefn.state || {}\n\t\tinternalDef.onAdd = compDefn.onAdd || null\n\t\tinternalDef.onRemove = compDefn.onRemove || null\n\t\tinternalDef.system = compDefn.system || null\n\t\tinternalDef.renderSystem = compDefn.renderSystem || null\n\t\tinternalDef.multi = !!compDefn.multi\n\n\t\tcomponents[name] = internalDef\n\t\tstorage[name] = DataStore.create()\n\n\t\tif (internalDef.system) systems.push(name)\n\t\tif (internalDef.renderSystem) renderSystems.push(name)\n\n\t\treturn name\n\t}\n\n\n\n\n\n\t/**\n\t * Deletes the component definition with the given name. \n\t * First removes the component from all entities that have it.\n\t * This probably shouldn't be called in real-world usage\n\t * (better to define all components when you begin and leave them be)\n\t * but it's here if you need it.\n\t * \n\t * ```js\n\t * ecs.deleteComponent( comp.name )\n\t * ```\n\t*/\n\tthis.deleteComponent = function (compName) {\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}`\n\n\t\tdata.list.forEach(obj => {\n\t\t\tvar id = obj.__id || obj[0].__id\n\t\t\tself.removeComponent(id, compName, true)\n\t\t})\n\n\t\tvar i = systems.indexOf(compName)\n\t\tvar j = renderSystems.indexOf(compName)\n\t\tif (i > -1) systems.splice(i, 1)\n\t\tif (j > -1) renderSystems.splice(j, 1)\n\n\t\tdelete components[compName]\n\t\tdelete storage[compName]\n\n\t\treturn self\n\t}\n\n\n\n\n\t/**\n\t * Adds a component to an entity, optionally initializing the state object.\n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: 'foo',\n\t * \tstate: { val: 0 }\n\t * })\n\t * ecs.addComponent(id, 'foo', {val:20})\n\t * ecs.getState(id, 'foo').val // 20\n\t * ```\n\t*/\n\tthis.addComponent = function (entID, compName, state) {\n\t\tvar def = components[compName]\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\n\t\t// if the component is pending removal, remove it so it can be readded\n\t\tvar pendingRemoval = false\n\t\tdeferredCompRemovals.forEach(obj => {\n\t\t\tif (obj.id === entID && obj.compName === compName) pendingRemoval = true\n\t\t})\n\t\tif (pendingRemoval) doDeferredComponentRemovals()\n\n\t\tif (data.hash[entID] && !def.multi) throw `Entity ${entID} already has component: ${compName}.`\n\n\t\t// create new component state object for this entity\n\t\tvar newState = Object.assign({}, { __id: entID }, def.state, state)\n\n\t\t// just in case passed-in state object had an __id property\n\t\tnewState.__id = entID\n\n\t\t// add to dataStore - for multi components, may already be present\n\t\tif (def.multi) {\n\t\t\tvar statesArr = data.hash[entID]\n\t\t\tif (!statesArr) {\n\t\t\t\tstatesArr = []\n\t\t\t\tDataStore.add(data, entID, statesArr)\n\t\t\t}\n\t\t\tstatesArr.push(newState)\n\t\t} else {\n\t\t\tDataStore.add(data, entID, newState)\n\t\t}\n\n\t\t// call handler and return\n\t\tif (def.onAdd) def.onAdd(entID, newState)\n\n\t\treturn this\n\t}\n\n\n\n\t/**\n\t * Checks if an entity has a component.\n\t * \n\t * ```js\n\t * ecs.addComponent(id, 'foo')\n\t * ecs.hasComponent(id, 'foo') // true\n\t * ```\n\t*/\n\n\tthis.hasComponent = function (entID, compName) {\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\t\treturn (data.hash[entID] !== undefined)\n\t}\n\n\n\n\n\n\t/**\n\t * Removes a component from an entity, deleting any state data.\n\t * \n\t * ```js\n\t * ecs.removeComponent(id, 'foo', true) // final arg means \"immediately\"\n\t * ecs.hasComponent(id, 'foo') // false\n\t * ecs.removeComponent(id, 'bar')\n\t * ecs.hasComponent(id, 'bar') // true - by default the removal is asynchronous\n\t * ```\n\t*/\n\tthis.removeComponent = function (entID, compName, immediately) {\n\t\tvar def = components[compName]\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\n\t\t// if comp isn't present, fail silently for multi or throw otherwise\n\t\tif (!data.hash[entID]) {\n\t\t\tif (def.multi) return self\n\t\t\telse throw `Entity ${entID} does not have component: ${compName} to remove.`\n\t\t}\n\n\t\t// defer or remove\n\t\tif (immediately) {\n\t\t\tremoveComponentNow(entID, compName)\n\t\t} else {\n\t\t\tdeferredCompRemovals.push({\n\t\t\t\tid: entID,\n\t\t\t\tcompName: compName,\n\t\t\t})\n\t\t\tmakeDeferralTimeout()\n\t\t}\n\n\t\treturn self\n\t}\n\n\n\n\n\n\t/**\n\t * Get the component state for a given entity.\n\t * It will automatically have an `__id` property for the entity id.\n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: 'foo',\n\t * \tstate: { val: 0 }\n\t * })\n\t * ecs.addComponent(id, 'foo')\n\t * ecs.getState(id, 'foo').val // 0\n\t * ecs.getState(id, 'foo').__id // equals id\n\t * ```\n\t*/\n\n\tthis.getState = function (entID, compName) {\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\t\treturn data.hash[entID]\n\t}\n\n\n\n\n\t/**\n\t * Get an array of state objects for every entity with the given component. \n\t * Each one will have an `__id` property for the entity id it refers to.\n\t * Don't add or remove elements from the returned list!\n\t * \n\t * ```js\n\t * var arr = ecs.getStatesList('foo')\n\t * // returns something shaped like:\n\t * // [ {__id:0, x:1}, {__id:7, x:2} ]\n\t * ``` \n\t*/\n\n\tECS.prototype.getStatesList = function (compName) {\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\t\treturn data.list\n\t}\n\n\n\n\n\t/**\n\t * Returns a `getState`-like accessor bound to a given component name. \n\t * The accessor is much faster than `getState`, so you should create an accessor \n\t * for any component whose state you'll be accessing a lot.\n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: 'size',\n\t * \tstate: { val: 0 }\n\t * })\n\t * ecs.addComponent(id, 'size')\n\t * var getSize = ecs.getStateAccessor('size')\n\t * getSize(id).val // 0\n\t * ``` \n\t*/\n\n\tthis.getStateAccessor = function (compName) {\n\t\tif (!storage[compName]) throw `Unknown component: ${compName}.`\n\t\tvar hash = storage[compName].hash\n\t\treturn function (entID) {\n\t\t\treturn hash[entID]\n\t\t}\n\t}\n\n\n\n\n\t/**\n\t * Returns a `hasComponent`-like accessor function bound to a given component name. \n\t * The accessor is much faster than `hasComponent`.\n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: 'foo',\n\t * })\n\t * ecs.addComponent(id, 'foo')\n\t * var hasFoo = ecs.getComponentAccessor('foo')\n\t * hasFoo(id) // true\n\t * ``` \n\t*/\n\n\tthis.getComponentAccessor = function (compName) {\n\t\tif (!storage[compName]) throw `Unknown component: ${compName}.`\n\t\tvar hash = storage[compName].hash\n\t\treturn function (entID) {\n\t\t\treturn (hash[entID] !== undefined) // TODO\n\t\t}\n\t}\n\n\n\n\n\n\t/**\n\t * Tells the ECS that a game tick has occurred, causing component \n\t * `system` functions to get called.\n\t * \n\t * The optional parameter simply gets passed to the system functions. \n\t * It's meant to be a timestep, but can be used (or not used) as you like. \n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: foo,\n\t * \tsystem: function(dt, states) {\n\t * \t\t// states is the same array you'd get from #getStatesList()\n\t * \t\tstates.forEach(state => {\n\t * \t\t\tconsole.log('Entity ID: ', state.__id)\n\t * \t\t})\n\t * \t}\n\t * })\n\t * ecs.tick(30) // triggers log statements\n\t * ```\n\t*/\n\n\tthis.tick = function (dt) {\n\t\trunAllDeferredRemovals()\n\t\tsystems.forEach(compName => {\n\t\t\tvar list = storage[compName].list\n\t\t\tvar comp = components[compName]\n\t\t\tcomp.system(dt, list)\n\t\t})\n\t\trunAllDeferredRemovals()\n\t\treturn self\n\t}\n\n\n\n\t/**\n\t * Functions exactly like `tick`, but calls `renderSystem` functions.\n\t * this effectively gives you a second set of systems that are \n\t * called with separate timing, in case you want to \n\t * [tick and render in separate loops](http://gafferongames.com/game-physics/fix-your-timestep/)\n\t * (which you should!).\n\t * \n\t * ```js\n\t * ecs.createComponent({\n\t * \tname: foo,\n\t * \trenderSystem: function(dt, states) {\n\t * \t\t// states is the same array you'd get from #getStatesList()\n\t * \t}\n\t * })\n\t * ecs.render(16.666)\n\t * ```\n\t*/\n\n\tthis.render = function (dt) {\n\t\trunAllDeferredRemovals()\n\t\trenderSystems.forEach(compName => {\n\t\t\tvar list = storage[compName].list\n\t\t\tvar comp = components[compName]\n\t\t\tcomp.renderSystem(dt, list)\n\t\t})\n\t\trunAllDeferredRemovals()\n\t\treturn self\n\t}\n\n\n\n\n\t/**\n\t * Removes a particular state instance of a multi-component.\n\t * Pass a final truthy argument to make this happen synchronously - \n\t * but be careful, that will splice an element out of the multi-component array,\n\t * changing the indexes of subsequent elements.\n\t * \n\t * ```js\n\t * ecs.getState(id, 'foo') // [ state1, state2, state3 ]\n\t * ecs.removeMultiComponent(id, 'foo', 1, true) // true means: immediately\n\t * ecs.getState(id, 'foo') // [ state1, state3 ]\n\t * ```\n\t */\n\tthis.removeMultiComponent = function (entID, compName, index, immediately) {\n\t\tvar def = components[compName]\n\t\tvar data = storage[compName]\n\t\tif (!data) throw `Unknown component: ${compName}.`\n\t\tif (!def.multi) throw 'removeMultiComponent called on non-multi component'\n\n\t\t// throw if comp isn't present, or multicomp isn't present at index\n\t\tvar statesArr = data.hash[entID]\n\t\tif (!statesArr || !statesArr[index]) {\n\t\t\tthrow `Multicomponent ${compName} instance not found at index ${index}`\n\t\t}\n\n\t\t// index removals by object, in case indexes change later\n\t\tvar stateToRemove = statesArr[index]\n\t\tif (immediately) {\n\t\t\tremoveMultiCompNow(entID, compName, stateToRemove)\n\t\t} else {\n\t\t\tdeferredMultiCompRemovals.push({\n\t\t\t\tid: entID,\n\t\t\t\tcompName: compName,\n\t\t\t\tstate: stateToRemove,\n\t\t\t})\n\t\t}\n\n\t\treturn self\n\t}\n\n\n\n\n\n\n\n\t/*\n\t * \n\t * \n\t *\t\tinternal implementation of various delete operations\n\t * \n\t * \n\t*/\n\n\n\t// delete entity - meaning simply remove all its components\n\tfunction deleteEntityNow(entID) {\n\t\t// For now loop over all components\n\t\t// Could speed this up by keeping a hash of components held by each entity?\n\t\tObject.keys(storage).forEach(compName => {\n\t\t\tvar data = storage[compName]\n\t\t\tif (data.hash[entID]) removeComponentNow(entID, compName)\n\t\t})\n\t}\n\n\n\t// remove given component from an entity\n\tfunction removeComponentNow(entID, compName) {\n\t\tvar def = components[compName]\n\t\tvar data = storage[compName]\n\t\tif (!data) return\n\t\tif (!data.hash[entID]) return // probably got removed twice during deferral\n\n\t\t// call onRemove handler - on each instance for multi components\n\t\tif (def.onRemove) {\n\t\t\tif (def.multi) {\n\t\t\t\tdata.hash[entID].forEach(state => {\n\t\t\t\t\tdef.onRemove(entID, state)\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tdef.onRemove(entID, data.hash[entID])\n\t\t\t}\n\t\t}\n\n\t\t// if multi, kill the states array to hopefully free the objects\n\t\tif (def.multi) data.hash[entID].length = 0\n\n\t\t// actual removal from data store\n\t\tDataStore.remove(data, entID)\n\t}\n\n\n\n\t// remove one state from a multi component\n\tfunction removeMultiCompNow(entID, compName, stateObj) {\n\t\tvar def = components[compName]\n\t\tvar data = storage[compName]\n\t\tvar statesArr = data.hash[entID]\n\t\tif (!statesArr) return\n\n\t\tvar i = statesArr.indexOf(stateObj)\n\t\tif (i < 0) return\n\t\tif (def.onRemove) {\n\t\t\tdef.onRemove(entID, stateObj)\n\t\t}\n\t\tstatesArr.splice(i, 1)\n\n\t\t// if this leaves the states list empty, remove the whole component\n\t\tif (statesArr.length === 0) {\n\t\t\tremoveComponentNow(entID, compName)\n\t\t}\n\t}\n\n\n\n\n\n\n\t/*\n\t * \n\t * \n\t *\t\tinternals for handling deferrals\n\t * \n\t * \n\t*/\n\n\n\n\t// debouncer - called whenever a deferral is queued\n\tfunction makeDeferralTimeout() {\n\t\tif (deferralTimeoutPending) return\n\t\tdeferralTimeoutPending = true\n\t\tsetTimeout(function () {\n\t\t\tdeferralTimeoutPending = false\n\t\t\trunAllDeferredRemovals()\n\t\t}, 1)\n\t}\n\tvar deferralTimeoutPending = false\n\n\n\t// Ping all removal queues. \n\t// called before and after tick/render, and after deferrals are queued\n\tfunction runAllDeferredRemovals() {\n\t\tdoDeferredComponentRemovals()\n\t\tdoDeferredMultiComponentRemovals()\n\t\tdoDeferredEntityRemovals()\n\t}\n\n\n\t// entities - queue of entity IDs\n\tfunction doDeferredEntityRemovals() {\n\t\twhile (deferredEntityRemovals.length) {\n\t\t\tvar entID = deferredEntityRemovals.pop()\n\t\t\tdeleteEntityNow(entID)\n\t\t}\n\t}\n\n\n\t// components - queue of { id, compName }\n\tfunction doDeferredComponentRemovals() {\n\t\twhile (deferredCompRemovals.length) {\n\t\t\tvar obj = deferredCompRemovals.pop()\n\t\t\tremoveComponentNow(obj.id, obj.compName)\n\t\t}\n\t}\n\n\n\t// multi components - queue of { id, compName, state }\n\tfunction doDeferredMultiComponentRemovals(ecs) {\n\t\twhile (deferredMultiCompRemovals.length) {\n\t\t\tvar obj = deferredMultiCompRemovals.pop()\n\t\t\tremoveMultiCompNow(obj.id, obj.compName, obj.state)\n\t\t\tobj.state = null\n\t\t}\n\t}\n\n\n\n}\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/ent-comp/src/ECS.js?"); - -/***/ }), - -/***/ "../../node_modules/ent-comp/src/dataStore.js": -/*!***********************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/ent-comp/src/dataStore.js ***! - \***********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n/*\n * \n * Encapsulates (mostly) a collection of objects, \n * exposed both as a hash and as an array\n * _map maps hash id to list index\n * \n * Note this is a dumb store, it doesn't check any inputs at all.\n * It also assumes every stored data object is stored like:\n * DataStore.add(data, 37, {__id:37} )\n * \n*/\n\n\nmodule.exports = {\n\n create: function () {\n return {\n list: [],\n hash: {},\n _map: {},\n }\n },\n\n\n add: function (data, id, object) {\n data.list.push(object)\n data.hash[id] = object\n data._map[id] = data.list.length - 1\n },\n\n\n remove: function (data, id) {\n // splice out of list\n var index = data._map[id]\n if (index === data.list.length - 1) {\n data.list.pop()\n } else {\n // replace element to be spliced with element from end\n var movedItem = data.list.pop()\n data.list[index] = movedItem\n // watch out, this bit breaks encapsulation by assuming object's contents\n // alternative would be to look through map for movedID's index\n var movedID = movedItem.__id || movedItem[0].__id\n data._map[movedID] = index\n }\n // finish\n delete data.hash[id]\n delete data._map[id]\n },\n\n\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/ent-comp/src/dataStore.js?"); - -/***/ }), - -/***/ "../../node_modules/events/events.js": -/*!**************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/events/events.js ***! - \**************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction $getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return $getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = $getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n var args = [];\n for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n ReflectApply(this.listener, this.target, args);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/events/events.js?"); - -/***/ }), - -/***/ "../../node_modules/fast-voxel-raycast/index.js": -/*!*************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/fast-voxel-raycast/index.js ***! - \*************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nfunction traceRay_impl( getVoxel,\n\tpx, py, pz,\n\tdx, dy, dz,\n\tmax_d, hit_pos, hit_norm) {\n\t\n\t// consider raycast vector to be parametrized by t\n\t// vec = [px,py,pz] + t * [dx,dy,dz]\n\t\n\t// algo below is as described by this paper:\n\t// http://www.cse.chalmers.se/edu/year/2010/course/TDA361/grid.pdf\n\t\n\tvar t = 0.0\n\t\t, floor = Math.floor\n\t\t, ix = floor(px) | 0\n\t\t, iy = floor(py) | 0\n\t\t, iz = floor(pz) | 0\n\n\t\t, stepx = (dx > 0) ? 1 : -1\n\t\t, stepy = (dy > 0) ? 1 : -1\n\t\t, stepz = (dz > 0) ? 1 : -1\n\t\t\n\t// dx,dy,dz are already normalized\n\t\t, txDelta = Math.abs(1 / dx)\n\t\t, tyDelta = Math.abs(1 / dy)\n\t\t, tzDelta = Math.abs(1 / dz)\n\n\t\t, xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix)\n\t\t, ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy)\n\t\t, zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz)\n\t\t\n\t// location of nearest voxel boundary, in units of t \n\t\t, txMax = (txDelta < Infinity) ? txDelta * xdist : Infinity\n\t\t, tyMax = (tyDelta < Infinity) ? tyDelta * ydist : Infinity\n\t\t, tzMax = (tzDelta < Infinity) ? tzDelta * zdist : Infinity\n\n\t\t, steppedIndex = -1\n\t\n\t// main loop along raycast vector\n\twhile (t <= max_d) {\n\t\t\n\t\t// exit check\n\t\tvar b = getVoxel(ix, iy, iz)\n\t\tif (b) {\n\t\t\tif (hit_pos) {\n\t\t\t\thit_pos[0] = px + t * dx\n\t\t\t\thit_pos[1] = py + t * dy\n\t\t\t\thit_pos[2] = pz + t * dz\n\t\t\t}\n\t\t\tif (hit_norm) {\n\t\t\t\thit_norm[0] = hit_norm[1] = hit_norm[2] = 0\n\t\t\t\tif (steppedIndex === 0) hit_norm[0] = -stepx\n\t\t\t\tif (steppedIndex === 1) hit_norm[1] = -stepy\n\t\t\t\tif (steppedIndex === 2) hit_norm[2] = -stepz\n\t\t\t}\n\t\t\treturn b\n\t\t}\n\t\t\n\t\t// advance t to next nearest voxel boundary\n\t\tif (txMax < tyMax) {\n\t\t\tif (txMax < tzMax) {\n\t\t\t\tix += stepx\n\t\t\t\tt = txMax\n\t\t\t\ttxMax += txDelta\n\t\t\t\tsteppedIndex = 0\n\t\t\t} else {\n\t\t\t\tiz += stepz\n\t\t\t\tt = tzMax\n\t\t\t\ttzMax += tzDelta\n\t\t\t\tsteppedIndex = 2\n\t\t\t}\n\t\t} else {\n\t\t\tif (tyMax < tzMax) {\n\t\t\t\tiy += stepy\n\t\t\t\tt = tyMax\n\t\t\t\ttyMax += tyDelta\n\t\t\t\tsteppedIndex = 1\n\t\t\t} else {\n\t\t\t\tiz += stepz\n\t\t\t\tt = tzMax\n\t\t\t\ttzMax += tzDelta\n\t\t\t\tsteppedIndex = 2\n\t\t\t}\n\t\t}\n\n\t}\n\t\n\t// no voxel hit found\n\tif (hit_pos) {\n\t\thit_pos[0] = px + t * dx\n\t\thit_pos[1] = py + t * dy\n\t\thit_pos[2] = pz + t * dz\n\t}\n\tif (hit_norm) {\n\t\thit_norm[0] = hit_norm[1] = hit_norm[2] = 0\n\t}\n\n\treturn 0\n\n}\n\n\n// conform inputs\n\nfunction traceRay(getVoxel, origin, direction, max_d, hit_pos, hit_norm) {\n\tvar px = +origin[0]\n\t\t, py = +origin[1]\n\t\t, pz = +origin[2]\n\t\t, dx = +direction[0]\n\t\t, dy = +direction[1]\n\t\t, dz = +direction[2]\n\t\t, ds = Math.sqrt(dx * dx + dy * dy + dz * dz)\n\n\tif (ds === 0) {\n\t\tthrow new Error(\"Can't raycast along a zero vector\")\n\t}\n\n\tdx /= ds\n\tdy /= ds\n\tdz /= ds\n\tif (typeof (max_d) === \"undefined\") {\n\t\tmax_d = 64.0\n\t} else {\n\t\tmax_d = +max_d\n\t}\n\treturn traceRay_impl(getVoxel, px, py, pz, dx, dy, dz, max_d, hit_pos, hit_norm)\n}\n\nmodule.exports = traceRay\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/fast-voxel-raycast/index.js?"); - -/***/ }), - -/***/ "../../node_modules/game-inputs/inputs.js": -/*!*******************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/game-inputs/inputs.js ***! - \*******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vkey = __webpack_require__(/*! vkey */ \"../../node_modules/vkey/index.js\")\nvar EventEmitter = __webpack_require__(/*! events */ \"../../node_modules/events/events.js\").EventEmitter;\n// mousewheel polyfill borrowed directly from game-shell\nvar addMouseWheel = __webpack_require__(/*! ./lib/mousewheel-polyfill.js */ \"../../node_modules/game-inputs/lib/mousewheel-polyfill.js\")\n\nmodule.exports = function(domElement, options) {\n return new Inputs(domElement, options)\n}\n\n\n/*\n * Simple inputs manager to abstract key/mouse inputs.\n * Inspired by (and where applicable stealing code from) \n * game-shell: https://github.com/mikolalysenko/game-shell\n * \n * inputs.bind( 'move-right', 'D', '' )\n * inputs.bind( 'move-left', 'A' )\n * inputs.unbind( 'move-left' )\n * \n * inputs.down.on( 'move-right', function( binding, event ) {})\n * inputs.up.on( 'move-right', function( binding, event ) {})\n *\n * inputs.state['move-right'] // true when corresponding keys are down\n * inputs.state.dx // mouse x movement since tick() was last called\n * inputs.getBindings() // [ 'move-right', 'move-left', ... ]\n*/\n\n\nfunction Inputs(element, opts) {\n\n // settings\n this.element = element || document\n opts = opts || {}\n this.preventDefaults = !!opts.preventDefaults\n this.stopPropagation = !!opts.stopPropagation\n\n // emitters\n this.down = new EventEmitter()\n this.up = new EventEmitter()\n\n // state object to be queried\n this.state = {\n dx: 0, dy: 0, \n scrollx: 0, scrolly: 0, scrollz: 0\n }\n\n // internal state\n this._keybindmap = {} // { 'vkeycode' : [ 'binding', 'binding2' ] }\n this._keyStates = {} // { 'vkeycode' : boolean }\n this._bindPressCounts = {} // { 'binding' : int }\n\n // register for dom events\n this.initEvents()\n}\n\n\n/*\n *\n * PUBLIC API \n *\n*/ \n\nInputs.prototype.initEvents = function() {\n // keys\n window.addEventListener( 'keydown', onKeyEvent.bind(undefined,this,true), false )\n window.addEventListener( 'keyup', onKeyEvent.bind(undefined,this,false), false )\n // mouse buttons\n this.element.addEventListener(\"mousedown\", onMouseEvent.bind(undefined,this,true), false)\n this.element.addEventListener(\"mouseup\", onMouseEvent.bind(undefined,this,false), false)\n this.element.oncontextmenu = onContextMenu.bind(undefined,this)\n // treat dragstart like mouseup - idiotically, mouseup doesn't fire after a drag starts (!)\n this.element.addEventListener(\"dragstart\", onMouseEvent.bind(undefined,this,false), false)\n // touch/mouse movement\n this.element.addEventListener(\"mousemove\", onMouseMove.bind(undefined,this), false)\n this.element.addEventListener(\"touchmove\", onMouseMove.bind(undefined,this), false)\n this.element.addEventListener(\"touchstart\", onTouchStart.bind(undefined,this), false)\n // scroll/mousewheel\n addMouseWheel(this.element, onMouseWheel.bind(undefined,this), false)\n}\n\n\n// Usage: bind( bindingName, vkeyCode, vkeyCode.. )\n// Note that inputs._keybindmap maps vkey codes to binding names\n// e.g. this._keybindmap['a'] = 'move-left'\nInputs.prototype.bind = function(binding) {\n for (var i=1; i-1) { arr.splice(i,1) }\n }\n}\n\n// tick function - clears out cumulative mouse movement state variables\nInputs.prototype.tick = function() {\n this.state.dx = this.state.dy = 0\n this.state.scrollx = this.state.scrolly = this.state.scrollz = 0\n}\n\n\n\nInputs.prototype.getBoundKeys = function() {\n var arr = []\n for (var b in this._keybindmap) { arr.push(b) }\n return arr\n}\n\n\n\n/*\n * INTERNALS - DOM EVENT HANDLERS\n*/ \n\n\nfunction onKeyEvent(inputs, wasDown, ev) {\n handleKeyEvent( ev.keyCode, vkey[ev.keyCode], wasDown, inputs, ev )\n}\n\nfunction onMouseEvent(inputs, wasDown, ev) {\n // simulate a code out of range of vkey\n var keycode = -1 - ev.button\n var vkeycode = '' \n handleKeyEvent( keycode, vkeycode, wasDown, inputs, ev )\n return false\n}\n\nfunction onContextMenu(inputs) {\n // cancel context menu if there's a binding for right mousebutton\n var arr = inputs._keybindmap['']\n if (arr) { return false }\n}\n\nfunction onMouseMove(inputs, ev) {\n // for now, just populate the state object with mouse movement\n var dx = ev.movementX || ev.mozMovementX || 0,\n dy = ev.movementY || ev.mozMovementY || 0\n // ad-hoc experimental touch support\n if (ev.touches && (dx|dy)===0) {\n var xy = getTouchMovement(ev)\n dx = xy[0]\n dy = xy[1]\n }\n inputs.state.dx += dx\n inputs.state.dy += dy\n}\n\n// experimental - for touch events, extract useful dx/dy\nvar lastTouchX = 0\nvar lastTouchY = 0\nvar lastTouchID = null\n\nfunction onTouchStart(inputs, ev) {\n var touch = ev.changedTouches[0]\n lastTouchX = touch.clientX\n lastTouchY = touch.clientY\n lastTouchID = touch.identifier\n}\n\nfunction getTouchMovement(ev) {\n var touch\n var touches = ev.changedTouches\n for (var i=0; i' : ''\n, 1: ''\n, 2: ''\n, 3: ''\n, 4: ''\n, 5: ''\n, 6: ''\n, 8: ''\n, 9: ''\n, 12: ''\n, 13: ''\n, 16: ''\n, 17: ''\n, 18: ''\n, 19: ''\n, 20: ''\n, 21: ''\n, 23: ''\n, 24: ''\n, 25: ''\n, 27: ''\n, 28: ''\n, 29: ''\n, 30: ''\n, 31: ''\n, 27: ''\n, 32: ''\n, 33: ''\n, 34: ''\n, 35: ''\n, 36: ''\n, 37: ''\n, 38: ''\n, 39: ''\n, 40: ''\n, 41: ''\n, 42: ''\n, 43: ''\n, 44: ''\n, 45: ''\n, 46: ''\n, 47: ''\n, 91: '' // meta-left -- no one handles left and right properly, so we coerce into one.\n, 92: '' // meta-right\n, 93: isOSX ? '' : '' // chrome,opera,safari all report this for meta-right (osx mbp).\n, 95: ''\n, 106: ''\n, 107: ''\n, 108: ''\n, 109: ''\n, 110: ''\n, 111: ''\n, 144: ''\n, 145: ''\n, 160: ''\n, 161: ''\n, 162: ''\n, 163: ''\n, 164: ''\n, 165: ''\n, 166: ''\n, 167: ''\n, 168: ''\n, 169: ''\n, 170: ''\n, 171: ''\n, 172: ''\n\n // ff/osx reports '' for '-'\n, 173: isOSX && maybeFirefox ? '-' : ''\n, 174: ''\n, 175: ''\n, 176: ''\n, 177: ''\n, 178: ''\n, 179: ''\n, 180: ''\n, 181: ''\n, 182: ''\n, 183: ''\n, 186: ';'\n, 187: '='\n, 188: ','\n, 189: '-'\n, 190: '.'\n, 191: '/'\n, 192: '`'\n, 219: '['\n, 220: '\\\\'\n, 221: ']'\n, 222: \"'\"\n, 223: ''\n, 224: '' // firefox reports meta here.\n, 226: ''\n, 229: ''\n, 231: isOpera ? '`' : ''\n, 246: ''\n, 247: ''\n, 248: ''\n, 249: ''\n, 250: ''\n, 251: ''\n, 252: ''\n, 253: ''\n, 254: ''\n}\n\nfor(i = 58; i < 65; ++i) {\n output[i] = String.fromCharCode(i)\n}\n\n// 0-9\nfor(i = 48; i < 58; ++i) {\n output[i] = (i - 48)+''\n}\n\n// A-Z\nfor(i = 65; i < 91; ++i) {\n output[i] = String.fromCharCode(i)\n}\n\n// num0-9\nfor(i = 96; i < 106; ++i) {\n output[i] = ''\n}\n\n// F1-F24\nfor(i = 112; i < 136; ++i) {\n output[i] = 'F'+(i-111)\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/vkey/index.js?"); - -/***/ }), - -/***/ "../../node_modules/voxel-aabb-sweep/index.js": -/*!***********************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/voxel-aabb-sweep/index.js ***! - \***********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n// reused array instances\n\nvar tr_arr = []\nvar ldi_arr = []\nvar tri_arr = []\nvar step_arr = []\nvar tDelta_arr = []\nvar tNext_arr = []\nvar vec_arr = []\nvar normed_arr = []\nvar base_arr = []\nvar max_arr = []\nvar left_arr = []\nvar result_arr = []\n\n\n\n// core implementation:\n\nfunction sweep_impl(getVoxel, callback, vec, base, max, epsilon) {\n\n // consider algo as a raycast along the AABB's leading corner\n // as raycast enters each new voxel, iterate in 2D over the AABB's \n // leading face in that axis looking for collisions\n // \n // original raycast implementation: https://github.com/andyhall/fast-voxel-raycast\n // original raycast paper: http://www.cse.chalmers.se/edu/year/2010/course/TDA361/grid.pdf\n\n var tr = tr_arr\n var ldi = ldi_arr\n var tri = tri_arr\n var step = step_arr\n var tDelta = tDelta_arr\n var tNext = tNext_arr\n var normed = normed_arr\n\n var floor = Math.floor\n var cumulative_t = 0.0\n var t = 0.0\n var max_t = 0.0\n var axis = 0\n var i = 0\n\n\n // init for the current sweep vector and take first step\n initSweep()\n if (max_t === 0) return 0\n\n axis = stepForward()\n\n // loop along raycast vector\n while (t <= max_t) {\n\n // sweeps over leading face of AABB\n if (checkCollision(axis)) {\n // calls the callback and decides whether to continue\n var done = handleCollision()\n if (done) return cumulative_t\n }\n\n axis = stepForward()\n }\n\n // reached the end of the vector unobstructed, finish and exit\n cumulative_t += max_t\n for (i = 0; i < 3; i++) {\n base[i] += vec[i]\n max[i] += vec[i]\n }\n return cumulative_t\n\n\n\n\n\n // low-level implementations of each step:\n function initSweep() {\n\n // parametrization t along raycast\n t = 0.0\n max_t = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])\n if (max_t === 0) return\n for (var i = 0; i < 3; i++) {\n var dir = (vec[i] >= 0)\n step[i] = dir ? 1 : -1\n // trailing / trailing edge coords\n var lead = dir ? max[i] : base[i]\n tr[i] = dir ? base[i] : max[i]\n // int values of lead/trail edges\n ldi[i] = leadEdgeToInt(lead, step[i])\n tri[i] = trailEdgeToInt(tr[i], step[i])\n // normed vector\n normed[i] = vec[i] / max_t\n // distance along t required to move one voxel in each axis\n tDelta[i] = Math.abs(1 / normed[i])\n // location of nearest voxel boundary, in units of t \n var dist = dir ? (ldi[i] + 1 - lead) : (lead - ldi[i])\n tNext[i] = (tDelta[i] < Infinity) ? tDelta[i] * dist : Infinity\n }\n\n }\n\n\n // check for collisions - iterate over the leading face on the advancing axis\n\n function checkCollision(i_axis) {\n var stepx = step[0]\n var x0 = (i_axis === 0) ? ldi[0] : tri[0]\n var x1 = ldi[0] + stepx\n\n var stepy = step[1]\n var y0 = (i_axis === 1) ? ldi[1] : tri[1]\n var y1 = ldi[1] + stepy\n\n var stepz = step[2]\n var z0 = (i_axis === 2) ? ldi[2] : tri[2]\n var z1 = ldi[2] + stepz\n\n // var j_axis = (i_axis + 1) % 3\n // var k_axis = (i_axis + 2) % 3\n // var s = ['x', 'y', 'z'][i_axis]\n // var js = ['x', 'y', 'z'][j_axis]\n // var ks = ['x', 'y', 'z'][k_axis]\n // var i0 = [x0, y0, z0][i_axis]\n // var j0 = [x0, y0, z0][j_axis]\n // var k0 = [x0, y0, z0][k_axis]\n // var i1 = [x1 - stepx, y1 - stepy, z1 - stepz][i_axis]\n // var j1 = [x1 - stepx, y1 - stepy, z1 - stepz][j_axis]\n // var k1 = [x1 - stepx, y1 - stepy, z1 - stepz][k_axis]\n // console.log('=== step', s, 'to', i0, ' sweep', js, j0 + ',' + j1, ' ', ks, k0 + ',' + k1)\n\n for (var x = x0; x != x1; x += stepx) {\n for (var y = y0; y != y1; y += stepy) {\n for (var z = z0; z != z1; z += stepz) {\n if (getVoxel(x, y, z)) return true\n }\n }\n }\n return false\n }\n\n\n // on collision - call the callback and return or set up for the next sweep\n\n function handleCollision() {\n\n // set up for callback\n cumulative_t += t\n var dir = step[axis]\n\n // vector moved so far, and left to move\n var done = t / max_t\n var left = left_arr\n for (i = 0; i < 3; i++) {\n var dv = vec[i] * done\n base[i] += dv\n max[i] += dv\n left[i] = vec[i] - dv\n }\n\n // set leading edge of stepped axis exactly to voxel boundary\n // else we'll sometimes rounding error beyond it\n if (dir > 0) {\n max[axis] = Math.round(max[axis])\n } else {\n base[axis] = Math.round(base[axis])\n }\n \n // call back to let client update the \"left to go\" vector\n var res = callback(cumulative_t, axis, dir, left)\n\n // bail out out on truthy response\n if (res) return true\n\n // init for new sweep along vec\n for (i = 0; i < 3; i++) vec[i] = left[i]\n initSweep()\n if (max_t === 0) return true // no vector left\n\n return false\n }\n\n\n // advance to next voxel boundary, and return which axis was stepped\n\n function stepForward() {\n var axis = (tNext[0] < tNext[1]) ?\n ((tNext[0] < tNext[2]) ? 0 : 2) :\n ((tNext[1] < tNext[2]) ? 1 : 2)\n var dt = tNext[axis] - t\n t = tNext[axis]\n ldi[axis] += step[axis]\n tNext[axis] += tDelta[axis]\n for (i = 0; i < 3; i++) {\n tr[i] += dt * normed[i]\n tri[i] = trailEdgeToInt(tr[i], step[i])\n }\n\n return axis\n }\n\n\n\n function leadEdgeToInt(coord, step) {\n return floor(coord - step * epsilon)\n }\n function trailEdgeToInt(coord, step) {\n return floor(coord + step * epsilon)\n }\n\n}\n\n\n\n\n\n// conform inputs\n\nfunction sweep(getVoxel, box, dir, callback, noTranslate, epsilon) {\n\n var vec = vec_arr\n var base = base_arr\n var max = max_arr\n var result = result_arr\n\n // init parameter float arrays\n for (var i = 0; i < 3; i++) {\n vec[i] = +dir[i]\n max[i] = +box.max[i]\n base[i] = +box.base[i]\n }\n\n if (!epsilon) epsilon = 1e-10\n\n // run sweep implementation\n var dist = sweep_impl(getVoxel, callback, vec, base, max, epsilon)\n\n // translate box by distance needed to updated base value\n if (!noTranslate) {\n for (i = 0; i < 3; i++) {\n result[i] = (dir[i] > 0) ? max[i] - box.max[i] : base[i] - box.base[i]\n }\n box.translate(result)\n }\n\n // return value is total distance moved (not necessarily magnitude of [end]-[start])\n return dist\n}\n\nmodule.exports = sweep\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/voxel-aabb-sweep/index.js?"); - -/***/ }), - -/***/ "../../node_modules/voxel-physics-engine/src/index.js": -/*!*******************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/voxel-physics-engine/src/index.js ***! - \*******************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar aabb = __webpack_require__(/*! aabb-3d */ \"../../node_modules/aabb-3d/index.js\")\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\nvar sweep = __webpack_require__(/*! voxel-aabb-sweep */ \"../../node_modules/voxel-aabb-sweep/index.js\")\nvar RigidBody = __webpack_require__(/*! ./rigidBody */ \"../../node_modules/voxel-physics-engine/src/rigidBody.js\")\n\n\nvar DEBUG = 0\n\n\nmodule.exports = function (opts, testSolid, testFluid) {\n return new Physics(opts, testSolid, testFluid)\n}\n\nvar defaults = {\n gravity: [0, -10, 0],\n minBounceImpulse: .5, // lowest collision impulse that bounces\n airDrag: 0.1,\n fluidDrag: 0.4,\n fluidDensity: 2.0,\n}\n\n\n/* \n * CONSTRUCTOR - represents a world of rigid bodies.\n * \n * Takes testSolid(x,y,z) function to query block solidity\n * Takes testFluid(x,y,z) function to query if a block is a fluid\n*/\nfunction Physics(opts, testSolid, testFluid) {\n opts = Object.assign({}, defaults, opts)\n\n this.gravity = opts.gravity\n this.airDrag = opts.airDrag\n this.fluidDensity = opts.fluidDensity\n this.fluidDrag = opts.fluidDrag\n this.minBounceImpulse = opts.minBounceImpulse\n this.bodies = []\n\n // collision function - TODO: abstract this into a setter?\n this.testSolid = testSolid\n this.testFluid = testFluid\n}\n\n\n/*\n * ADDING AND REMOVING RIGID BODIES\n*/\n\nPhysics.prototype.addBody = function (_aabb, mass, friction,\n restitution, gravMult, onCollide) {\n _aabb = _aabb || new aabb([0, 0, 0], [1, 1, 1])\n if (typeof mass == 'undefined') mass = 1\n if (typeof friction == 'undefined') friction = 1\n if (typeof restitution == 'undefined') restitution = 0\n if (typeof gravMult == 'undefined') gravMult = 1\n var b = new RigidBody(_aabb, mass, friction, restitution, gravMult, onCollide)\n this.bodies.push(b)\n return b\n}\n\nPhysics.prototype.removeBody = function (b) {\n var i = this.bodies.indexOf(b)\n if (i < 0) return undefined\n this.bodies.splice(i, 1)\n b.aabb = b.onCollide = null\n}\n\n\n\n\n/*\n * PHYSICS AND COLLISIONS\n*/\n\nvar a = vec3.create()\nvar dv = vec3.create()\nvar dx = vec3.create()\nvar impacts = vec3.create()\nvar oldResting = vec3.create()\n\n\n/*\n * TICK HANDLER\n*/\nPhysics.prototype.tick = function (dt) {\n // convert dt to seconds\n dt = dt / 1000\n var noGravity = equals(0, vec3.squaredLength(this.gravity))\n\n this.bodies.forEach(b => iterateBody(this, b, dt, noGravity))\n}\n\n\n\n/*\n * PER-BODY MAIN PHYSICS ROUTINE\n*/\n\nfunction iterateBody(self, b, dt, noGravity) {\n vec3.copy(oldResting, b.resting)\n\n // treat bodies with <= mass as static\n if (b.mass <= 0) {\n vec3.set(b.velocity, 0, 0, 0)\n vec3.set(b._forces, 0, 0, 0)\n vec3.set(b._impulses, 0, 0, 0)\n return\n }\n\n // skip bodies if static or no velocity/forces/impulses\n var localNoGrav = noGravity || (b.gravityMultiplier === 0)\n if (bodyAsleep(self, b, dt, localNoGrav)) return\n b._sleepFrameCount--\n\n // check if under water, if so apply buoyancy and drag forces\n applyFluidForces(self, b)\n\n // debug hooks\n sanityCheck(b._forces)\n sanityCheck(b._impulses)\n sanityCheck(b.velocity)\n sanityCheck(b.resting)\n\n // semi-implicit Euler integration\n\n // a = f/m + gravity*gravityMultiplier\n vec3.scale(a, b._forces, 1 / b.mass)\n vec3.scaleAndAdd(a, a, self.gravity, b.gravityMultiplier)\n\n // dv = i/m + a*dt\n // v1 = v0 + dv\n vec3.scale(dv, b._impulses, 1 / b.mass)\n vec3.scaleAndAdd(dv, dv, a, dt)\n vec3.add(b.velocity, b.velocity, dv)\n\n // apply friction based on change in velocity this frame\n if (b.friction) {\n applyFrictionByAxis(0, b, dv)\n applyFrictionByAxis(1, b, dv)\n applyFrictionByAxis(2, b, dv)\n }\n\n // linear air or fluid friction - effectively v *= drag\n // body settings override global settings\n var drag = (b.airDrag >= 0) ? b.airDrag : self.airDrag\n if (b.inFluid) {\n drag = (b.fluidDrag >= 0) ? b.fluidDrag : self.fluidDrag\n drag *= 1 - (1 - b.ratioInFluid) ** 2\n }\n var mult = Math.max(1 - drag * dt / b.mass, 0)\n vec3.scale(b.velocity, b.velocity, mult)\n\n // x1-x0 = v1*dt\n vec3.scale(dx, b.velocity, dt)\n\n // clear forces and impulses for next timestep\n vec3.set(b._forces, 0, 0, 0)\n vec3.set(b._impulses, 0, 0, 0)\n\n // cache old position for use in autostepping\n if (b.autoStep) {\n cloneAABB(tmpBox, b.aabb)\n }\n\n // sweeps aabb along dx and accounts for collisions\n processCollisions(self, b.aabb, dx, b.resting)\n\n // if autostep, and on ground, run collisions again with stepped up aabb\n if (b.autoStep) {\n tryAutoStepping(self, b, tmpBox, dx)\n }\n\n // Collision impacts. b.resting shows which axes had collisions:\n for (var i = 0; i < 3; ++i) {\n impacts[i] = 0\n if (b.resting[i]) {\n // count impact only if wasn't collided last frame\n if (!oldResting[i]) impacts[i] = -b.velocity[i]\n b.velocity[i] = 0\n }\n }\n var mag = vec3.length(impacts)\n if (mag > .001) { // epsilon\n // bounce if over minBounceImpulse\n if (mag > self.minBounceImpulse && b.restitution) {\n vec3.scale(impacts, impacts, b.restitution * b.mass)\n b.applyImpulse(impacts)\n }\n // send collision event regardless\n if (b.onCollide) b.onCollide(impacts)\n }\n\n\n // sleep check\n var vsq = vec3.squaredLength(b.velocity)\n if (vsq > 1e-5) b._markActive()\n}\n\n\n\n\n\n\n\n\n/*\n * FLUIDS\n*/\n\nfunction applyFluidForces(self, body) {\n // First pass at handling fluids. Assumes fluids are settled\n // thus, only check at corner of body, and only from bottom up\n var box = body.aabb\n var cx = Math.floor(box.base[0])\n var cz = Math.floor(box.base[2])\n var y0 = Math.floor(box.base[1])\n var y1 = Math.floor(box.max[1])\n\n if (!self.testFluid(cx, y0, cz)) {\n body.inFluid = false\n body.ratioInFluid = 0\n return\n }\n\n // body is in a fluid - find out how much of body is submerged\n var submerged = 1\n var cy = y0 + 1\n while (cy <= y1 && self.testFluid(cx, cy, cz)) {\n submerged++\n cy++\n }\n var fluidLevel = y0 + submerged\n var heightInFluid = fluidLevel - box.base[1]\n var ratioInFluid = heightInFluid / box.vec[1]\n if (ratioInFluid > 1) ratioInFluid = 1\n var vol = box.vec[0] * box.vec[1] * box.vec[2]\n var displaced = vol * ratioInFluid\n // bouyant force = -gravity * fluidDensity * volumeDisplaced\n var f = _fluidVec\n vec3.scale(f, self.gravity, -self.fluidDensity * displaced)\n body.applyForce(f)\n\n body.inFluid = true\n body.ratioInFluid = ratioInFluid\n}\n\nvar _fluidVec = vec3.create()\n\n\n\n\n\n/*\n * FRICTION\n*/\n\n\nfunction applyFrictionByAxis(axis, body, dvel) {\n // friction applies only if moving into a touched surface\n var restDir = body.resting[axis]\n var vNormal = dvel[axis]\n if (restDir === 0) return\n if (restDir * vNormal <= 0) return\n\n // current vel lateral to friction axis\n vec3.copy(lateralVel, body.velocity)\n lateralVel[axis] = 0\n var vCurr = vec3.length(lateralVel)\n if (equals(vCurr, 0)) return\n\n // treat current change in velocity as the result of a pseudoforce\n // Fpseudo = m*dv/dt\n // Base friction force on normal component of the pseudoforce\n // Ff = u * Fnormal\n // Ff = u * m * dvnormal / dt\n // change in velocity due to friction force\n // dvF = dt * Ff / m\n // = dt * (u * m * dvnormal / dt) / m\n // = u * dvnormal\n var dvMax = Math.abs(body.friction * vNormal)\n\n // decrease lateral vel by dvMax (or clamp to zero)\n var scaler = (vCurr > dvMax) ? (vCurr - dvMax) / vCurr : 0\n body.velocity[(axis + 1) % 3] *= scaler\n body.velocity[(axis + 2) % 3] *= scaler\n}\nvar lateralVel = vec3.create()\n\n\n\n\n\n\n/*\n * COLLISION HANDLER\n*/\n\n// sweep aabb along velocity vector and set resting vector\nfunction processCollisions(self, box, velocity, resting) {\n vec3.set(resting, 0, 0, 0)\n return sweep(self.testSolid, box, velocity, function (dist, axis, dir, vec) {\n resting[axis] = dir\n vec[axis] = 0\n })\n}\n\n\n\n\n\n/*\n * AUTO-STEPPING\n*/\n\nvar tmpBox = new aabb([], [])\nvar tmpResting = vec3.create()\nvar targetPos = vec3.create()\nvar upvec = vec3.create()\nvar leftover = vec3.create()\n\nfunction tryAutoStepping(self, b, oldBox, dx) {\n if (b.resting[1] >= 0 && !b.inFluid) return\n\n // // direction movement was blocked before trying a step\n var xBlocked = (b.resting[0] !== 0)\n var zBlocked = (b.resting[2] !== 0)\n if (!(xBlocked || zBlocked)) return\n\n // continue autostepping only if headed sufficiently into obstruction\n var ratio = Math.abs(dx[0] / dx[2])\n var cutoff = 4\n if (!xBlocked && ratio > cutoff) return\n if (!zBlocked && ratio < 1 / cutoff) return\n\n // original target position before being obstructed\n vec3.add(targetPos, oldBox.base, dx)\n\n // move towards the target until the first X/Z collision\n var getVoxels = self.testSolid\n sweep(getVoxels, oldBox, dx, function (dist, axis, dir, vec) {\n if (axis === 1) vec[axis] = 0\n else return true\n })\n\n var y = b.aabb.base[1]\n var ydist = Math.floor(y + 1.001) - y\n vec3.set(upvec, 0, ydist, 0)\n var collided = false\n // sweep up, bailing on any obstruction\n sweep(getVoxels, oldBox, upvec, function (dist, axis, dir, vec) {\n collided = true\n return true\n })\n if (collided) return // could't move upwards\n\n // now move in X/Z however far was left over before hitting the obstruction\n vec3.subtract(leftover, targetPos, oldBox.base)\n leftover[1] = 0\n processCollisions(self, oldBox, leftover, tmpResting)\n\n // bail if no movement happened in the originally blocked direction\n if (xBlocked && !equals(oldBox.base[0], targetPos[0])) return\n if (zBlocked && !equals(oldBox.base[2], targetPos[2])) return\n\n // done - oldBox is now at the target autostepped position\n cloneAABB(b.aabb, oldBox)\n b.resting[0] = tmpResting[0]\n b.resting[2] = tmpResting[2]\n if (b.onStep) b.onStep()\n}\n\n\n\n\n\n/*\n * SLEEP CHECK\n*/\n\nfunction bodyAsleep(self, body, dt, noGravity) {\n if (body._sleepFrameCount > 0) return false\n // without gravity bodies stay asleep until a force/impulse wakes them up\n if (noGravity) return true\n // otherwise check body is resting against something\n // i.e. sweep along by distance d = 1/2 g*t^2\n // and check there's still a collision\n var isResting = false\n var gmult = 0.5 * dt * dt * body.gravityMultiplier\n vec3.scale(sleepVec, self.gravity, gmult)\n sweep(self.testSolid, body.aabb, sleepVec, function () {\n isResting = true\n return true\n }, true)\n return isResting\n}\nvar sleepVec = vec3.create()\n\n\n\n\n\nfunction equals(a, b) { return Math.abs(a - b) < 1e-5 }\n\nfunction cloneAABB(tgt, src) {\n for (var i = 0; i < 3; i++) {\n tgt.base[i] = src.base[i]\n tgt.max[i] = src.max[i]\n tgt.vec[i] = src.vec[i]\n }\n}\n\n\n\nvar sanityCheck = function () { }\nif (DEBUG) sanityCheck = function (v) {\n if (isNaN(vec3.length(v))) throw 'Vector with NAN: ', v\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/voxel-physics-engine/src/index.js?"); - -/***/ }), - -/***/ "../../node_modules/voxel-physics-engine/src/rigidBody.js": -/*!***********************************************************************************!*\ - !*** /Users/andy/dev/game/noa/node_modules/voxel-physics-engine/src/rigidBody.js ***! - \***********************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar aabb = __webpack_require__(/*! aabb-3d */ \"../../node_modules/aabb-3d/index.js\")\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n\nvar DEBUG = 0\n\n\nmodule.exports = RigidBody\n\n\n\n/*\n * RIGID BODY - internal data structure\n * Only AABB bodies right now. Someday will likely need spheres?\n*/\n\nfunction RigidBody(_aabb, mass, friction, restitution, gravMult, onCollide, autoStep) {\n this.aabb = new aabb(_aabb.base, _aabb.vec) // clone\n this.mass = mass\n this.friction = friction\n this.restitution = restitution\n this.gravityMultiplier = gravMult\n this.onCollide = onCollide\n this.autoStep = !!autoStep\n this.airDrag = -1 // overrides global airDrag when >= 0\n this.fluidDrag = -1 // overrides global fluidDrag when >= 0\n this.onStep = null\n \n // internals\n this.velocity = vec3.create()\n this.resting = [0, 0, 0]\n this.inFluid = false\n this._ratioInFluid = 0\n this._forces = vec3.create()\n this._impulses = vec3.create()\n this._sleepFrameCount = 10 | 0\n}\n\nRigidBody.prototype.setPosition = function (p) {\n sanityCheck(p)\n vec3.subtract(p, p, this.aabb.base)\n this.aabb.translate(p)\n this._markActive()\n}\nRigidBody.prototype.getPosition = function () {\n return vec3.clone(this.aabb.base)\n}\nRigidBody.prototype.applyForce = function (f) {\n sanityCheck(f)\n vec3.add(this._forces, this._forces, f)\n this._markActive()\n}\nRigidBody.prototype.applyImpulse = function (i) {\n sanityCheck(i)\n vec3.add(this._impulses, this._impulses, i)\n this._markActive()\n}\nRigidBody.prototype._markActive = function () {\n this._sleepFrameCount = 10 | 0\n}\n\n\n\n// temp\nRigidBody.prototype.atRestX = function () { return this.resting[0] }\nRigidBody.prototype.atRestY = function () { return this.resting[1] }\nRigidBody.prototype.atRestZ = function () { return this.resting[2] }\n\n\n\n\n\nvar sanityCheck = function () { }\nif (DEBUG) sanityCheck = function (v) {\n if (isNaN(vec3.length(v))) throw 'Vector with NAN: ', v\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/node_modules/voxel-physics-engine/src/rigidBody.js?"); - -/***/ }), - -/***/ "../../node_modules/webpack/buildin/global.js": -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || Function(\"return this\")() || (1, eval)(\"this\");\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n//# sourceURL=webpack:///(webpack)/buildin/global.js?"); - -/***/ }), - -/***/ "../../package.json": -/*!*********************************************!*\ - !*** /Users/andy/dev/game/noa/package.json ***! - \*********************************************/ -/*! exports provided: name, version, description, main, scripts, author, keywords, license, dependencies, repository, bugs, devDependencies, default */ -/***/ (function(module) { - -eval("module.exports = {\"name\":\"noa-engine\",\"version\":\"0.25.1\",\"description\":\"Experimental voxel game engine\",\"main\":\"src/index.js\",\"scripts\":{\"start\":\"cd docs/hello-world && webpack-dev-server\",\"test\":\"cd docs/test && webpack-dev-server\",\"build\":\"cd docs/hello-world && webpack && cd ../test && webpack\"},\"author\":\"Andy Hall\",\"keywords\":[\"voxel\",\"game\",\"engine\"],\"license\":\"MIT\",\"dependencies\":{\"aabb-3d\":\"andyhall/aabb-3d\",\"box-intersect\":\"^1.0.1\",\"ent-comp\":\"^0.7.0\",\"fast-voxel-raycast\":\"^0.1.1\",\"game-inputs\":\"^0.2.0\",\"game-shell\":\"andyhall/game-shell\",\"gl-vec3\":\"^1.1.3\",\"ndarray\":\"^1.0.16\",\"ndarray-hash\":\"^1.0.0\",\"voxel-aabb-sweep\":\"^0.5.0\",\"voxel-physics-engine\":\"^0.9.0\"},\"repository\":\"https://github.com/andyhall/noa\",\"bugs\":{\"url\":\"https://github.com/andyhall/noa/issues\"},\"devDependencies\":{\"webpack\":\"^4.26.0\",\"webpack-cli\":\"^3.1.2\",\"webpack-dev-server\":\"^3.1.11\"}};\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/package.json?"); - -/***/ }), - -/***/ "../../src/components/collideEntities.js": -/*!******************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/collideEntities.js ***! - \******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar boxIntersect = __webpack_require__(/*! box-intersect */ \"../../node_modules/box-intersect/index.js\")\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n\n/*\n * \tEvery frame, entities with this component will get mutually checked for colliions\n * \n * * cylinder: flag for checking collisions as a vertical cylindar (rather than AABB)\n * * collideBits: category for this entity\n * * collideMask: categories this entity collides with\n * * callback: function(other_id) - called when `own.collideBits & other.collideMask` is true\n * \n * \n * \t\tNotes:\n * \tSet collideBits=0 for entities like bullets, which can collide with things \n * \t\tbut are never the target of a collision.\n * \tSet collideMask=0 for things with no callback - things that get collided with,\n * \t\tbut don't themselves instigate collisions.\n * \n*/\n\n\n\nmodule.exports = function (noa) {\n\n\treturn {\n\n\t\tname: 'collideEntities',\n\n\t\tstate: {\n\t\t\tcylinder: false,\n\t\t\tcollideBits: 1 | 0,\n\t\t\tcollideMask: 1 | 0,\n\t\t\tcallback: null,\n\t\t},\n\n\t\tonAdd: null,\n\n\t\tonRemove: null,\n\n\n\t\tsystem: function entityCollider(dt, states) {\n\t\t\tvar ents = noa.ents\n\n\t\t\t// data struct that boxIntersect looks for\n\t\t\t// - array of [lo, lo, lo, hi, hi, hi] extents\n\t\t\tvar intervals = []\n\t\t\tfor (var i = 0; i < states.length; i++) {\n\t\t\t\tvar id = states[i].__id\n\t\t\t\tvar dat = ents.getPositionData(id)\n\t\t\t\tintervals[i] = dat._extents\n\t\t\t}\n\n\t\t\t// run the intersect library\n\t\t\tboxIntersect(intervals, function (a, b) {\n\t\t\t\tvar stateA = states[a]\n\t\t\t\tvar stateB = states[b]\n\t\t\t\tvar intervalA = intervals[a]\n\t\t\t\tvar intervalB = intervals[b]\n\t\t\t\tif (cylindricalHitTest(stateA, stateB, intervalA, intervalB)) {\n\t\t\t\t\thandleCollision(noa, stateA, stateB)\n\t\t\t\t}\n\t\t\t})\n\n\t\t}\n\t}\n\n\n\n\t/*\n\t * \n\t * \t\tIMPLEMENTATION\n\t * \n\t*/\n\n\n\tfunction handleCollision(noa, stateA, stateB) {\n\t\tvar idA = stateA.__id\n\t\tvar idB = stateB.__id\n\n\t\t// entities really do overlap, so check masks and call event handlers\n\t\tif (stateA.collideMask & stateB.collideBits) {\n\t\t\tif (stateA.callback) stateA.callback(idB)\n\t\t}\n\t\tif (stateB.collideMask & stateA.collideBits) {\n\t\t\tif (stateB.callback) stateB.callback(idA)\n\t\t}\n\n\t\t// general pairwise handler\n\t\tnoa.ents.onPairwiseEntityCollision(idA, idB)\n\t}\n\n\n\n\t// For entities whose extents overlap, \n\t// test if collision still happens when taking cylinder flags into account\n\n\tfunction cylindricalHitTest(stateA, stateB, intervalA, intervalB) {\n\t\tif (stateA.cylinder) {\n\t\t\tif (stateB.cylinder) {\n\t\t\t\treturn cylinderCylinderTest(intervalA, intervalB)\n\t\t\t} else {\n\t\t\t\treturn cylinderBoxTest(intervalA, intervalB)\n\t\t\t}\n\t\t} else if (stateB.cylinder) {\n\t\t\treturn cylinderBoxTest(intervalB, intervalA)\n\t\t}\n\t\treturn true\n\t}\n\n\n\n\n\t// Cylinder-cylinder hit test (AABBs are known to overlap)\n\t// given their extent arrays [lo, lo, lo, hi, hi, hi]\n\n\tfunction cylinderCylinderTest(a, b) {\n\t\t// distance between cylinder centers\n\t\tvar rada = (a[3] - a[0]) / 2\n\t\tvar radb = (b[3] - b[0]) / 2\n\t\tvar dx = a[0] + rada - (b[0] + radb)\n\t\tvar dz = a[2] + rada - (b[2] + radb)\n\t\t// collide if dist <= sum of radii\n\t\tvar distsq = dx * dx + dz * dz\n\t\tvar radsum = rada + radb\n\t\treturn (distsq <= radsum * radsum)\n\t}\n\n\n\n\n\t// Cylinder-Box hit test (AABBs are known to overlap)\n\t// given their extent arrays [lo, lo, lo, hi, hi, hi]\n\n\tfunction cylinderBoxTest(cyl, cube) {\n\t\t// X-z center of cylinder\n\t\tvar rad = (cyl[3] - cyl[0]) / 2\n\t\tvar cx = cyl[0] + rad\n\t\tvar cz = cyl[2] + rad\n\t\t// point in X-Z square closest to cylinder\n\t\tvar px = clamp(cx, cube[0], cube[3])\n\t\tvar pz = clamp(cz, cube[2], cube[5])\n\t\t// collision if distance from that point to circle <= cylinder radius\n\t\tvar dx = px - cx\n\t\tvar dz = pz - cz\n\t\tvar distsq = dx * dx + dz * dz\n\t\treturn (distsq <= rad * rad)\n\t}\n\n\tfunction clamp(val, lo, hi) {\n\t\treturn (val < lo) ? lo : (val > hi) ? hi : val\n\t}\n\n\n\n\n}\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/collideEntities.js?"); - -/***/ }), - -/***/ "../../src/components/collideTerrain.js": -/*!*****************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/collideTerrain.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'collideTerrain',\n\n\t\tstate: {\n\t\t\tcallback: null\n\t\t},\n\n\t\tonAdd: function (eid, state) {\n\t\t\t// add collide handler for physics engine to call\n\t\t\tvar ents = noa.entities\n\t\t\tif (ents.hasPhysics(eid)) {\n\t\t\t\tvar body = ents.getPhysicsBody(eid)\n\t\t\t\tbody.onCollide = function bodyOnCollide(impulse) {\n\t\t\t\t\tvar cb = noa.ents.getCollideTerrain(eid).callback\n\t\t\t\t\tif (cb) cb(impulse, eid)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tonRemove: function (eid, state) {\n\t\t\tvar ents = noa.entities\n\t\t\tif (ents.hasPhysics(eid)) {\n\t\t\t\tents.getPhysicsBody(eid).onCollide = null\n\t\t\t}\n\t\t},\n\n\n\t\tsystem: null\n\n\n\t}\n}\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/collideTerrain.js?"); - -/***/ }), - -/***/ "../../src/components/fadeOnZoom.js": -/*!*************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/fadeOnZoom.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n/**\n * Component for the player entity, when active hides the player's mesh \n * when camera zoom is less than a certain amount\n */\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'fadeOnZoom',\n\n\t\tstate: {\n\t\t\tcutoff: 1.5,\n\t\t\t_showing: true\n\t\t},\n\n\t\tonAdd: null,\n\n\t\tonRemove: null,\n\n\t\tsystem: function fadeOnZoomProc(dt, states) {\n\t\t\tvar zoom = noa.rendering._currentZoom\n\t\t\tvar ents = noa.entities\n\t\t\tstates.forEach(state => {\n\t\t\t\tcheckZoom(state, state.__id, zoom, ents)\n\t\t\t})\n\t\t}\n\t}\n}\n\n\nfunction checkZoom(state, id, zoom, ents) {\n\tif (!ents.hasMesh(id)) return\n\n\tif (state._showing && zoom < state.cutoff || !state._showing && zoom > state.cutoff) {\n\t\tvar mesh = ents.getMeshData(id).mesh\n\t\tmesh.visibility = state._showing = (zoom > state.cutoff)\n\t}\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/fadeOnZoom.js?"); - -/***/ }), - -/***/ "../../src/components/followsEntity.js": -/*!****************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/followsEntity.js ***! - \****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n\n/*\n * Indicates that an entity should be moved to another entity's position each tick,\n * possibly by a fixed offset, and the same for renderPositions each render\n*/\n\nmodule.exports = function (noa) {\n\n\treturn {\n\n\t\tname: 'followsEntity',\n\n\t\tstate: {\n\t\t\tentity: 0 | 0,\n\t\t\toffset: null,\n\t\t},\n\n\t\tonAdd: function (eid, state) {\n\t\t\tvar off = vec3.create()\n\t\t\tstate.offset = (state.offset) ? vec3.copy(off, state.offset) : off\n\t\t\tupdatePosition(state)\n\t\t\tupdateRenderPosition(state)\n\t\t},\n\n\t\tonRemove: null,\n\n\n\t\t// on tick, copy over regular positions\n\t\tsystem: function followEntity(dt, states) {\n\t\t\tstates.forEach(state => {\n\t\t\t\tupdatePosition(state)\n\t\t\t})\n\t\t},\n\n\n\t\t// on render, copy over render positions\n\t\trenderSystem: function followEntityMesh(dt, states) {\n\t\t\tstates.forEach(state => {\n\t\t\t\tupdateRenderPosition(state)\n\t\t\t})\n\t\t}\n\t}\n\n\n\n\tfunction updatePosition(state) {\n\t\tvar id = state.__id\n\t\tvar self = noa.ents.getPositionData(id)\n\t\tvar other = noa.ents.getPositionData(state.entity)\n\t\tif (other) {\n\t\t\tvec3.add(self.position, other.position, state.offset)\n\t\t\tself._extentsChanged = true\n\t\t} else {\n\t\t\tnoa.ents.removeComponent(id, noa.ents.names.followsEntity)\n\t\t}\n\t}\n\n\tfunction updateRenderPosition(state) {\n\t\tvar id = state.__id\n\t\tvar self = noa.ents.getPositionData(id)\n\t\tvar other = noa.ents.getPositionData(state.entity)\n\t\tif (other) {\n\t\t\tvec3.add(self.renderPosition, other.renderPosition, state.offset)\n\t\t} else {\n\t\t\tnoa.ents.removeComponent(id, noa.ents.names.followsEntity)\n\t\t}\n\t}\n\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/followsEntity.js?"); - -/***/ }), - -/***/ "../../src/components/mesh.js": -/*!*******************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/mesh.js ***! - \*******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'mesh',\n\n\t\tstate: {\n\t\t\tmesh: null,\n\t\t\toffset: null\n\t\t},\n\n\n\t\tonAdd: function (eid, state) {\n\t\t\tif (state.mesh) {\n\t\t\t\tnoa.rendering.addMeshToScene(state.mesh)\n\t\t\t} else {\n\t\t\t\tthrow new Error('Mesh component added without a mesh - probably a bug!')\n\t\t\t}\n\t\t\tif (!state.offset) {\n\t\t\t\tstate.offset = new vec3.create()\n\t\t\t}\n\n\t\t\t// initialize mesh to correct position\n\t\t\tvar pos = noa.ents.getPosition(eid)\n\t\t\tvar mpos = state.mesh.position\n\t\t\tmpos.x = pos[0] + state.offset[0]\n\t\t\tmpos.y = pos[1] + state.offset[1]\n\t\t\tmpos.z = pos[2] + state.offset[2]\n\t\t},\n\n\n\t\tonRemove: function (eid, state) {\n\t\t\tstate.mesh.dispose()\n\t\t},\n\n\n\t\tsystem: null,\n\n\n\n\t\trenderSystem: function (dt, states) {\n\t\t\t// before render move each mesh to its render position, \n\t\t\t// set by the physics engine or driving logic\n\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar id = state.__id\n\n\t\t\t\tvar rpos = noa.ents.getPositionData(id).renderPosition\n\t\t\t\tvar x = rpos[0] + state.offset[0]\n\t\t\t\tvar y = rpos[1] + state.offset[1]\n\t\t\t\tvar z = rpos[2] + state.offset[2]\n\n\t\t\t\tstate.mesh.position.copyFromFloats(x, y, z)\n\t\t\t})\n\t\t}\n\n\n\t}\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/mesh.js?"); - -/***/ }), - -/***/ "../../src/components/movement.js": -/*!***********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/movement.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n/**\n * \n * Movement component. State stores settings like jump height, etc.,\n * as well as current state (running, jumping, heading angle).\n * Processor checks state and applies movement/friction/jump forces\n * to the entity's physics body. \n * \n */\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'movement',\n\n\t\tstate: {\n\t\t\t// current state\n\t\t\theading: 0, \t\t\t// radians\n\t\t\trunning: false,\n\t\t\tjumping: false,\n\n\t\t\t// options:\n\t\t\tmaxSpeed: 10,\n\t\t\tmoveForce: 30,\n\t\t\tresponsiveness: 15,\n\t\t\trunningFriction: 0,\n\t\t\tstandingFriction: 2,\n\n\t\t\tairMoveMult: 0.5,\n\t\t\tjumpImpulse: 10,\n\t\t\tjumpForce: 12,\n\t\t\tjumpTime: 500, \t\t\t// ms\n\t\t\tairJumps: 1,\n\n\t\t\t// internal state\n\t\t\t_jumpCount: 0,\n\t\t\t_isJumping: 0,\n\t\t\t_currjumptime: 0,\n\t\t},\n\n\t\tonAdd: null,\n\n\t\tonRemove: null,\n\n\n\t\tsystem: function movementProcessor(dt, states) {\n\t\t\tvar ents = noa.entities\n\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar body = ents.getPhysicsBody(state.__id)\n\t\t\t\tapplyMovementPhysics(dt, state, body)\n\t\t\t})\n\n\t\t}\n\n\n\t}\n}\n\n\nvar tempvec = vec3.create()\nvar tempvec2 = vec3.create()\nvar zeroVec = vec3.create()\n\n\nfunction applyMovementPhysics(dt, state, body) {\n\t// move implementation originally written as external module\n\t// see https://github.com/andyhall/voxel-fps-controller\n\t// for original code\n\n\t// jumping\n\tvar onGround = (body.atRestY() < 0)\n\tvar canjump = (onGround || state._jumpCount < state.airJumps)\n\tif (onGround) {\n\t\tstate._isJumping = false\n\t\tstate._jumpCount = 0\n\t}\n\n\t// process jump input\n\tif (state.jumping) {\n\t\tif (state._isJumping) { // continue previous jump\n\t\t\tif (state._currjumptime > 0) {\n\t\t\t\tvar jf = state.jumpForce\n\t\t\t\tif (state._currjumptime < dt) jf *= state._currjumptime / dt\n\t\t\t\tbody.applyForce([0, jf, 0])\n\t\t\t\tstate._currjumptime -= dt\n\t\t\t}\n\t\t} else if (canjump) { // start new jump\n\t\t\tstate._isJumping = true\n\t\t\tif (!onGround) state._jumpCount++\n\t\t\tstate._currjumptime = state.jumpTime\n\t\t\tbody.applyImpulse([0, state.jumpImpulse, 0])\n\t\t\t// clear downward velocity on airjump\n\t\t\tif (!onGround && body.velocity[1] < 0) body.velocity[1] = 0\n\t\t}\n\t} else {\n\t\tstate._isJumping = false\n\t}\n\n\t// apply movement forces if entity is moving, otherwise just friction\n\tvar m = tempvec\n\tvar push = tempvec2\n\tif (state.running) {\n\n\t\tvar speed = state.maxSpeed\n\t\t// todo: add crouch/sprint modifiers if needed\n\t\t// if (state.sprint) speed *= state.sprintMoveMult\n\t\t// if (state.crouch) speed *= state.crouchMoveMult\n\t\tvec3.set(m, 0, 0, speed)\n\n\t\t// rotate move vector to entity's heading\n\t\tvec3.rotateY(m, m, zeroVec, state.heading)\n\n\t\t// push vector to achieve desired speed & dir\n\t\t// following code to adjust 2D velocity to desired amount is patterned on Quake: \n\t\t// https://github.com/id-Software/Quake-III-Arena/blob/master/code/game/bg_pmove.c#L275\n\t\tvec3.subtract(push, m, body.velocity)\n\t\tpush[1] = 0\n\t\tvar pushLen = vec3.length(push)\n\t\tvec3.normalize(push, push)\n\n\t\tif (pushLen > 0) {\n\t\t\t// pushing force vector\n\t\t\tvar canPush = state.moveForce\n\t\t\tif (!onGround) canPush *= state.airMoveMult\n\n\t\t\t// apply final force\n\t\t\tvar pushAmt = state.responsiveness * pushLen\n\t\t\tif (canPush > pushAmt) canPush = pushAmt\n\n\t\t\tvec3.scale(push, push, canPush)\n\t\t\tbody.applyForce(push)\n\t\t}\n\n\t\t// different friction when not moving\n\t\t// idea from Sonic: http://info.sonicretro.org/SPG:Running\n\t\tbody.friction = state.runningFriction\n\t} else {\n\t\tbody.friction = state.standingFriction\n\t}\n\n\n\n}\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/movement.js?"); - -/***/ }), - -/***/ "../../src/components/physics.js": -/*!**********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/physics.js ***! - \**********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n\nmodule.exports = function (noa) {\n\n\n\treturn {\n\n\t\tname: 'physics',\n\n\n\t\tstate: {\n\t\t\tbody: null,\n\t\t},\n\n\n\t\tonAdd: function (entID, state) {\n\t\t\tstate.body = noa.physics.addBody()\n\t\t\t// implicitly assume body has a position component, to get size\n\t\t\tvar dat = noa.ents.getPositionData(state.__id)\n\t\t\tnoa.ents.setEntitySize(state.__id, dat.width, dat.height, dat.width)\n\t\t},\n\n\n\t\tonRemove: function (entID, state) {\n\t\t\t// update position before removing\n\t\t\t// this lets entity wind up at e.g. the result of a collision\n\t\t\t// even if physics component is removed in collision handler\n\t\t\tif (noa.ents.hasPosition(state.__id)) {\n\t\t\t\tvar pdat = noa.ents.getPositionData(state.__id)\n\t\t\t\tupdatePositionFromPhysics(state, pdat)\n\t\t\t\tbacktrackRenderPos(state, pdat, 0, false)\n\t\t\t}\n\t\t\tnoa.physics.removeBody(state.body)\n\t\t},\n\n\n\t\tsystem: function (dt, states) {\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar pdat = noa.ents.getPositionData(state.__id)\n\t\t\t\tupdatePositionFromPhysics(state, pdat)\n\t\t\t})\n\t\t},\n\n\n\t\trenderSystem: function (dt, states) {\n\n\t\t\t// dt is time (ms) since physics engine tick\n\t\t\t// to avoid temporal aliasing, render the state as if lerping between\n\t\t\t// the last position and the next one \n\t\t\t// since the entity data is the \"next\" position this amounts to \n\t\t\t// offsetting each entity into the past by tickRate - dt\n\t\t\t// http://gafferongames.com/game-physics/fix-your-timestep/\n\n\t\t\tvar backtrackAmt = - (noa._tickRate - dt) / 1000\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar id = state.__id\n\t\t\t\tvar pdat = noa.ents.getPositionData(id)\n\t\t\t\tvar smoothed = noa.ents.cameraSmoothed(id)\n\t\t\t\tbacktrackRenderPos(state, pdat, backtrackAmt, smoothed)\n\t\t\t})\n\t\t}\n\n\t}\n\n}\n\n\n\nvar offset = vec3.create()\nvar pos = vec3.create()\n\n\n\nfunction updatePositionFromPhysics(state, posDat) {\n\toffset[0] = offset[2] = posDat.width / 2\n\toffset[1] = 0\n\tvar pos = posDat.position\n\tvar base = state.body.aabb.base\n\tvar max = state.body.aabb.max\n\tvar ext = posDat._extents\n\tfor (var j = 0; j < 3; j++) {\n\t\tpos[j] = base[j] + offset[j]\n\t\text[j] = base[j]\n\t\text[j + 3] = max[j]\n\t}\n}\n\n\nfunction backtrackRenderPos(state, posDat, backtrackAmt, smoothed) {\n\t// pos = pos + backtrack * body.velocity\n\tvec3.scaleAndAdd(pos, posDat.position, state.body.velocity, backtrackAmt)\n\n\t// smooth out update if component is present\n\t// (this is set after sudden movements like auto-stepping)\n\tif (smoothed) vec3.lerp(pos, posDat.renderPosition, pos, 0.3)\n\n\t// copy values over to renderPosition, \n\tvec3.copy(posDat.renderPosition, pos)\n}\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/physics.js?"); - -/***/ }), - -/***/ "../../src/components/position.js": -/*!***********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/position.js ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\n\n\n/**\n * \n * \tComponent holding entity's position, width, and height.\n * By convention, \"position\" is the bottom center of the entity's AABB\n * \n */\n\n\nmodule.exports = function (noa) {\n\n\tvar hasWarned = false\n\n\treturn {\n\n\t\tname: 'position',\n\n\t\tstate: {\n\t\t\tposition: null,\n\t\t\trenderPosition: null,\n\t\t\twidth: +0,\n\t\t\theight: +0,\n\t\t\t_extents: null,\n\t\t\t_extentsChanged: true,\n\t\t},\n\n\n\t\tonAdd: function (eid, state) {\n\t\t\tif (state.position) {\n\t\t\t\tif (!(state.position instanceof Float32Array) && !hasWarned) {\n\t\t\t\t\tconsole.warn('Better to set entity positions as instances of \"gl-vec3\"!')\n\t\t\t\t\thasWarned = true\n\t\t\t\t}\n\t\t\t} else state.position = vec3.create()\n\n\t\t\tstate.renderPosition = vec3.create()\n\t\t\tvec3.copy(state.renderPosition, state.position)\n\n\t\t\tstate._extents = new Float32Array(6)\n\t\t},\n\n\t\tonRemove: null,\n\n\n\n\t\tsystem: function (dt, states) {\n\t\t\tstates.forEach(state => {\n\t\t\t\tif (!state._extentsChanged) return\n\t\t\t\tupdateExtents(state._extents, state.position, state.height, state.width)\n\t\t\t\tstate._extentsChanged = false\n\t\t\t})\n\t\t},\n\n\n\t}\n}\n\n\nfunction updateExtents(ext, pos, height, width) {\n\tvar hw = width / 2\n\text[0] = pos[0] - hw\n\text[1] = pos[1]\n\text[2] = pos[2] - hw\n\text[3] = pos[0] + hw\n\text[4] = pos[1] + height\n\text[5] = pos[2] + hw\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/position.js?"); - -/***/ }), - -/***/ "../../src/components/receivesInputs.js": -/*!*****************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/receivesInputs.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n/**\n * \n * Input processing component - gets (key) input state and \n * applies it to receiving entities by updating their movement \n * component state (heading, movespeed, jumping, etc.)\n * \n */\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'receivesInputs',\n\n\t\tstate: {},\n\n\t\tonAdd: null,\n\n\t\tonRemove: null,\n\n\t\tsystem: function inputProcessor(dt, states) {\n\t\t\tvar ents = noa.entities\n\t\t\tvar inputState = noa.inputs.state\n\t\t\tvar camHeading = noa.rendering.getCameraRotation()[1]\n\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar moveState = ents.getMovement(state.__id)\n\t\t\t\tsetMovementState(moveState, inputState, camHeading)\n\t\t\t})\n\t\t}\n\n\t}\n}\n\n\n\nfunction setMovementState(state, inputs, camHeading) {\n\tstate.jumping = !!inputs.jump\n\n\tvar fb = inputs.forward ? (inputs.backward ? 0 : 1) : (inputs.backward ? -1 : 0)\n\tvar rl = inputs.right ? (inputs.left ? 0 : 1) : (inputs.left ? -1 : 0)\n\n\tif ((fb | rl) === 0) {\n\t\tstate.running = false\n\t} else {\n\t\tstate.running = true\n\t\tif (fb) {\n\t\t\tif (fb == -1) camHeading += Math.PI\n\t\t\tif (rl) {\n\t\t\t\tcamHeading += Math.PI / 4 * fb * rl // didn't plan this but it works!\n\t\t\t}\n\t\t} else {\n\t\t\tcamHeading += rl * Math.PI / 2\n\t\t}\n\t\tstate.heading = camHeading\n\t}\n\n}\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/receivesInputs.js?"); - -/***/ }), - -/***/ "../../src/components/shadow.js": -/*!*********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/shadow.js ***! - \*********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\nvar shadowDist\n\nmodule.exports = function (noa, dist) {\n\n\tshadowDist = dist\n\n\t// create a mesh to re-use for shadows\n\tvar scene = noa.rendering.getScene()\n\tvar disc = BABYLON.Mesh.CreateDisc('shadow', 0.75, 30, scene)\n\tdisc.rotation.x = Math.PI / 2\n\tdisc.material = noa.rendering.makeStandardMaterial('shadowMat')\n\tdisc.material.diffuseColor = BABYLON.Color3.Black()\n\tdisc.material.ambientColor = BABYLON.Color3.Black()\n\tdisc.material.alpha = 0.5\n\tdisc.setEnabled(false)\n\n\t// source mesh needn't be in the scene graph\n\tscene.removeMesh(disc)\n\n\n\treturn {\n\n\t\tname: 'shadow',\n\n\t\tstate: {\n\t\t\tsize: 0.5,\n\t\t\t_mesh: null,\n\t\t},\n\n\n\t\tonAdd: function (eid, state) {\n\t\t\tstate._mesh = noa.rendering.makeMeshInstance(disc, false)\n\t\t},\n\n\n\t\tonRemove: function (eid, state) {\n\t\t\tstate._mesh.dispose()\n\t\t},\n\n\n\t\tsystem: function shadowSystem(dt, states) {\n\t\t\tvar cpos = noa.rendering.getCameraPosition()\n\t\t\tvec3.set(camPos, cpos.x, cpos.y, cpos.z)\n\t\t\tvar dist = shadowDist\n\t\t\tstates.forEach(state => {\n\t\t\t\tupdateShadowHeight(state.__id, state._mesh, state.size, dist, noa)\n\t\t\t})\n\t\t},\n\n\n\t\trenderSystem: function (dt, states) {\n\t\t\t// before render adjust shadow x/z to render positions\n\t\t\tstates.forEach(state => {\n\t\t\t\tvar rpos = noa.ents.getPositionData(state.__id).renderPosition\n\t\t\t\tvar spos = state._mesh.position\n\t\t\t\tspos.x = rpos[0]\n\t\t\t\tspos.z = rpos[2]\n\t\t\t})\n\t\t}\n\n\n\n\n\t}\n}\n\nvar down = vec3.fromValues(0, -1, 0)\nvar camPos = vec3.fromValues(0, 0, 0)\nvar shadowPos = vec3.fromValues(0, 0, 0)\n\nfunction updateShadowHeight(id, mesh, size, shadowDist, noa) {\n\tvar ents = noa.entities\n\tvar dat = ents.getPositionData(id)\n\tvar loc = dat.position\n\tvar y\n\n\t// find Y location, from physics if on ground, otherwise by raycast\n\tif (ents.hasPhysics(id) && ents.getPhysicsBody(id).resting[1] < 0) {\n\t\ty = dat.renderPosition[1]\n\t} else {\n\t\tvar pick = noa.pick(loc, down, shadowDist)\n\t\tif (pick) {\n\t\t\ty = pick.position[1]\n\t\t} else {\n\t\t\tmesh.setEnabled(false)\n\t\t\treturn\n\t\t}\n\t}\n\n\ty = Math.round(y) // pick results get slightly countersunk\n\t// set shadow slightly above ground to avoid z-fighting\n\tvec3.set(shadowPos, mesh.position.x, y, mesh.position.z)\n\tvar sqdist = vec3.squaredDistance(camPos, shadowPos)\n\t// offset ~ 0.01 for nearby shadows, up to 0.1 at distance of ~40\n\tvar offset = 0.01 + 0.1 * (sqdist / 1600)\n\tif (offset > 0.1) offset = 0.1\n\tmesh.position.y = y + offset\n\t// set shadow scale\n\tvar dist = loc[1] - y\n\tvar scale = size * 0.7 * (1 - dist / shadowDist)\n\tmesh.scaling.copyFromFloats(scale, scale, scale)\n\tmesh.setEnabled(true)\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/shadow.js?"); - -/***/ }), - -/***/ "../../src/components/smoothCamera.js": -/*!***************************************************************!*\ - !*** /Users/andy/dev/game/noa/src/components/smoothCamera.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = function (noa) {\n\treturn {\n\n\t\tname: 'smooth-camera',\n\n\t\tstate: {\n\t\t\ttime: 100.1\n\t\t},\n\n\t\tonAdd: null,\n\n\t\tonRemove: null,\n\n\t\tsystem: function (dt, states) {\n\t\t\t// remove self after time elapses\n\t\t\tstates.forEach(state => {\n\t\t\t\tstate.time -= dt\n\t\t\t\tif (state.time < 0) noa.ents.removeComponent(state.__id, 'smooth-camera')\n\t\t\t})\n\t\t},\n\n\n\n\t}\n}\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/components/smoothCamera.js?"); - -/***/ }), - -/***/ "../../src/index.js": -/*!*********************************************!*\ - !*** /Users/andy/dev/game/noa/src/index.js ***! - \*********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n/*!\n * noa: an experimental voxel game engine.\n * @url github.com/andyhall/noa\n * @author Andy Hall \n * @license MIT\n*/\n\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\nvar ndarray = __webpack_require__(/*! ndarray */ \"../../node_modules/ndarray/ndarray.js\")\nvar EventEmitter = __webpack_require__(/*! events */ \"../../node_modules/events/events.js\").EventEmitter\nvar createContainer = __webpack_require__(/*! ./lib/container */ \"../../src/lib/container.js\")\nvar createRendering = __webpack_require__(/*! ./lib/rendering */ \"../../src/lib/rendering.js\")\nvar createWorld = __webpack_require__(/*! ./lib/world */ \"../../src/lib/world.js\")\nvar createInputs = __webpack_require__(/*! ./lib/inputs */ \"../../src/lib/inputs.js\")\nvar createPhysics = __webpack_require__(/*! ./lib/physics */ \"../../src/lib/physics.js\")\nvar createCamControls = __webpack_require__(/*! ./lib/camera */ \"../../src/lib/camera.js\")\nvar createRegistry = __webpack_require__(/*! ./lib/registry */ \"../../src/lib/registry.js\")\nvar createEntities = __webpack_require__(/*! ./lib/entities */ \"../../src/lib/entities.js\")\nvar raycast = __webpack_require__(/*! fast-voxel-raycast */ \"../../node_modules/fast-voxel-raycast/index.js\")\n\n\nmodule.exports = Engine\n\n\n\n// profiling flag\nvar PROFILE = 0\nvar PROFILE_RENDER = 0\nvar DEBUG_QUEUES = 0\n\n\n\n\nvar defaults = {\n debug: false,\n silent: false,\n playerHeight: 1.8,\n playerWidth: 0.6,\n playerStart: [0, 10, 0],\n playerAutoStep: false,\n tickRate: 33, // ms per tick - not ticks per second\n blockTestDistance: 10,\n stickyPointerLock: true,\n dragCameraOutsidePointerLock: true,\n skipDefaultHighlighting: false,\n}\n\n/**\n * Main engine object. \n * Emits: *tick, beforeRender, afterRender, targetBlockChanged*\n * \n * ```js\n * var noaEngine = require('noa-engine')\n * var noa = noaEngine(opts)\n * ```\n * \n * @class noa\n*/\n\nfunction Engine(opts) {\n if (!(this instanceof Engine)) return new Engine(opts)\n\n this.version = __webpack_require__(/*! ../package.json */ \"../../package.json\").version\n if (!opts.silent) {\n var debugstr = (opts.debug) ? ' (debug)' : ''\n console.log(`noa-engine v${this.version}${debugstr}`)\n }\n\n opts = Object.assign({}, defaults, opts)\n this._tickRate = opts.tickRate\n this._paused = false\n this._dragOutsideLock = opts.dragCameraOutsidePointerLock\n var self = this\n\n // container (html/div) manager\n this.container = createContainer(this, opts)\n\n // inputs manager - abstracts key/mouse input\n this.inputs = createInputs(this, opts, this.container.element)\n\n // create block/item property registry\n this.registry = createRegistry(this, opts)\n\n // create world manager\n this.world = createWorld(this, opts)\n\n // rendering manager - abstracts all draws to 3D context\n this.rendering = createRendering(this, opts, this.container.canvas)\n\n // Entity manager / Entity Component System (ECS)\n this.entities = createEntities(this, opts)\n // convenience\n this.ents = this.entities\n\n // physics engine - solves collisions, properties, etc.\n this.physics = createPhysics(this, opts)\n\n // camera controller\n this.cameraControls = createCamControls(this, opts)\n\n\n var ents = this.ents\n\n /** Entity id for the player entity */\n this.playerEntity = ents.add(\n opts.playerStart, // starting location- TODO: get from options\n opts.playerWidth, opts.playerHeight,\n null, null, // no mesh for now, no meshOffset, \n true, true\n )\n\n // make player entity it collide with terrain and other entities\n ents.addComponent(this.playerEntity, ents.names.collideTerrain)\n ents.addComponent(this.playerEntity, ents.names.collideEntities)\n\n // adjust default physics parameters\n var body = ents.getPhysicsBody(this.playerEntity)\n body.gravityMultiplier = 2 // less floaty\n body.autoStep = opts.playerAutoStep // auto step onto blocks\n\n /** reference to player entity's physics body */\n this.playerBody = body\n\n // input component - sets entity's movement state from key inputs\n ents.addComponent(this.playerEntity, ents.names.receivesInputs)\n\n // add a component to make player mesh fade out when zooming in\n ents.addComponent(this.playerEntity, ents.names.fadeOnZoom)\n\n // movement component - applies movement forces\n // todo: populate movement settings from options\n var moveOpts = {\n airJumps: 1\n }\n ents.addComponent(this.playerEntity, ents.names.movement, moveOpts)\n\n // how high above the player's position the eye is (for picking, camera tracking) \n this.playerEyeOffset = 0.9 * opts.playerHeight\n\n\n\n\n // set up block targeting\n this.blockTestDistance = opts.blockTestDistance\n\n /** function for which block IDs are targetable. \n * Defaults to a solidity check, but can be overridden */\n this.blockTargetIdCheck = this.registry.getBlockSolidity\n\n /** Dynamically updated object describing the currently targeted block */\n this.targetedBlock = null\n\n // add a default block highlighting function\n if (!opts.skipDefaultHighlighting) {\n // the default listener, defined onto noa in case people want to remove it later\n this.defaultBlockHighlightFunction = function (tgt) {\n if (tgt) {\n self.rendering.highlightBlockFace(true, tgt.position, tgt.normal)\n } else {\n self.rendering.highlightBlockFace(false)\n }\n }\n this.on('targetBlockChanged', this.defaultBlockHighlightFunction)\n }\n\n // init rendering stuff that needed to wait for engine internals\n this.rendering.initScene()\n\n // expose constants, for HACKINGâ„¢\n this._constants = __webpack_require__(/*! ./lib/constants */ \"../../src/lib/constants.js\")\n\n // temp hacks for development\n if (opts.debug) {\n window.noa = this\n window.scene = this.rendering._scene\n window.ndarray = ndarray\n window.vec3 = vec3\n var debug = false\n this.inputs.bind('debug', 'Z')\n this.inputs.down.on('debug', function onDebug() {\n debug = !debug\n if (debug) window.scene.debugLayer.show(); else window.scene.debugLayer.hide();\n })\n }\n\n\n\n}\n\nEngine.prototype = Object.create(EventEmitter.prototype)\n\n\n/*\n * Core Engine API\n*/\n\n\n\n\n/*\n * Tick function, called by container module at a fixed timestep. Emits #tick(dt),\n * where dt is the tick rate in ms (default 16.6)\n*/\n\nEngine.prototype.tick = function () {\n if (this._paused) return\n profile_hook('start')\n var dt = this._tickRate // fixed timesteps!\n this.world.tick(dt) // chunk creation/removal\n profile_hook('world')\n if (!this.world.playerChunkLoaded) {\n // when waiting on worldgen, just tick the meshing queue and exit\n this.rendering.tick(dt)\n return\n }\n this.physics.tick(dt) // iterates physics\n profile_hook('physics')\n this.rendering.tick(dt) // zooms camera, does deferred chunk meshing\n profile_hook('rendering')\n updateBlockTargets(this) // finds targeted blocks, and highlights one if needed\n profile_hook('targets')\n this.emit('tick', dt)\n profile_hook('tick event')\n profile_hook('end')\n this.inputs.tick() // clears accumulated tick/mouseMove data\n if (DEBUG_QUEUES) debugQueues(this)\n}\n\n\nvar __qwasDone = true, __qstart\nfunction debugQueues(self) {\n var a = self.world._chunkIDsToAdd.length\n var b = self.world._chunkIDsToCreate.length\n var c = self.rendering._chunksToMesh.length\n var d = self.rendering._numMeshedChunks\n if (a + b + c > 0) console.log([\n 'Chunks:', 'unmade', a,\n 'pending creation', b,\n 'to mesh', c,\n 'meshed', d,\n ].join(' \\t'))\n if (__qwasDone && a + b + c > 0) {\n __qwasDone = false\n __qstart = performance.now()\n }\n if (!__qwasDone && a + b + c === 0) {\n __qwasDone = true\n console.log('Queue empty after ' + Math.round(performance.now() - __qstart) + 'ms')\n }\n}\n\n\n\n\n\n\n/*\n * Render function, called every animation frame. Emits #beforeRender(dt), #afterRender(dt) \n * where dt is the time in ms *since the last tick*.\n*/\n\nEngine.prototype.render = function (framePart) {\n if (this._paused) return\n profile_hook_render('start')\n var dt = framePart * this._tickRate // ms since last tick\n // only move camera during pointerlock or mousedown, or if pointerlock is unsupported\n if (this.container.hasPointerLock ||\n !this.container.supportsPointerLock ||\n (this._dragOutsideLock && this.inputs.state.fire)) {\n this.cameraControls.updateForRender()\n }\n // clear cumulative mouse inputs\n this.inputs.state.dx = this.inputs.state.dy = 0\n // events and render\n this.emit('beforeRender', dt)\n profile_hook_render('before render')\n this.rendering.render(dt)\n profile_hook_render('render')\n this.emit('afterRender', dt)\n profile_hook_render('after render')\n profile_hook_render('end')\n}\n\n\n\n/*\n * Utility APIs\n*/\n\n/** \n * Pausing the engine will also stop render/tick events, etc.\n * @param paused\n*/\nEngine.prototype.setPaused = function (paused) {\n this._paused = !!paused\n // when unpausing, clear any built-up mouse inputs\n if (!paused) {\n this.inputs.state.dx = this.inputs.state.dy = 0\n }\n}\n\n/** @param x,y,z */\nEngine.prototype.getBlock = function (x, y, z) {\n if (x.length) {\n return this.world.getBlockID(x[0], x[1], x[2])\n } else {\n return this.world.getBlockID(x, y, z)\n }\n}\n\n/** @param x,y,z */\nEngine.prototype.setBlock = function (id, x, y, z) {\n // skips the entity collision check\n if (x.length) {\n return this.world.setBlockID(id, x[0], x[1], x[2])\n } else {\n return this.world.setBlockID(id, x, y, z)\n }\n}\n\n/**\n * Adds a block unless obstructed by entities \n * @param id,x,y,z */\nEngine.prototype.addBlock = function (id, x, y, z) {\n // add a new terrain block, if nothing blocks the terrain there\n if (x.length) {\n if (this.entities.isTerrainBlocked(x[0], x[1], x[2])) return\n this.world.setBlockID(id, x[0], x[1], x[2])\n return id\n } else {\n if (this.entities.isTerrainBlocked(x, y, z)) return\n this.world.setBlockID(id, x, y, z)\n return id\n }\n}\n\n\n\n/** */\nEngine.prototype.getPlayerPosition = function () {\n return this.entities.getPosition(this.playerEntity)\n}\n\n/** */\nEngine.prototype.getPlayerMesh = function () {\n return this.entities.getMeshData(this.playerEntity).mesh\n}\n\n/** */\nEngine.prototype.setPlayerEyeOffset = function (y) {\n this.playerEyeOffset = y\n var state = this.ents.getState(this.rendering.cameraTarget, this.ents.names.followsEntity)\n state.offset[1] = y\n}\n\n/** */\nEngine.prototype.getPlayerEyePosition = function () {\n var pos = this.entities.getPosition(this.playerEntity)\n vec3.copy(_eyeLoc, pos)\n _eyeLoc[1] += this.playerEyeOffset\n return _eyeLoc\n}\nvar _eyeLoc = vec3.create()\n\n/** */\nEngine.prototype.getCameraVector = function () {\n // rendering works with babylon's xyz vectors\n var v = this.rendering.getCameraVector()\n vec3.set(_camVec, v.x, v.y, v.z)\n return _camVec\n}\nvar _camVec = vec3.create()\n\n\n\n/**\n * Raycast through the world, returning a result object for any non-air block\n * @param pos\n * @param vec\n * @param dist\n */\nEngine.prototype.pick = function (pos, vec, dist, blockIdTestFunction) {\n if (dist === 0) return null\n // if no block ID function is specified default to solidity check\n var testFn = blockIdTestFunction || this.registry.getBlockSolidity\n var world = this.world\n var testVoxel = function (x, y, z) {\n var id = world.getBlockID(x, y, z)\n return testFn(id)\n }\n pos = pos || this.getPlayerEyePosition()\n vec = vec || this.getCameraVector()\n dist = dist || this.blockTestDistance\n var rpos = _hitResult.position\n var rnorm = _hitResult.normal\n var hit = raycast(testVoxel, pos, vec, dist, rpos, rnorm)\n if (!hit) return null\n // position is right on a voxel border - adjust it so flooring will work as expected\n for (var i = 0; i < 3; i++) rpos[i] -= 0.01 * rnorm[i]\n return _hitResult\n}\nvar _hitResult = {\n position: vec3.create(),\n normal: vec3.create(),\n}\n\n\n\n\n\n\n\n// Each frame, by default pick along the player's view vector \n// and tell rendering to highlight the struck block face\nfunction updateBlockTargets(noa) {\n var newhash = ''\n var blockIdFn = noa.blockTargetIdCheck || noa.registry.getBlockSolidity\n var result = noa.pick(null, null, null, blockIdFn)\n if (result) {\n var dat = _targetedBlockDat\n for (var i = 0; i < 3; i++) {\n // position values are right on a border, so adjust them before flooring!\n var n = result.normal[i] | 0\n var p = Math.floor(result.position[i])\n dat.position[i] = p\n dat.normal[i] = n\n dat.adjacent[i] = p + n\n newhash += '|' + p + '|' + n\n }\n dat.blockID = noa.world.getBlockID(dat.position[0], dat.position[1], dat.position[2])\n newhash += '|' + result.blockID\n noa.targetedBlock = dat\n } else {\n noa.targetedBlock = null\n }\n if (newhash != _prevTargetHash) {\n noa.emit('targetBlockChanged', noa.targetedBlock)\n _prevTargetHash = newhash\n }\n}\n\nvar _targetedBlockDat = {\n blockID: 0,\n position: [],\n normal: [],\n adjacent: [],\n}\n\nvar _prevTargetHash = ''\n\n\n\n\n\n\n\n\n\n\nvar profile_hook = function (s) { }\nvar profile_hook_render = function (s) { }\nif (PROFILE) (function () {\n var timer = new (__webpack_require__(/*! ./lib/util */ \"../../src/lib/util.js\").Timer)(200, 'tick ')\n profile_hook = function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\nif (PROFILE_RENDER) (function () {\n var timer = new (__webpack_require__(/*! ./lib/util */ \"../../src/lib/util.js\").Timer)(200, 'render ')\n profile_hook_render = function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/index.js?"); - -/***/ }), - -/***/ "../../src/lib/camera.js": -/*!**************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/camera.js ***! - \**************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = function (noa, opts) {\n\treturn new CameraController(noa, opts)\n}\n\n\n\n/*\n* Controller for the camera\n*\n*/\n\n\nvar defaults = {\n\trotationScaleX: 0.0025,\n\trotationScaleY: 0.0025,\n\tinverseY: false,\n}\n\n\nfunction CameraController(noa, opts) {\n\tthis.noa = noa\n\n\t// options\n\topts = Object.assign({}, defaults, opts)\n\tthis.rotationScaleX = opts.rotationScaleX\n\tthis.rotationScaleY = opts.rotationScaleY\n\tthis.inverseY = opts.inverseY\n}\n\n\n\n\n\n/**\n * On render, move/rotate the camera based on target and mouse inputs\n */\n\nCameraController.prototype.updateForRender = function () {\n\t// input state\n\tvar state = this.noa.inputs.state\n\n\t// TODO: REMOVE EVENTUALLY\n\tbugFix(state)\n\n\t// Rotation: translate dx/dy inputs into y/x axis camera angle changes\n\tvar dx = this.rotationScaleY * state.dy * ((this.inverseY) ? -1 : 1)\n\tvar dy = this.rotationScaleX * state.dx\n\n\t// normalize/clamp/update\n\tvar camrot = this.noa.rendering.getCameraRotation() // [x,y]\n\tvar rotX = clamp(camrot[0] + dx, rotXcutoff)\n\tvar rotY = (camrot[1] + dy) % (Math.PI * 2)\n\tthis.noa.rendering.setCameraRotation(rotX, rotY)\n\n}\n\nvar rotXcutoff = (Math.PI / 2) - .0001 // engines can be weird when xRot == pi/2\n\nfunction clamp(value, to) {\n\treturn isFinite(to) ? Math.max(Math.min(value, to), -to) : value\n}\n\n\n\n// workaround for this Chrome 63 + Win10 bug\n// https://bugs.chromium.org/p/chromium/issues/detail?id=781182\nfunction bugFix(state) {\n\tvar dx = state.dx\n\tvar dy = state.dy\n\tvar wval = document.body.clientWidth / 6\n\tvar hval = document.body.clientHeight / 6\n\tvar badx = (Math.abs(dx) > wval && (dx / lastx) < -1)\n\tvar bady = (Math.abs(dy) > hval && (dy / lasty) < -1)\n\tif (badx || bady) {\n\t\tstate.dx = lastx\n\t\tstate.dy = lasty\n\t\tlastx = (dx > 0) ? 1 : -1\n\t\tlasty = (dy > 0) ? 1 : -1\n\t} else {\n\t\tif (dx) lastx = dx\n\t\tif (dy) lasty = dy\n\t}\n}\n\nvar lastx = 0\nvar lasty = 0\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/camera.js?"); - -/***/ }), - -/***/ "../../src/lib/chunk.js": -/*!*************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/chunk.js ***! - \*************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar constants = __webpack_require__(/*! ./constants */ \"../../src/lib/constants.js\")\nvar ndarray = __webpack_require__(/*! ndarray */ \"../../node_modules/ndarray/ndarray.js\")\n\n\n\nmodule.exports = Chunk\n\n\n// shared references to terrain/object meshers\nvar terrainMesher = __webpack_require__(/*! ./terrainMesher */ \"../../src/lib/terrainMesher.js\")\nvar objectMesher = __webpack_require__(/*! ./objectMesher */ \"../../src/lib/objectMesher.js\")\n\n\n\n\n/* \n * \n * Chunk\n * \n * Stores and manages voxel ids and flags for each voxel within chunk\n * See constants.js for internal data representation\n * \n*/\n\n\n\n// data representation\nvar ID_MASK = constants.ID_MASK\nvar VAR_MASK = constants.VAR_MASK\nvar SOLID_BIT = constants.SOLID_BIT\nvar OPAQUE_BIT = constants.OPAQUE_BIT\nvar OBJECT_BIT = constants.OBJECT_BIT\n\n\n\n\n/*\n *\n * Chunk constructor\n *\n*/\n\nfunction Chunk(noa, id, i, j, k, size) {\n this.id = id\n\n this.noa = noa\n this.isDisposed = false\n this.isGenerated = false\n this.inInvalid = false\n this.octreeBlock = null\n this._terrainMesh = null\n\n this.isEmpty = false\n this.isFull = false\n\n // packed data storage\n var s = size + 2 // 1 block of padding on each side\n var arr = new Uint16Array(s * s * s)\n this.array = new ndarray(arr, [s, s, s])\n this.i = i\n this.j = j\n this.k = k\n this.size = size\n this.x = i * size\n this.y = j * size\n this.z = k * size\n\n // flags to track if things need re-meshing\n this._terrainDirty = false\n this._objectsDirty = false\n\n // init references shared among all chunks\n setBlockLookups(noa)\n\n // build unpadded and transposed array views for internal use\n rebuildArrayViews(this)\n\n // adds some properties to the chunk for handling object meshes\n objectMesher.initChunk(this)\n\n}\n\n\n\n// Registry lookup references shared by all chunks\nvar solidLookup\nvar opaqueLookup\nvar objectMeshLookup\nvar blockHandlerLookup\n\nfunction setBlockLookups(noa) {\n solidLookup = noa.registry._solidityLookup\n opaqueLookup = noa.registry._opacityLookup\n objectMeshLookup = noa.registry._blockMeshLookup\n blockHandlerLookup = noa.registry._blockHandlerLookup\n}\n\n\n\n\n/*\n *\n * Chunk API\n *\n*/\n\n// get/set deal with block IDs, so that this class acts like an ndarray\n\nChunk.prototype.get = function (x, y, z) {\n return ID_MASK & this._unpaddedView.get(x, y, z)\n}\n\nChunk.prototype.getSolidityAt = function (x, y, z) {\n return (SOLID_BIT & this._unpaddedView.get(x, y, z)) ? true : false\n}\n\nChunk.prototype.set = function (x, y, z, id) {\n var oldID = this._unpaddedView.get(x, y, z)\n var oldIDnum = oldID & ID_MASK\n if (id === oldIDnum) return\n\n // manage data\n var newID = packID(id)\n this._unpaddedView.set(x, y, z, newID)\n\n // handle object meshes\n if (oldID & OBJECT_BIT) removeObjectBlock(this, x, y, z)\n if (newID & OBJECT_BIT) addObjectBlock(this, id, x, y, z)\n\n // track full/emptyness\n if (newID !== 0) this.isEmpty = false\n if (!(newID & OPAQUE_BIT)) this.isFull = false\n\n // call block handlers\n callBlockHandler(this, oldIDnum, 'onUnset', x, y, z)\n callBlockHandler(this, id, 'onSet', x, y, z)\n\n // mark terrain dirty unless neither block was terrain\n if (isTerrain(oldID) || isTerrain(newID)) this._terrainDirty = true\n}\n\n\n\n\n\n\n// helper to call handler of a given type at a particular xyz\n\nfunction callBlockHandler(chunk, blockID, type, x, y, z) {\n var hobj = blockHandlerLookup[blockID]\n if (!hobj) return\n var handler = hobj[type]\n if (!handler) return\n // ignore all handlers if block is in chunk's edge padding blocks\n var s = chunk.size\n if (x < 0 || y < 0 || z < 0 || x >= s || y >= s || z >= s) return\n handler(chunk.x + x, chunk.y + y, chunk.z + z)\n}\n\n\n\n// Convert chunk's voxel terrain into a babylon.js mesh\n// Used internally, but needs to be public so mesh-building hacks can call it\nChunk.prototype.mesh = function (matGetter, colGetter, useAO, aoVals, revAoVal) {\n return terrainMesher.meshChunk(this, matGetter, colGetter, useAO, aoVals, revAoVal)\n}\n\n\n\n\n\n// gets called by World when this chunk has been queued for remeshing\nChunk.prototype.updateMeshes = function () {\n if (this._terrainDirty) {\n this.noa.rendering.removeTerrainMesh(this)\n var mesh = this.mesh()\n if (mesh) this.noa.rendering.addTerrainMesh(this, mesh)\n this._terrainDirty = false\n }\n if (this._objectsDirty) {\n objectMesher.buildObjectMesh(this)\n this._objectsDirty = false\n }\n}\n\n\n\n\n\n\n\n// helper to determine if a block counts as \"terrain\" (non-air, non-object)\nfunction isTerrain(id) {\n if (id === 0) return false\n // treat object blocks as terrain if solid (they affect AO)\n if (id & OBJECT_BIT) return !!(id & SOLID_BIT)\n return true\n}\n\n// helper to pack a block ID into the internally stored form, given lookup tables\nfunction packID(id) {\n var newID = id\n if (solidLookup[id]) newID |= SOLID_BIT\n if (opaqueLookup[id]) newID |= OPAQUE_BIT\n if (objectMeshLookup[id]) newID |= OBJECT_BIT\n return newID\n}\n\n\n\n\n\n\n\n\n/*\n * \n * Init\n * \n * Gets called right after client filled the voxel ID data array\n*/\n\n\n\nChunk.prototype.initData = function () {\n // remake other views, assuming that data has changed\n rebuildArrayViews(this)\n // flags for tracking if chunk is entirely opaque or transparent\n var fullyOpaque = OPAQUE_BIT\n var fullyAir = true\n\n // init everything in one big scan\n var arr = this.array\n var data = arr.data\n var len = arr.shape[0]\n var kstride = arr.stride[2]\n for (var i = 0; i < len; ++i) {\n var edge1 = (i === 0 || i === len - 1)\n for (var j = 0; j < len; ++j) {\n var d0 = arr.index(i, j, 0)\n var edge2 = edge1 || (j === 0 || j === len - 1)\n for (var k = 0; k < len; ++k, d0 += kstride) {\n // pull raw ID - could in principle be packed, so mask it\n var id = data[d0] & ID_MASK\n // skip air blocks\n if (id === 0) {\n fullyOpaque = 0\n continue\n }\n // store ID as packed internal representation\n var packed = packID(id) | 0\n data[d0] = packed\n // track whether chunk is entirely full or empty\n fullyOpaque &= packed\n fullyAir = false\n // within unpadded view, handle object blocks and handlers\n var atEdge = edge2 || (k === 0 || k === len - 1)\n if (!atEdge) {\n if (OBJECT_BIT & packed) {\n addObjectBlock(this, id, i - 1, j - 1, k - 1)\n }\n callBlockHandler(this, id, 'onLoad', i - 1, j - 1, k - 1)\n }\n }\n }\n }\n\n this.isFull = !!(fullyOpaque & OPAQUE_BIT)\n this.isEmpty = !!(fullyAir)\n this._terrainDirty = !(this.isFull || this.isEmpty)\n\n this.isGenerated = true\n}\n\n\n// helper to rebuild several transformed views on the data array\n\nfunction rebuildArrayViews(chunk) {\n var arr = chunk.array\n var size = chunk.size\n chunk._unpaddedView = arr.lo(1, 1, 1).hi(size, size, size)\n}\n\n\n\n// accessors related to meshing\n\nfunction addObjectBlock(chunk, id, x, y, z) {\n objectMesher.addObjectBlock(chunk, id, x, y, z)\n chunk._objectsDirty = true\n}\n\nfunction removeObjectBlock(chunk, x, y, z) {\n objectMesher.removeObjectBlock(chunk, x, y, z)\n chunk._objectsDirty = true\n}\n\n\n\n\n\n// dispose function - just clears properties and references\n\nChunk.prototype.dispose = function () {\n // look through the data for onUnload handlers\n callAllBlockHandlers(this, 'onUnload')\n\n // let meshers dispose their stuff\n objectMesher.disposeChunk(this)\n\n // apparently there's no way to dispose typed arrays, so just null everything\n this.array.data = null\n this.array = null\n this._unpaddedView = null\n\n this.isGenerated = false\n this.isDisposed = true\n}\n\n\n// helper to call a given handler for all blocks in the chunk\n\nfunction callAllBlockHandlers(chunk, type) {\n var view = chunk._unpaddedView\n var data = view.data\n var si = view.stride[0]\n var sj = view.stride[1]\n var sk = view.stride[2]\n var size = view.shape[0]\n var d0 = view.offset\n for (var i = 0; i < size; ++i) {\n for (var j = 0; j < size; ++j) {\n for (var k = 0; k < size; ++k) {\n var id = ID_MASK & data[d0]\n callBlockHandler(chunk, id, type, i, j, k)\n d0 += sk\n }\n d0 -= sk * size\n d0 += sj\n }\n d0 -= sj * size\n d0 += si\n }\n}\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/chunk.js?"); - -/***/ }), - -/***/ "../../src/lib/constants.js": -/*!*****************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/constants.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar constants = {}\nmodule.exports = constants\n\n\n/* \n * Internal voxel data representation\n *\n * Each voxel is stored as a Uint16.\n * Voxel id is stored in lowest bits, and flags stored in upper bits for fast retrieval\n * \n * Stores, from right to left:\n * 9 bits of voxel ID\n * 4 bits of variation (e.g. orientation) --- Not yet being used!\n * 1 bit solidity (i.e. physics-wise)\n * 1 bit opacity (whether voxel obscures neighboring faces)\n * 1 bit object marker (marks non-terrain blocks with custom meshes)\n*/\n\n\nvar ID_BITS = 9\nvar ID_MASK = (1 << ID_BITS) - 1\n\nvar VAR_BITS = 4\nvar VAR_OFFSET = ID_BITS\nvar VAR_MASK = ((1 << VAR_BITS) - 1) << VAR_OFFSET\n\nvar n = ID_BITS + VAR_BITS\nvar SOLID_BIT = 1 << n++\nvar OPAQUE_BIT = 1 << n++\nvar OBJECT_BIT = 1 << n++\n\n// exports\n\nconstants.ID_MASK = ID_MASK\nconstants.VAR_MASK = VAR_MASK\nconstants.SOLID_BIT = SOLID_BIT\nconstants.OPAQUE_BIT = OPAQUE_BIT\nconstants.OBJECT_BIT = OBJECT_BIT\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/constants.js?"); - -/***/ }), - -/***/ "../../src/lib/container.js": -/*!*****************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/container.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar createGameShell = __webpack_require__(/*! game-shell */ \"../../node_modules/game-shell/shell.js\")\n// var createGameShell = require('../../../../npm-modules/game-shell')\nvar EventEmitter = __webpack_require__(/*! events */ \"../../node_modules/events/events.js\").EventEmitter\n\n\nmodule.exports = function (noa, opts) {\n\treturn new Container(noa, opts)\n}\n\n/*\n* Container module\n* Wraps game-shell module and manages HTML container, canvas, etc.\n* Emits: DOMready\n*/\n\nfunction Container(noa, opts) {\n\topts = opts || {}\n\tthis._noa = noa\n\tthis.element = opts.domElement || createContainerDiv()\n\tthis.canvas = getOrCreateCanvas(this.element)\n\tthis._shell = createShell(this.canvas, opts)\n\n\t// mouse state/feature detection\n\tthis.hasPointerLock = false\n\tthis.supportsPointerLock = false\n\tthis.pointerInGame = false\n\tthis.isFocused = document.hasFocus()\n\n\t// basic listeners\n\tvar self = this\n\tvar lockChange = function (ev) { onLockChange(self, ev) }\n\tdocument.addEventListener(\"pointerlockchange\", lockChange, false)\n\tdocument.addEventListener(\"mozpointerlockchange\", lockChange, false)\n\tdocument.addEventListener(\"webkitpointerlockchange\", lockChange, false)\n\tdetectPointerLock(self)\n\n\tself.element.addEventListener('mouseenter', function () { self.pointerInGame = true })\n\tself.element.addEventListener('mouseleave', function () { self.pointerInGame = false })\n\n\twindow.addEventListener('focus', function () { self.isFocused = true })\n\twindow.addEventListener('blur', function () { self.isFocused = false })\n\n\t// get shell events after it's initialized\n\tthis._shell.on('init', onShellInit.bind(null, this))\n}\n\nContainer.prototype = Object.create(EventEmitter.prototype)\n\n\n\n/*\n* SHELL EVENTS\n*/\n\nfunction onShellInit(self) {\n\t// create shell listeners that drive engine functions\n\tvar noa = self._noa\n\tvar shell = self._shell\n\tshell.on('tick', function onTick(n) { noa.tick(n) })\n\tshell.on('render', function onRender(n) { noa.render(n) })\n\tshell.on('resize', noa.rendering.resize.bind(noa.rendering))\n\n\t// let other components know DOM is ready\n\tself.emit('DOMready')\n}\n\n\n\n/*\n* PUBLIC API \n*/\n\nContainer.prototype.appendTo = function (htmlElement) {\n\tthis.element.appendChild(htmlElement)\n}\n\n\n\nContainer.prototype.setPointerLock = function (lock) {\n\t// not sure if this will work robustly\n\tthis._shell.pointerLock = !!lock\n}\n\n\n\n\n\n/*\n* INTERNALS\n*/\n\n\n\nfunction createContainerDiv() {\n\t// based on github.com/mikolalysenko/game-shell - makeDefaultContainer()\n\tvar container = document.createElement(\"div\")\n\tcontainer.tabindex = 1\n\tcontainer.style.position = \"absolute\"\n\tcontainer.style.left = \"0px\"\n\tcontainer.style.right = \"0px\"\n\tcontainer.style.top = \"0px\"\n\tcontainer.style.bottom = \"0px\"\n\tcontainer.style.height = \"100%\"\n\tcontainer.style.overflow = \"hidden\"\n\tdocument.body.appendChild(container)\n\tdocument.body.style.overflow = \"hidden\" //Prevent bounce\n\tdocument.body.style.height = \"100%\"\n\tcontainer.id = 'noa-container'\n\treturn container\n}\n\n\nfunction createShell(canvas, opts) {\n\tvar shellDefaults = {\n\t\tpointerLock: true,\n\t\tpreventDefaults: false\n\t}\n\topts = Object.assign(shellDefaults, opts)\n\topts.element = canvas\n\tvar shell = createGameShell(opts)\n\tshell.preventDefaults = opts.preventDefaults\n\treturn shell\n}\n\nfunction getOrCreateCanvas(el) {\n\t// based on github.com/stackgl/gl-now - default canvas\n\tvar canvas = el.querySelector('canvas')\n\tif (!canvas) {\n\t\tcanvas = document.createElement('canvas')\n\t\tcanvas.style.position = \"absolute\"\n\t\tcanvas.style.left = \"0px\"\n\t\tcanvas.style.top = \"0px\"\n\t\tcanvas.style.height = \"100%\"\n\t\tcanvas.style.width = \"100%\"\n\t\tcanvas.id = 'noa-canvas'\n\t\tel.insertBefore(canvas, el.firstChild);\n\t}\n\treturn canvas\n}\n\n\n// track changes in Pointer Lock state\nfunction onLockChange(self, ev) {\n\tvar el = document.pointerLockElement ||\n\t\tdocument.mozPointerLockElement ||\n\t\tdocument.webkitPointerLockElement\n\tif (el) {\n\t\tself.hasPointerLock = true\n\t\tself.emit('gainedPointerLock')\n\t} else {\n\t\tself.hasPointerLock = false\n\t\tself.emit('lostPointerLock')\n\t}\n\t// this works around a Firefox bug where no mouse-in event \n\t// gets issued after starting pointerlock\n\tif (el) {\n\t\t// act as if pointer is in game window while pointerLock is true\n\t\tself.pointerInGame = true\n\t}\n}\n\n\n// set up stuff to detect pointer lock support.\n// Needlessly complex because Chrome/Android claims to support but doesn't.\n// For now, just feature detect, but assume no support if a touch event occurs\n// TODO: see if this makes sense on hybrid touch/mouse devices\nfunction detectPointerLock(self) {\n\tvar lockElementExists =\n\t\t('pointerLockElement' in document) ||\n\t\t('mozPointerLockElement' in document) ||\n\t\t('webkitPointerLockElement' in document)\n\tif (lockElementExists) {\n\t\tself.supportsPointerLock = true\n\t\tvar listener = function (e) {\n\t\t\tself.supportsPointerLock = false\n\t\t\tdocument.removeEventListener(e.type, listener)\n\t\t}\n\t\tdocument.addEventListener('touchmove', listener)\n\t}\n}\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/container.js?"); - -/***/ }), - -/***/ "../../src/lib/entities.js": -/*!****************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/entities.js ***! - \****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar aabb = __webpack_require__(/*! aabb-3d */ \"../../node_modules/aabb-3d/index.js\")\nvar vec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\nvar EntComp = __webpack_require__(/*! ent-comp */ \"../../node_modules/ent-comp/src/ECS.js\")\n// var EntComp = require('../../../../npm-modules/ent-comp')\n\nmodule.exports = function (noa, opts) {\n\treturn new Entities(noa, opts)\n}\n\nvar defaults = {\n\tshadowDistance: 10,\n}\n\n\n\n/**\n * Wrangles entities. \n * This class is an instance of [ECS](https://github.com/andyhall/ent-comp), \n * and as such implements the usual ECS methods.\n * It's also decorated with helpers and accessor functions for getting component existence/state.\n * \n * Expects entity definitions in a specific format - see source `components` folder for examples.\n * \n * @class noa.entities\n*/\n\nfunction Entities(noa, opts) {\n\t// inherit from the ECS library\n\tEntComp.call(this)\n\n\tthis.noa = noa\n\topts = Object.assign({}, defaults, opts)\n\n\t// properties\n\t/**\n\t * Hash containing the component names of built-in components.\n\t * @name names\n\t */\n\tthis.names = {}\n\n\t// options\n\tvar shadowDist = opts.shadowDistance\n\n\t// register components with the ECS\n\tthis.names.position = this.createComponent(__webpack_require__(/*! ../components/position */ \"../../src/components/position.js\")(noa))\n\tthis.names.physics = this.createComponent(__webpack_require__(/*! ../components/physics */ \"../../src/components/physics.js\")(noa))\n\tthis.names.followsEntity = this.createComponent(__webpack_require__(/*! ../components/followsEntity */ \"../../src/components/followsEntity.js\")(noa))\n\tthis.names.mesh = this.createComponent(__webpack_require__(/*! ../components/mesh */ \"../../src/components/mesh.js\")(noa))\n\tthis.names.shadow = this.createComponent(__webpack_require__(/*! ../components/shadow */ \"../../src/components/shadow.js\")(noa, shadowDist))\n\tthis.names.collideTerrain = this.createComponent(__webpack_require__(/*! ../components/collideTerrain */ \"../../src/components/collideTerrain.js\")(noa))\n\tthis.names.collideEntities = this.createComponent(__webpack_require__(/*! ../components/collideEntities */ \"../../src/components/collideEntities.js\")(noa))\n\tthis.names.smoothCamera = this.createComponent(__webpack_require__(/*! ../components/smoothCamera */ \"../../src/components/smoothCamera.js\")(noa))\n\tthis.names.movement = this.createComponent(__webpack_require__(/*! ../components/movement */ \"../../src/components/movement.js\")(noa))\n\tthis.names.receivesInputs = this.createComponent(__webpack_require__(/*! ../components/receivesInputs */ \"../../src/components/receivesInputs.js\")(noa))\n\tthis.names.fadeOnZoom = this.createComponent(__webpack_require__(/*! ../components/fadeOnZoom */ \"../../src/components/fadeOnZoom.js\")(noa))\n\n\t// decorate the entities object with accessor functions\n\tthis.isPlayer = function (id) { return id === noa.playerEntity }\n\tthis.hasPhysics = this.getComponentAccessor(this.names.physics)\n\tthis.cameraSmoothed = this.getComponentAccessor(this.names.smoothCamera)\n\tthis.hasMesh = this.getComponentAccessor(this.names.mesh)\n\n\t// position functions\n\tthis.hasPosition = this.getComponentAccessor(this.names.position)\n\tvar getPos = this.getStateAccessor(this.names.position)\n\tthis.getPositionData = getPos\n\tthis.getPosition = function (id) { return getPos(id).position }\n\tthis.setPosition = function (id, x, y, z) {\n\t\tvar pdat = this.getPositionData(id)\n\t\tvec3.set(pdat.position, x, y, z)\n\t\tvec3.set(pdat.renderPosition, x, y, z)\n\t\tpdat._extentsChanged = true\n\t\tif (this.hasPhysics(id)) {\n\t\t\tsetAABBFromPosition(this.getPhysicsBody(id).aabb, pdat)\n\t\t}\n\t}\n\n\t// physics\n\tvar getPhys = this.getStateAccessor(this.names.physics)\n\tthis.getPhysicsBody = function (id) { return getPhys(id).body }\n\n\t// misc\n\tthis.getMeshData = this.getStateAccessor(this.names.mesh)\n\tthis.getMovement = this.getStateAccessor(this.names.movement)\n\tthis.getCollideTerrain = this.getStateAccessor(this.names.collideTerrain)\n\tthis.getCollideEntities = this.getStateAccessor(this.names.collideEntities)\n\n\t// pairwise collideEntities event - this is for client to override\n\tthis.onPairwiseEntityCollision = function (id1, id2) { }\n\n\t// events\n\tvar self = this\n\tnoa.on('tick', function (dt) { self.tick(dt) })\n\tnoa.on('beforeRender', function (dt) { self.render(dt) })\n\n}\n\n// inherit from EntComp\nEntities.prototype = Object.create(EntComp.prototype)\nEntities.prototype.constructor = Entities\n\n\n\n\n/*\n *\n * ENTITY MANAGER API\n *\n*/\n\n\n/** @param id,name,state */\nEntities.prototype.addComponentAgain = function (id, name, state) {\n\t// removes component first if necessary\n\tif (this.hasComponent(id, name)) this.removeComponent(id, name, true)\n\tthis.addComponent(id, name, state)\n}\n\n\n/** @param x,y,z */\nEntities.prototype.isTerrainBlocked = function (x, y, z) {\n\t// checks if terrain location is blocked by entities\n\tvar box = _blockAABB\n\tvar eps = 0.001\n\tbox.setPosition([x + eps, y + eps, z + eps])\n\tvar hits = this.getEntitiesInAABB(box, this.names.collideTerrain)\n\treturn (hits.length > 0)\n}\nvar _blockAABB = new aabb([0, 0, 0], [0.998, 0.998, 0.998])\n\n\n/** @param x,y,z */\nEntities.prototype.setEntitySize = function (id, xs, ys, zs) {\n\t// adding this so client doesn't need to understand the internals\n\tif (!this.hasPosition(id)) throw 'Set size of entity without a position component'\n\tvar pdat = this.getPositionData(id)\n\tpdat.width = (xs + zs) / 2\n\tpdat.height = ys\n\tpdat._extentsChanged = true\n\tif (this.hasPhysics(id)) {\n\t\tvar box = this.getPhysicsBody(id).aabb\n\t\tsetAABBFromPosition(box, pdat)\n\t}\n}\n\n\nfunction setAABBFromPosition(box, posData) {\n\tvar w = posData.width\n\tvar pos = posData.position\n\tvar hw = w / 2\n\tvec3.set(box.base, pos[0] - hw, pos[1], pos[2] - hw)\n\tvec3.set(box.vec, w, posData.height, w)\n\tvec3.add(box.max, box.base, box.vec)\n}\n\n\n/** @param box */\nEntities.prototype.getEntitiesInAABB = function (box, withComponent) {\n\t// TODO - use bipartite box-intersect?\n\tvar hits = []\n\tvar self = this\n\tvar posArr = (withComponent) ?\n\t\tself.getStatesList(withComponent).map(function (state) {\n\t\t\treturn self.getPositionData(state.__id)\n\t\t}) :\n\t\tposArr = self.getStatesList(this.names.position)\n\tvar tmpBox = _searchBox\n\tfor (var i = 0; i < posArr.length; i++) {\n\t\tsetAABBFromPosition(tmpBox, posArr[i])\n\t\tif (box.intersects(tmpBox)) hits.push(posArr[i].__id)\n\t}\n\treturn hits\n}\nvar _searchBox = new aabb([], [])\n\n\n\n/** \n * Helper to set up a general entity, and populate with some common components depending on arguments.\n * \n * Parameters: position, width, height [, mesh, meshOffset, doPhysics, shadow]\n * \n * @param position\n * @param width\n * @param height..\n */\nEntities.prototype.add = function (position, width, height, // required\n\tmesh, meshOffset, doPhysics, shadow) {\n\n\tvar self = this\n\n\t// new entity\n\tvar eid = this.createEntity()\n\n\t// position component - force position vector to be a vec3\n\tvar pos = vec3.create()\n\tvec3.copy(pos, position)\n\tthis.addComponent(eid, this.names.position, {\n\t\tposition: pos,\n\t\twidth: width,\n\t\theight: height\n\t})\n\n\t// rigid body in physics simulator\n\tif (doPhysics) {\n\t\t// body = this.noa.physics.addBody(box)\n\t\tthis.addComponent(eid, this.names.physics)\n\t\tvar body = this.getPhysicsBody(eid)\n\n\t\t// handler for physics engine to call on auto-step\n\t\tvar smoothName = this.names.smoothCamera\n\t\tbody.onStep = function () {\n\t\t\tself.addComponentAgain(eid, smoothName)\n\t\t}\n\t}\n\n\t// mesh for the entity\n\tif (mesh) {\n\t\tif (!meshOffset) meshOffset = vec3.create()\n\t\tthis.addComponent(eid, this.names.mesh, {\n\t\t\tmesh: mesh,\n\t\t\toffset: meshOffset\n\t\t})\n\t}\n\n\t// add shadow-drawing component\n\tif (shadow) {\n\t\tthis.addComponent(eid, this.names.shadow, { size: width })\n\t}\n\n\treturn eid\n}\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/entities.js?"); - -/***/ }), - -/***/ "../../src/lib/inputs.js": -/*!**************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/inputs.js ***! - \**************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar createInputs = __webpack_require__(/*! game-inputs */ \"../../node_modules/game-inputs/inputs.js\")\n\n\nmodule.exports = function (noa, opts, element) {\n return makeInputs(noa, opts, element)\n}\n\n\nvar defaultBindings = {\n bindings: {\n \"forward\": [\"W\", \"\"],\n \"left\": [\"A\", \"\"],\n \"backward\": [\"S\", \"\"],\n \"right\": [\"D\", \"\"],\n \"fire\": \"\",\n \"mid-fire\": [\"\", \"Q\"],\n \"alt-fire\": [\"\", \"E\"],\n \"jump\": \"\",\n \"sprint\": \"\",\n \"crouch\": \"\"\n }\n}\n\n\nfunction makeInputs(noa, opts, element) {\n opts = Object.assign({}, defaultBindings, opts)\n var inputs = createInputs(element, opts)\n var b = opts.bindings\n for (var name in b) {\n var arr = (Array.isArray(b[name])) ? b[name] : [b[name]]\n arr.unshift(name)\n inputs.bind.apply(inputs, arr)\n }\n return inputs\n}\n\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/inputs.js?"); - -/***/ }), - -/***/ "../../src/lib/objectMesher.js": -/*!********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/objectMesher.js ***! - \********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nvar removeUnorderedListItem = __webpack_require__(/*! ./util */ \"../../src/lib/util.js\").removeUnorderedListItem\n\n\nmodule.exports = new ObjectMesher()\n\n\n// enable for profiling..\nvar PROFILE = 0\n\n\n\n\n// helper class to hold data about a single object mesh\nfunction ObjMeshDat(id, x, y, z) {\n this.id = id | 0\n this.x = x | 0\n this.y = y | 0\n this.z = z | 0\n}\n\n\n\n\n\n\n\n/*\n * \n * \n * Object meshing\n * Per-chunk handling of the creation/disposal of voxels with static meshes\n * \n * \n*/\n\n\nfunction ObjectMesher() {\n\n\n // adds properties to the new chunk that will be used when processing\n this.initChunk = function (chunk) {\n chunk._objectBlocks = {}\n chunk._mergedObjectSystems = []\n }\n\n this.disposeChunk = function (chunk) {\n removeCurrentSystems(chunk)\n chunk._objectBlocks = null\n }\n\n function removeCurrentSystems(chunk) {\n var systems = chunk._mergedObjectSystems\n while (systems.length) {\n var sps = systems.pop()\n if (sps.mesh && chunk.octreeBlock && chunk.octreeBlock.entries) {\n removeUnorderedListItem(chunk.octreeBlock.entries, sps.mesh)\n }\n if (sps.mesh) sps.mesh.dispose()\n sps.dispose()\n }\n }\n\n\n\n // accessors for the chunk to regester as object voxels are set/unset\n this.addObjectBlock = function (chunk, id, x, y, z) {\n var key = x + '|' + y + '|' + z\n chunk._objectBlocks[key] = new ObjMeshDat(id, x, y, z, null)\n }\n\n this.removeObjectBlock = function (chunk, x, y, z) {\n var key = x + '|' + y + '|' + z\n if (chunk._objectBlocks[key]) delete chunk._objectBlocks[key]\n }\n\n\n\n\n /*\n * \n * main implementation - re-creates all needed object mesh instances\n * \n */\n\n this.buildObjectMesh = function (chunk) {\n profile_hook('start')\n // remove the current (if any) sps/mesh\n removeCurrentSystems(chunk)\n\n var scene = chunk.noa.rendering.getScene()\n var objectMeshLookup = chunk.noa.registry._blockMeshLookup\n\n // preprocess everything to build lists of object block keys\n // hashed by material ID and then by block ID\n var matIndexes = {}\n for (var key in chunk._objectBlocks) {\n var blockDat = chunk._objectBlocks[key]\n var blockID = blockDat.id\n var mat = objectMeshLookup[blockID].material\n var matIndex = (mat) ? scene.materials.indexOf(mat) : -1\n if (!matIndexes[matIndex]) matIndexes[matIndex] = {}\n if (!matIndexes[matIndex][blockID]) matIndexes[matIndex][blockID] = []\n matIndexes[matIndex][blockID].push(key)\n }\n profile_hook('preprocess')\n\n // data structure now looks like:\n // matIndexes = {\n // 2: { // i.e. 2nd material in scene\n // 14: { // i.e. voxel ID 14 from registry\n // [ '2|3|4' ] // key of block's local coords\n // }\n // }\n // }\n\n var x0 = chunk.i * chunk.size\n var y0 = chunk.j * chunk.size\n var z0 = chunk.k * chunk.size\n\n // build one SPS for each material\n for (var ix in matIndexes) {\n\n var meshHash = matIndexes[ix]\n var sps = buildSPSforMaterialIndex(chunk, scene, meshHash, x0, y0, z0)\n profile_hook('made SPS')\n\n // build SPS into the scene\n var merged = sps.buildMesh()\n profile_hook('built mesh')\n\n // finish up\n merged.material = (ix > -1) ? scene.materials[ix] : null\n merged.position.x = x0\n merged.position.y = y0\n merged.position.z = z0\n merged.freezeWorldMatrix()\n merged.freezeNormals()\n\n chunk.octreeBlock.entries.push(merged)\n chunk._mergedObjectSystems.push(sps)\n }\n\n profile_hook('end')\n }\n\n\n\n\n function buildSPSforMaterialIndex(chunk, scene, meshHash, x0, y0, z0) {\n var blockHash = chunk._objectBlocks\n // base sps\n var sps = new BABYLON.SolidParticleSystem('object_sps_' + chunk.id, scene, {\n updatable: false,\n })\n\n var blockHandlerLookup = chunk.noa.registry._blockHandlerLookup\n var objectMeshLookup = chunk.noa.registry._blockMeshLookup\n\n // run through mesh hash adding shapes and position functions\n for (var blockID in meshHash) {\n var mesh = objectMeshLookup[blockID]\n var blockArr = meshHash[blockID]\n var count = blockArr.length\n\n var handlerFn\n var handlers = blockHandlerLookup[blockID]\n if (handlers) handlerFn = handlers.onCustomMeshCreate\n // jshint -W083\n var setShape = function (particle, partIndex, shapeIndex) {\n var key = blockArr[shapeIndex]\n var dat = blockHash[key]\n // set global positions for the custom handler, if any\n particle.position.set(x0 + dat.x + 0.5, y0 + dat.y, z0 + dat.z + 0.5)\n if (handlerFn) handlerFn(particle, x0 + dat.x, y0 + dat.y, z0 + dat.z)\n // revert to local positions\n particle.position.x -= x0\n particle.position.y -= y0\n particle.position.z -= z0\n }\n sps.addShape(mesh, count, { positionFunction: setShape })\n blockArr.length = 0\n }\n\n return sps\n }\n\n\n\n\n}\n\n\n\n\n\n\n\n\n\nvar profile_hook = (function () {\n if (!PROFILE) return function () { }\n var every = 50\n var timer = new (__webpack_require__(/*! ./util */ \"../../src/lib/util.js\").Timer)(every, 'Object meshing')\n return function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/objectMesher.js?"); - -/***/ }), - -/***/ "../../src/lib/physics.js": -/*!***************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/physics.js ***! - \***************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar createPhysics = __webpack_require__(/*! voxel-physics-engine */ \"../../node_modules/voxel-physics-engine/src/index.js\")\n// var createPhysics = require('../../../../npm-modules/voxel-physics-engine')\n\n\nmodule.exports = function (noa, opts) {\n\treturn makePhysics(noa, opts)\n}\n\n\n/*\n *\n * Simple wrapper module for the physics library\n *\n*/\n\n\nvar defaults = {\n\tgravity: [0, -10, 0],\n\tairDrag: 0.1,\n}\n\n\nfunction makePhysics(noa, opts) {\n\topts = Object.assign({}, defaults, opts)\n\tvar world = noa.world\n\tvar blockGetter = function (x, y, z) { return world.getBlockSolidity(x, y, z) }\n\tvar isFluidGetter = function (x, y, z) { return world.getBlockFluidity(x, y, z) }\n\n\tvar physics = createPhysics(opts, blockGetter, isFluidGetter)\n\n\treturn physics\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/physics.js?"); - -/***/ }), - -/***/ "../../src/lib/registry.js": -/*!****************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/registry.js ***! - \****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nmodule.exports = function (noa, opts) {\n return new Registry(noa, opts)\n}\n\n\n/**\n * This is where clients register block types and their materials & properties.\n * @class noa.registry\n */\n\n\n/*\n * data structs in the registry:\n * registry \n * blockSolidity: id -> boolean\n * blockOpacity: id -> boolean\n * blockIsFluid: id -> boolean\n * blockMats: id -> 6x matID [-x, +x, -y, +y, -z, +z]\n * blockProps id -> obj of less-often accessed properties\n * blockMeshes: id -> obj/null (custom mesh to instantiate)\n * blockHandlers id -> instance of `BlockCallbackHolder` or null \n * matIDs matName -> matID (int)\n * matData matID -> { color, alpha, texture, textureAlpha }\n*/\n\n\nvar defaults = {\n texturePath: ''\n}\n\nvar blockDefaults = {\n solid: true,\n opaque: true,\n fluidDensity: 1.0,\n viscosity: 0.5,\n}\n\nvar MAX_BLOCK_IDS = 255 // currently stored in chunks as int8\n\n\nfunction Registry(noa, opts) {\n this.noa = noa\n opts = Object.assign({}, defaults, opts)\n\n\n /* \n * \n * data structures\n * \n */\n\n // lookup arrays for block props and flags - all keyed by blockID\n // fill in first value for id=0, empty space\n var blockSolidity = [false]\n var blockOpacity = [false]\n var blockIsFluid = [false]\n var blockMats = [0, 0, 0, 0, 0, 0]\n var blockProps = [null]\n var blockMeshes = [null]\n var blockHandlers = [null]\n\n // material data structs\n var matIDs = {} // mat name -> id\n var matData = [null] // mat id -> { color, alpha, texture, textureAlpha }\n\n // option data to save\n var texturePath = opts.texturePath\n\n\n\n /* \n * \n * Block registration methods\n * \n */\n\n\n\n /**\n * Register (by integer ID) a block type and its parameters.\n * \n * @param id: integer, currently 1..255. This needs to be passed in by the \n * client because it goes into the chunk data, which someday will get serialized.\n * \n * @param options: Recognized fields for the options object:\n * \n * * material: can be:\n * * one (String) material name\n * * array of 2 names: [top/bottom, sides]\n * * array of 3 names: [top, bottom, sides]\n * * array of 6 names: [-x, +x, -y, +y, -z, +z]\n * If not specified, terrain won't be meshed for the block type\n * * solid: (true) solidity for physics purposes\n * * opaque: (true) fully obscures neighboring blocks\n * * fluid: (false) whether nonsolid block is a fluid (buoyant, viscous..)\n * * blockMeshes: (null) if specified, noa will create an instance of the mesh instead of rendering voxel terrain\n * * fluidDensity: (1.0) for fluid blocks\n * * viscosity: (0.5) for fluid blocks\n * * onLoad(): block event handler\n * * onUnload(): block event handler\n * * onSet(): block event handler\n * * onUnset(): block event handler\n * * onCustomMeshCreate(): block event handler\n */\n\n\n this.registerBlock = function (id, _options) {\n _options = _options || {}\n blockDefaults.solid = !_options.fluid\n blockDefaults.opaque = !_options.fluid\n var opts = Object.assign({}, blockDefaults, _options)\n\n // console.log('register block: ', id, opts)\n if (id < 1 || id > MAX_BLOCK_IDS) throw 'Block id exceeds max: ' + id\n\n // if block ID is greater than current highest ID, \n // register fake blocks to avoid holes in lookup arrays\n while (id > blockSolidity.length) {\n this.registerBlock(blockSolidity.length, {})\n }\n\n // flags default to solid, opaque, nonfluid\n blockSolidity[id] = !!opts.solid\n blockOpacity[id] = !!opts.opaque\n blockIsFluid[id] = !!opts.fluid\n\n // store any custom mesh, and if one is present assume no material\n blockMeshes[id] = opts.blockMesh || null\n if (blockMeshes[id]) opts.material = null\n\n // parse out material parameter\n // always store 6 material IDs per blockID, so material lookup is monomorphic\n var mat = opts.material || null\n var mats\n if (!mat) {\n mats = [null, null, null, null, null, null]\n } else if (typeof mat == 'string') {\n mats = [mat, mat, mat, mat, mat, mat]\n } else if (mat.length && mat.length == 2) {\n // interpret as [top/bottom, sides]\n mats = [mat[1], mat[1], mat[0], mat[0], mat[1], mat[1]]\n } else if (mat.length && mat.length == 3) {\n // interpret as [top, bottom, sides]\n mats = [mat[2], mat[2], mat[0], mat[1], mat[2], mat[2]]\n } else if (mat.length && mat.length == 6) {\n // interpret as [-x, +x, -y, +y, -z, +z]\n mats = mat\n } else throw 'Invalid material parameter: ' + mat\n\n // argument is material name, but store as material id, allocating one if needed\n for (var i = 0; i < 6; ++i) {\n blockMats[id * 6 + i] = getMaterialId(this, matIDs, mats[i], true)\n }\n\n // props data object - currently only used for fluid properties\n blockProps[id] = {}\n\n // if block is fluid, initialize properties if needed\n if (blockIsFluid[id]) {\n blockProps[id].fluidDensity = opts.fluidDensity\n blockProps[id].viscosity = opts.viscosity\n }\n\n // event callbacks\n var hasHandler = opts.onLoad || opts.onUnload || opts.onSet || opts.onUnset || opts.onCustomMeshCreate\n blockHandlers[id] = (hasHandler) ? new BlockCallbackHolder(opts) : null\n\n return id\n }\n\n\n\n\n /*\n * Register (by name) a material and its parameters.\n * \n * @param name,color,textureURL,texHasAlpha\n * @param renderMaterial an optional BABYLON material to be used for block faces with this block material\n */\n\n this.registerMaterial = function (name, color, textureURL, texHasAlpha, renderMaterial) {\n // console.log('register mat: ', name, color, textureURL)\n var id = matIDs[name] || matData.length\n matIDs[name] = id\n var alpha = 1\n if (color && color.length == 4) {\n alpha = color.pop()\n }\n matData[id] = {\n color: color || [1, 1, 1],\n alpha: alpha,\n texture: textureURL ? texturePath + textureURL : '',\n textureAlpha: !!texHasAlpha,\n renderMat: renderMaterial || null,\n }\n return id\n }\n\n\n\n /*\n * quick accessors for querying block ID stuff\n */\n\n // block solidity (as in physics)\n this.getBlockSolidity = function (id) {\n return blockSolidity[id]\n }\n\n // block opacity - whether it obscures the whole voxel (dirt) or \n // can be partially seen through (like a fencepost, etc)\n this.getBlockOpacity = function (id) {\n return blockOpacity[id]\n }\n\n // block is fluid or not\n this.getBlockFluidity = function (id) {\n return blockIsFluid[id]\n }\n\n // Get block property object passed in at registration\n this.getBlockProps = function (id) {\n return blockProps[id]\n }\n\n // look up a block ID's face material\n // dir is a value 0..5: [ +x, -x, +y, -y, +z, -z ]\n this.getBlockFaceMaterial = function (blockId, dir) {\n return blockMats[blockId * 6 + dir]\n }\n\n\n\n\n\n // look up material color given ID\n this.getMaterialColor = function (matID) {\n return matData[matID].color\n }\n\n // look up material texture given ID\n this.getMaterialTexture = function (matID) {\n return matData[matID].texture\n }\n\n // look up material's properties: color, alpha, texture, textureAlpha\n this.getMaterialData = function (matID) {\n return matData[matID]\n }\n\n\n\n\n\n /*\n * \n * Meant for internal use within the engine\n * \n */\n\n\n // internal access to lookup arrays\n this._solidityLookup = blockSolidity\n this._opacityLookup = blockOpacity\n this._blockMeshLookup = blockMeshes\n this._blockHandlerLookup = blockHandlers\n\n\n\n\n\n\n // look up color used for vertices of blocks of given material\n // - i.e. white if it has a texture, color otherwise\n this._getMaterialVertexColor = function (matID) {\n if (matData[matID].texture) return white\n return matData[matID].color\n }\n var white = [1, 1, 1]\n\n\n\n\n\n /*\n * \n * default initialization\n * \n */\n\n // add a default material and set ID=1 to it\n // note that registering new block data overwrites the old\n this.registerMaterial('dirt', [0.4, 0.3, 0], null)\n this.registerBlock(1, { material: 'dirt' })\n\n\n\n}\n\n\n\n/*\n * \n * helpers\n * \n*/\n\n\n\n// look up material ID given its name\n// if lazy is set, pre-register the name and return an ID\nfunction getMaterialId(reg, matIDs, name, lazyInit) {\n if (!name) return 0\n var id = matIDs[name]\n if (id === undefined && lazyInit) id = reg.registerMaterial(name)\n return id\n}\n\n\n\n// data class for holding block callback references\nfunction BlockCallbackHolder(opts) {\n this.onLoad = opts.onLoad || null\n this.onUnload = opts.onUnload || null\n this.onSet = opts.onSet || null\n this.onUnset = opts.onUnset || null\n this.onCustomMeshCreate = opts.onCustomMeshCreate || null\n}\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/registry.js?"); - -/***/ }), - -/***/ "../../src/lib/rendering.js": -/*!*****************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/rendering.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar glvec3 = __webpack_require__(/*! gl-vec3 */ \"../../node_modules/gl-vec3/index.js\")\nvar aabb = __webpack_require__(/*! aabb-3d */ \"../../node_modules/aabb-3d/index.js\")\nvar sweep = __webpack_require__(/*! voxel-aabb-sweep */ \"../../node_modules/voxel-aabb-sweep/index.js\")\nvar removeUnorderedListItem = __webpack_require__(/*! ./util */ \"../../src/lib/util.js\").removeUnorderedListItem\n\n\n// For now, assume Babylon.js has been imported into the global space already\nif (!BABYLON) {\n throw new Error('Babylon.js reference not found! Abort! Abort!')\n}\n\nmodule.exports = function (noa, opts, canvas) {\n return new Rendering(noa, opts, canvas)\n}\n\nvar vec3 = BABYLON.Vector3 // not a gl-vec3, in this module only!!\nvar col3 = BABYLON.Color3\n\n\n\n// profiling flags\nvar PROFILE = 0\n\n\n\nvar defaults = {\n showFPS: false,\n antiAlias: true,\n clearColor: [0.8, 0.9, 1],\n ambientColor: [1, 1, 1],\n lightDiffuse: [1, 1, 1],\n lightSpecular: [1, 1, 1],\n groundLightColor: [0.5, 0.5, 0.5],\n initialCameraZoom: 0,\n cameraZoomSpeed: .2,\n cameraMaxAngle: (Math.PI / 2) - 0.01,\n useAO: true,\n AOmultipliers: [0.93, 0.8, 0.5],\n reverseAOmultiplier: 1.0,\n useOctreesForDynamicMeshes: true,\n preserveDrawingBuffer: true,\n}\n\n\n\n\n\nfunction Rendering(noa, opts, canvas) {\n this.noa = noa\n opts = Object.assign({}, defaults, opts)\n this.zoomDistance = opts.initialCameraZoom // zoom setting\n this._currentZoom = this.zoomDistance // current actual zoom level\n this._cameraZoomSpeed = opts.cameraZoomSpeed\n this._maxCamAngle = opts.cameraMaxAngle\n\n // internals\n this._dynamicMeshes = []\n this.useAO = !!opts.useAO\n this.aoVals = opts.AOmultipliers\n this.revAoVal = opts.reverseAOmultiplier\n this.meshingCutoffTime = 6 // ms\n this._dynamicMeshOctrees = opts.useOctreesForDynamicMeshes\n this._resizeDebounce = 250 // ms\n\n // set up babylon scene\n initScene(this, canvas, opts)\n\n // for debugging\n if (opts.showFPS) setUpFPS()\n}\n\n\n// Constructor helper - set up the Babylon.js scene and basic components\nfunction initScene(self, canvas, opts) {\n if (!BABYLON) throw new Error('BABYLON.js engine not found!')\n\n // init internal properties\n self._engine = new BABYLON.Engine(canvas, opts.antiAlias, {\n preserveDrawingBuffer: opts.preserveDrawingBuffer,\n })\n self._scene = new BABYLON.Scene(self._engine)\n var scene = self._scene\n // remove built-in listeners\n scene.detachControl()\n\n // octree setup\n self._octree = new BABYLON.Octree($ => { })\n self._octree.blocks = []\n scene._selectionOctree = self._octree\n\n // camera, and empty mesh to hold it, and one to accumulate rotations\n self._rotationHolder = new BABYLON.Mesh('rotHolder', scene)\n self._cameraHolder = new BABYLON.Mesh('camHolder', scene)\n self._camera = new BABYLON.FreeCamera('camera', new vec3(0, 0, 0), scene)\n self._camera.parent = self._cameraHolder\n self._camera.minZ = .01\n self._cameraHolder.visibility = false\n self._rotationHolder.visibility = false\n\n // plane obscuring the camera - for overlaying an effect on the whole view\n self._camScreen = BABYLON.Mesh.CreatePlane('camScreen', 10, scene)\n self.addMeshToScene(self._camScreen)\n self._camScreen.position.z = .1\n self._camScreen.parent = self._camera\n self._camScreenMat = self.makeStandardMaterial('camscreenmat')\n self._camScreen.material = self._camScreenMat\n self._camScreen.setEnabled(false)\n self._camLocBlock = 0\n\n // apply some defaults\n self._light = new BABYLON.HemisphericLight('light', new vec3(0.1, 1, 0.3), scene)\n function arrToColor(a) { return new col3(a[0], a[1], a[2]) }\n scene.clearColor = arrToColor(opts.clearColor)\n scene.ambientColor = arrToColor(opts.ambientColor)\n self._light.diffuse = arrToColor(opts.lightDiffuse)\n self._light.specular = arrToColor(opts.lightSpecular)\n self._light.groundColor = arrToColor(opts.groundLightColor)\n\n // make a default flat material (used or clone by terrain, etc)\n self.flatMaterial = self.makeStandardMaterial('flatmat')\n\n}\n\n\n\n/*\n * PUBLIC API \n*/\n\n// Init anything about scene that needs to wait for engine internals\nRendering.prototype.initScene = function () {\n // engine entity to follow the player and act as camera target\n this.cameraTarget = this.noa.ents.createEntity(['position'])\n this.noa.ents.addComponent(this.cameraTarget, 'followsEntity', {\n entity: this.noa.playerEntity,\n offset: [0, this.noa.playerEyeOffset, 0],\n })\n}\n\n// accessor for client app to build meshes and register materials\nRendering.prototype.getScene = function () {\n return this._scene\n}\n\n// per-tick listener for rendering-related stuff\nRendering.prototype.tick = function (dt) {\n if (this._dynamicMeshOctrees) updateDynamicMeshOctrees(this)\n}\n\n\n\n\n\nRendering.prototype.render = function (dt) {\n profile_hook('start')\n updateCamera(this)\n profile_hook('updateCamera')\n this._engine.beginFrame()\n profile_hook('beginFrame')\n this._scene.render()\n profile_hook('render')\n fps_hook()\n this._engine.endFrame()\n profile_hook('endFrame')\n profile_hook('end')\n}\n\n\n\nRendering.prototype.resize = function (e) {\n if (!pendingResize) {\n pendingResize = true\n setTimeout(() => {\n this._engine.resize()\n pendingResize = false\n }, this._resizeDebounce)\n }\n}\nvar pendingResize = false\n\n\n\nRendering.prototype.highlightBlockFace = function (show, posArr, normArr) {\n var m = getHighlightMesh(this)\n if (show) {\n // bigger slop when zoomed out\n var dist = this._currentZoom + glvec3.distance(this.noa.getPlayerEyePosition(), posArr)\n var slop = 0.001 + 0.001 * dist\n var pos = _highlightPos\n for (var i = 0; i < 3; ++i) {\n pos[i] = Math.floor(posArr[i]) + .5 + ((0.5 + slop) * normArr[i])\n }\n m.position.copyFromFloats(pos[0], pos[1], pos[2])\n m.rotation.x = (normArr[1]) ? Math.PI / 2 : 0\n m.rotation.y = (normArr[0]) ? Math.PI / 2 : 0\n }\n m.setEnabled(show)\n}\nvar _highlightPos = glvec3.create()\n\n\nRendering.prototype.getCameraVector = function () {\n return vec3.TransformCoordinates(BABYLON.Axis.Z, this._rotationHolder.getWorldMatrix())\n}\nvar zero = vec3.Zero()\nRendering.prototype.getCameraPosition = function () {\n return vec3.TransformCoordinates(zero, this._camera.getWorldMatrix())\n}\nRendering.prototype.getCameraRotation = function () {\n var rot = this._rotationHolder.rotation\n return [rot.x, rot.y]\n}\nRendering.prototype.setCameraRotation = function (x, y) {\n var rot = this._rotationHolder.rotation\n rot.x = Math.max(-this._maxCamAngle, Math.min(this._maxCamAngle, x))\n rot.y = y\n}\n\n\n\n\n// add a mesh to the scene's octree setup so that it renders\n// pass in isStatic=true if the mesh won't move (i.e. change octree blocks)\nRendering.prototype.addMeshToScene = function (mesh, isStatic) {\n // exit silently if mesh has already been added and not removed\n if (mesh._currentNoaChunk || this._octree.dynamicContent.includes(mesh)) {\n return\n }\n var pos = mesh.position\n var chunk = this.noa.world._getChunkByCoords(pos.x, pos.y, pos.z)\n if (this._dynamicMeshOctrees && chunk && chunk.octreeBlock) {\n // add to an octree\n chunk.octreeBlock.entries.push(mesh)\n mesh._currentNoaChunk = chunk\n } else {\n // mesh added outside an active chunk - so treat as scene-dynamic\n this._octree.dynamicContent.push(mesh)\n }\n // remember for updates if it's not static\n if (!isStatic) this._dynamicMeshes.push(mesh)\n // handle remover when mesh gets disposed\n var remover = this.removeMeshFromScene.bind(this, mesh)\n mesh.onDisposeObservable.add(remover)\n}\n\n// undo the above\nRendering.prototype.removeMeshFromScene = function (mesh) {\n if (mesh._currentNoaChunk && mesh._currentNoaChunk.octreeBlock) {\n removeUnorderedListItem(mesh._currentNoaChunk.octreeBlock.entries, mesh)\n }\n mesh._currentNoaChunk = null\n removeUnorderedListItem(this._octree.dynamicContent, mesh)\n removeUnorderedListItem(this._dynamicMeshes, mesh)\n}\n\n\n\n\n// runs once per tick - move any dynamic meshes to correct chunk octree\nfunction updateDynamicMeshOctrees(self) {\n for (var i = 0; i < self._dynamicMeshes.length; i++) {\n var mesh = self._dynamicMeshes[i]\n if (mesh._isDisposed) continue // shouldn't be possible\n var pos = mesh.position\n var prev = mesh._currentNoaChunk || null\n var next = self.noa.world._getChunkByCoords(pos.x, pos.y, pos.z) || null\n if (prev === next) continue\n // mesh has moved chunks since last update\n // remove from previous location...\n if (prev && prev.octreeBlock) {\n removeUnorderedListItem(prev.octreeBlock.entries, mesh)\n } else {\n removeUnorderedListItem(self._octree.dynamicContent, mesh)\n }\n // ... and add to new location\n if (next && next.octreeBlock) {\n next.octreeBlock.entries.push(mesh)\n } else {\n self._octree.dynamicContent.push(mesh)\n }\n mesh._currentNoaChunk = next\n }\n}\n\n\n\nRendering.prototype.makeMeshInstance = function (mesh, isStatic) {\n var m = mesh.createInstance(mesh.name + ' instance' || false)\n if (mesh.billboardMode) m.billboardMode = mesh.billboardMode\n // add to scene so as to render\n this.addMeshToScene(m, isStatic)\n\n // testing performance tweaks\n\n // make instance meshes skip over getLOD checks, since there may be lots of them\n // mesh.getLOD = m.getLOD = function () { return mesh }\n m._currentLOD = mesh\n\n // make terrain instance meshes skip frustum checks \n // (they'll still get culled by octree checks)\n // if (isStatic) m.isInFrustum = function () { return true }\n\n return m\n}\n\n\n\n// Create a default standardMaterial:\n// flat, nonspecular, fully reflects diffuse and ambient light\nRendering.prototype.makeStandardMaterial = function (name) {\n var mat = new BABYLON.StandardMaterial(name, this._scene)\n mat.specularColor.copyFromFloats(0, 0, 0)\n mat.ambientColor.copyFromFloats(1, 1, 1)\n mat.diffuseColor.copyFromFloats(1, 1, 1)\n return mat\n}\n\n\n\n\n\n\n\n/*\n *\n * \n * ACCESSORS FOR CHUNK ADD/REMOVAL/MESHING\n *\n * \n*/\n\nRendering.prototype.prepareChunkForRendering = function (chunk) {\n var cs = chunk.size\n var min = new vec3(chunk.x, chunk.y, chunk.z)\n var max = new vec3(chunk.x + cs, chunk.y + cs, chunk.z + cs)\n chunk.octreeBlock = new BABYLON.OctreeBlock(min, max, undefined, undefined, undefined, $ => { })\n this._octree.blocks.push(chunk.octreeBlock)\n}\n\nRendering.prototype.disposeChunkForRendering = function (chunk) {\n this.removeTerrainMesh(chunk)\n removeUnorderedListItem(this._octree.blocks, chunk.octreeBlock)\n chunk.octreeBlock.entries.length = 0\n chunk.octreeBlock = null\n}\n\nRendering.prototype.addTerrainMesh = function (chunk, mesh) {\n this.removeTerrainMesh(chunk)\n if (mesh.getIndices().length) this.addMeshToScene(mesh, true)\n chunk._terrainMesh = mesh\n}\n\nRendering.prototype.removeTerrainMesh = function (chunk) {\n if (!chunk._terrainMesh) return\n chunk._terrainMesh.dispose()\n chunk._terrainMesh = null\n}\n\n\n\n\n\n\n\n\n\n\n/*\n *\n * INTERNALS\n *\n*/\n\n\n\n\n/*\n *\n * zoom/camera related internals\n *\n*/\n\n\n// check if obstructions are behind camera by sweeping back an AABB\n// along the negative camera vector\n\nfunction cameraObstructionDistance(self) {\n var size = 0.2\n if (!_camBox) {\n _camBox = new aabb([0, 0, 0], [size * 2, size * 2, size * 2])\n _getVoxel = function (x, y, z) {\n return self.noa.world.getBlockSolidity(x, y, z)\n }\n }\n\n var pos = self._cameraHolder.position\n glvec3.set(_posVec, pos.x - size, pos.y - size, pos.z - size)\n _camBox.setPosition(_posVec)\n\n var dist = -self.zoomDistance\n var cam = self.getCameraVector()\n glvec3.set(_camVec, dist * cam.x, dist * cam.y, dist * cam.z)\n\n return sweep(_getVoxel, _camBox, _camVec, function (dist, axis, dir, vec) {\n return true\n }, true)\n}\n\nvar _posVec = glvec3.create()\nvar _camVec = glvec3.create()\nvar _camBox\nvar _getVoxel\n\n\n\n\n// Various updates to camera position/zoom, called every render\n\nfunction updateCamera(self) {\n // update cameraHolder pos/rot from rotation holder and target entity\n self._cameraHolder.rotation.copyFrom(self._rotationHolder.rotation)\n var cpos = self.noa.ents.getPositionData(self.cameraTarget).renderPosition\n self._cameraHolder.position.copyFromFloats(cpos[0], cpos[1], cpos[2])\n\n // check obstructions and tween camera towards clipped position\n var dist = self.zoomDistance\n var speed = self._cameraZoomSpeed\n if (dist > 0) {\n dist = cameraObstructionDistance(self)\n if (dist < self._currentZoom) self._currentZoom = dist\n }\n self._currentZoom += speed * (dist - self._currentZoom)\n self._camera.position.z = -self._currentZoom\n\n // check id of block camera is in for overlay effects (e.g. being in water) \n var cam = self.getCameraPosition()\n var id = self.noa.world.getBlockID(Math.floor(cam.x), Math.floor(cam.y), Math.floor(cam.z))\n checkCameraEffect(self, id)\n}\n\n\n\n// If camera's current location block id has alpha color (e.g. water), apply/remove an effect\n\nfunction checkCameraEffect(self, id) {\n if (id === self._camLocBlock) return\n if (id === 0) {\n self._camScreen.setEnabled(false)\n } else {\n var matId = self.noa.registry.getBlockFaceMaterial(id, 0)\n if (matId) {\n var matData = self.noa.registry.getMaterialData(matId)\n var col = matData.color\n var alpha = matData.alpha\n if (col && alpha && alpha < 1) {\n self._camScreenMat.diffuseColor = new col3(col[0], col[1], col[2])\n self._camScreenMat.alpha = alpha\n self._camScreen.setEnabled(true)\n }\n }\n }\n self._camLocBlock = id\n}\n\n\n\n\n\n\n// make or get a mesh for highlighting active voxel\nfunction getHighlightMesh(rendering) {\n var m = rendering._highlightMesh\n if (!m) {\n var mesh = BABYLON.Mesh.CreatePlane(\"highlight\", 1.0, rendering._scene)\n var hlm = rendering.makeStandardMaterial('highlightMat')\n hlm.backFaceCulling = false\n hlm.emissiveColor = new col3(1, 1, 1)\n hlm.alpha = 0.2\n mesh.material = hlm\n m = rendering._highlightMesh = mesh\n // outline\n var s = 0.5\n var lines = BABYLON.Mesh.CreateLines(\"hightlightLines\", [\n new vec3(s, s, 0),\n new vec3(s, -s, 0),\n new vec3(-s, -s, 0),\n new vec3(-s, s, 0),\n new vec3(s, s, 0)\n ], rendering._scene)\n lines.color = new col3(1, 1, 1)\n lines.parent = mesh\n\n rendering.addMeshToScene(m)\n rendering.addMeshToScene(lines)\n }\n return m\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/*\n * \n * sanity checks:\n * \n*/\n\nRendering.prototype.debug_SceneCheck = function () {\n var meshes = this._scene.meshes\n var dyns = this._octree.dynamicContent\n var octs = []\n var numOcts = 0\n var mats = this._scene.materials\n var allmats = []\n mats.forEach(mat => {\n if (mat.subMaterials) mat.subMaterials.forEach(mat => allmats.push(mat))\n else allmats.push(mat)\n })\n this._octree.blocks.forEach(function (block) {\n numOcts++\n block.entries.forEach(m => octs.push(m))\n })\n meshes.forEach(function (m) {\n if (m._isDisposed) warn(m, 'disposed mesh in scene')\n if (empty(m)) return\n if (missing(m, dyns, octs)) warn(m, 'non-empty mesh missing from octree')\n if (!m.material) { warn(m, 'non-empty scene mesh with no material'); return }\n (m.material.subMaterials || [m.material]).forEach(function (mat) {\n if (missing(mat, mats)) warn(mat, 'mesh material not in scene')\n })\n })\n var unusedMats = []\n allmats.forEach(mat => {\n var used = false\n meshes.forEach(mesh => {\n if (mesh.material === mat) used = true\n if (!mesh.material || !mesh.material.subMaterials) return\n if (mesh.material.subMaterials.includes(mat)) used = true\n })\n if (!used) unusedMats.push(mat.name)\n })\n if (unusedMats.length) {\n console.warn('Materials unused by any mesh: ', unusedMats.join(', '))\n }\n dyns.forEach(function (m) {\n if (missing(m, meshes)) warn(m, 'octree/dynamic mesh not in scene')\n })\n octs.forEach(function (m) {\n if (missing(m, meshes)) warn(m, 'octree block mesh not in scene')\n })\n var avgPerOct = Math.round(10 * octs.length / numOcts) / 10\n console.log('meshes - octree:', octs.length, ' dynamic:', dyns.length,\n ' avg meshes/octreeBlock:', avgPerOct)\n function warn(obj, msg) { console.warn(obj.name + ' --- ' + msg) }\n function empty(mesh) { return (mesh.getIndices().length === 0) }\n function missing(obj, list1, list2) {\n if (!obj) return false\n if (list1.includes(obj)) return false\n if (list2 && list2.includes(obj)) return false\n return true\n }\n return 'done.'\n}\n\nRendering.prototype.debug_MeshCount = function () {\n var ct = {}\n this._scene.meshes.forEach(m => {\n var n = m.name || ''\n n = n.replace(/-\\d+.*/, '#')\n n = n.replace(/\\d+.*/, '#')\n n = n.replace(/(rotHolder|camHolder|camScreen)/, 'rendering use')\n n = n.replace(/atlas sprite .*/, 'atlas sprites')\n ct[n] = ct[n] || 0\n ct[n]++\n })\n for (var s in ct) console.log(' ' + (ct[s] + ' ').substr(0, 7) + s)\n}\n\n\n\n\n\n\n\nvar profile_hook = (function () {\n if (!PROFILE) return function () { }\n var every = 200\n var timer = new (__webpack_require__(/*! ./util */ \"../../src/lib/util.js\").Timer)(every, 'render internals')\n return function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\n\n\n\nvar fps_hook = function () { }\nfunction setUpFPS() {\n var div = document.createElement('div')\n div.id = 'noa_fps'\n var style = 'position:absolute; top:0; right:0; z-index:0;'\n style += 'color:white; background-color:rgba(0,0,0,0.5);'\n style += 'font:14px monospace; text-align:center;'\n style += 'min-width:2em; margin:4px;'\n div.style = style\n document.body.appendChild(div)\n var every = 1000\n var ct = 0\n var longest = 0\n var start = performance.now()\n var last = start\n fps_hook = function () {\n ct++\n var nt = performance.now()\n if (nt - last > longest) longest = nt - last\n last = nt\n if (nt - start < every) return\n var fps = Math.round(ct / (nt - start) * 1000)\n var min = Math.round(1 / longest * 1000)\n div.innerHTML = fps + '
' + min\n ct = 0\n longest = 0\n start = nt\n }\n}\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/rendering.js?"); - -/***/ }), - -/***/ "../../src/lib/terrainMesher.js": -/*!*********************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/terrainMesher.js ***! - \*********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\n\n\nmodule.exports = new TerrainMesher()\n\n\n\n\n// enable for profiling..\nvar PROFILE = 0\n\n\n\n\n/*\n * \n * TERRAIN MESHER!!\n * \n*/\n\n\nfunction TerrainMesher() {\n\n var greedyMesher = new GreedyMesher()\n var meshBuilder = new MeshBuilder()\n\n\n /*\n * \n * Entry point and high-level flow\n * \n */\n\n this.meshChunk = function (chunk, matGetter, colGetter, ignoreMaterials, useAO, aoVals, revAoVal) {\n profile_hook('start')\n var noa = chunk.noa\n\n // args\n var array = chunk.array\n var mats = matGetter || noa.registry.getBlockFaceMaterial\n var cols = colGetter || noa.registry._getMaterialVertexColor\n var ao = (useAO === undefined) ? noa.rendering.useAO : useAO\n var vals = aoVals || noa.rendering.aoVals\n var rev = isNaN(revAoVal) ? noa.rendering.revAoVal : revAoVal\n\n // greedy mesher creates an array of Submesh structs\n var subMeshes = greedyMesher.mesh(array, mats, cols, ao, vals, rev)\n\n // builds the babylon mesh that will be added to the scene\n var mesh\n if (Object.keys(subMeshes).length) {\n mesh = meshBuilder.build(chunk, subMeshes, ignoreMaterials)\n profile_hook('built terrain')\n }\n\n profile_hook('end')\n return mesh || null\n }\n\n}\n\n\n\n\n/*\n * \n * Submesh - holds one submesh worth of greedy-meshed data\n * \n * Basically, the greedy mesher builds these and the mesh builder consumes them\n * \n*/\n\nfunction Submesh(id) {\n this.id = id | 0\n this.positions = []\n this.indices = []\n this.normals = []\n this.colors = []\n this.uvs = []\n}\n\nSubmesh.prototype.dispose = function () {\n this.positions = null\n this.indices = null\n this.normals = null\n this.colors = null\n this.uvs = null\n}\n\n\n\n\n\n\n\n\n/*\n * \n * Mesh Builder - turns an array of Submesh data into a \n * Babylon.js mesh/submeshes, ready to be added to the scene\n * \n*/\n\nfunction MeshBuilder() {\n\n var noa\n\n\n // core\n this.build = function (chunk, meshdata, ignoreMaterials) {\n noa = chunk.noa\n\n // preprocess meshdata entries to merge those that will use default terrain material\n var mergeCriteria = function (mdat) {\n if (ignoreMaterials) return true\n if (mdat.renderMat) return false\n var url = noa.registry.getMaterialTexture(mdat.id)\n var alpha = noa.registry.getMaterialData(mdat.id).alpha\n if (url || alpha < 1) return false\n }\n mergeSubmeshes(meshdata, mergeCriteria)\n\n // now merge everything, keeping track of vertices/indices/materials\n var results = mergeSubmeshes(meshdata, () => true)\n\n // merge sole remaining submesh instance into a babylon mesh\n var mdat = meshdata[results.mergedID]\n var name = 'chunk_' + chunk.id\n var mats = results.matIDs.map(id => getTerrainMaterial(id, ignoreMaterials))\n var mesh = buildMeshFromSubmesh(mdat, name, mats, results.vertices, results.indices)\n\n // position, freeze and exit\n var x = chunk.i * chunk.size\n var y = chunk.j * chunk.size\n var z = chunk.k * chunk.size\n mesh.position.x = x\n mesh.position.y = y\n mesh.position.z = z\n\n mesh.freezeWorldMatrix()\n mesh.freezeNormals()\n return mesh\n }\n\n\n\n // this version builds a parent mesh + child meshes, rather than\n // one big mesh with submeshes and a multimaterial.\n // This should be obsolete, unless the first one has problems..\n this.buildWithoutMultimats = function (chunk, meshdata, ignoreMaterials) {\n noa = chunk.noa\n\n // preprocess meshdata entries to merge those that use default terrain material\n var mergeCriteria = function (mdat) {\n if (ignoreMaterials) return true\n if (mdat.renderMat) return false\n var url = noa.registry.getMaterialTexture(mdat.id)\n var alpha = noa.registry.getMaterialData(mdat.id).alpha\n if (url || alpha < 1) return false\n }\n mergeSubmeshes(meshdata, mergeCriteria)\n\n // go through (remaining) meshdata entries and create a mesh for each\n // call the first one the parent, and attach others to it\n var parent = null\n var keylist = Object.keys(meshdata)\n for (var i = 0; i < keylist.length; ++i) {\n var mdat = meshdata[keylist[i]]\n var matID = mdat.id\n var mat = getTerrainMaterial(matID, ignoreMaterials)\n var name = 'chunk_inner_' + chunk.id + ' ' + matID\n var mesh = buildMeshFromSubmesh(mdat, name, [mat])\n\n if (!parent) {\n parent = mesh\n // position the parent globally\n var x = chunk.i * chunk.size\n var y = chunk.j * chunk.size\n var z = chunk.k * chunk.size\n parent.position.x = x\n parent.position.y = y\n parent.position.z = z\n } else {\n mesh.parent = parent\n }\n\n mesh.freezeWorldMatrix()\n mesh.freezeNormals()\n }\n\n return parent\n }\n\n\n\n // given a set of submesh objects, merge all those that \n // meet some criteria into the first such submesh\n // modifies meshDataList in place!\n function mergeSubmeshes(meshDataList, criteria) {\n var vertices = []\n var indices = []\n var matIDs = []\n\n var keylist = Object.keys(meshDataList)\n var target = null\n var targetID\n for (var i = 0; i < keylist.length; ++i) {\n var mdat = meshDataList[keylist[i]]\n if (!criteria(mdat)) continue\n\n vertices.push(mdat.positions.length)\n indices.push(mdat.indices.length)\n matIDs.push(mdat.id)\n\n if (!target) {\n target = mdat\n targetID = keylist[i]\n\n } else {\n var indexOffset = target.positions.length / 3\n // merge data in \"mdat\" onto \"target\"\n target.positions = target.positions.concat(mdat.positions)\n target.normals = target.normals.concat(mdat.normals)\n target.colors = target.colors.concat(mdat.colors)\n target.uvs = target.uvs.concat(mdat.uvs)\n // indices must be offset relative to data being merged onto\n for (var j = 0, len = mdat.indices.length; j < len; ++j) {\n target.indices.push(mdat.indices[j] + indexOffset)\n }\n // get rid of entry that's been merged\n mdat.dispose()\n delete meshDataList[keylist[i]]\n }\n }\n\n return {\n mergedID: targetID,\n vertices: vertices,\n indices: indices,\n matIDs: matIDs,\n }\n }\n\n\n\n function buildMeshFromSubmesh(submesh, name, mats, verts, inds) {\n\n // base mesh and vertexData object\n var scene = noa.rendering.getScene()\n var mesh = new BABYLON.Mesh(name, scene)\n var vdat = new BABYLON.VertexData()\n vdat.positions = submesh.positions\n vdat.indices = submesh.indices\n vdat.normals = submesh.normals\n vdat.colors = submesh.colors\n vdat.uvs = submesh.uvs\n vdat.applyToMesh(mesh)\n submesh.dispose()\n\n if (mats.length === 1) {\n // if only one material ID, assign as a regular mesh and return\n mesh.material = mats[0]\n\n } else {\n // else we need to make a multimaterial and define (babylon) submeshes\n var multiMat = new BABYLON.MultiMaterial('multimat ' + name, scene)\n mesh.subMeshes = []\n // var totalVerts = vdat.positions.length\n // var totalInds = vdat.indices.length\n var vertStart = 0\n var indStart = 0\n for (var i = 0; i < mats.length; i++) {\n multiMat.subMaterials[i] = mats[i]\n var sub = new BABYLON.SubMesh(i, vertStart, verts[i], indStart, inds[i], mesh)\n mesh.subMeshes[i] = sub\n vertStart += verts[i]\n indStart += inds[i]\n }\n mesh.material = multiMat\n }\n\n return mesh\n }\n\n\n\n\n // Material wrangling\n\n\n var materialCache = {}\n\n // manage materials/textures to avoid duplicating them\n function getTerrainMaterial(matID, ignore) {\n if (ignore) return noa.rendering.flatMaterial\n var name = 'terrain mat ' + matID\n if (!materialCache[name]) materialCache[name] = makeTerrainMaterial(matID)\n return materialCache[name]\n }\n\n\n // canonical function to make a terrain material\n function makeTerrainMaterial(id) {\n // if user-specified render material is defined, use it\n var matData = noa.registry.getMaterialData(id)\n if (matData.renderMat) return matData.renderMat\n // otherwise determine which built-in material to use\n var url = noa.registry.getMaterialTexture(id)\n var alpha = matData.alpha\n if (!url && alpha == 1) {\n // base material is fine for non-textured case, if no alpha\n return noa.rendering.flatMaterial\n }\n var mat = noa.rendering.flatMaterial.clone('terrain' + id)\n if (url) {\n var scene = noa.rendering.getScene()\n var tex = new BABYLON.Texture(url, scene, true, false, BABYLON.Texture.NEAREST_SAMPLINGMODE)\n if (matData.textureAlpha) tex.hasAlpha = true\n mat.diffuseTexture = tex\n }\n if (matData.alpha < 1) {\n mat.alpha = matData.alpha\n }\n return mat\n }\n}\n\n\n\n\n\n\n\n\n/*\n * Greedy voxel meshing algorithm\n * based initially on algo by Mikola Lysenko:\n * http://0fps.net/2012/07/07/meshing-minecraft-part-2/\n * but evolved quite a bit since then\n * AO handling by me, stitched together out of cobwebs and dreams\n * \n * Arguments:\n * arr: 3D ndarray of dimensions X,Y,Z\n * packed with solidity/opacity booleans in higher bits\n * getMaterial: function( blockID, dir )\n * returns a material ID based on block id and which cube face it is\n * (assume for now that each mat ID should get its own mesh)\n * getColor: function( materialID )\n * looks up a color (3-array) by material ID\n * TODO: replace this with a lookup array?\n * doAO: whether or not to bake ambient occlusion into vertex colors\n * aoValues: array[3] of color multipliers for AO (least to most occluded)\n * revAoVal: \"reverse ao\" - color multiplier for unoccluded exposed edges\n *\n * Return object: array of mesh objects keyed by material ID\n * arr[id] = {\n * id: material id for mesh\n * vertices: ints, range 0 .. X/Y/Z\n * indices: ints\n * normals: ints, -1 .. 1\n * colors: floats, 0 .. 1\n * uvs: floats, 0 .. X/Y/Z\n * }\n*/\n\nfunction GreedyMesher() {\n\n // data representation constants\n var constants = __webpack_require__(/*! ./constants */ \"../../src/lib/constants.js\")\n\n var ID_MASK = constants.ID_MASK\n var VAR_MASK = constants.VAR_MASK\n var SOLID_BIT = constants.SOLID_BIT\n var OPAQUE_BIT = constants.OPAQUE_BIT\n var OBJECT_BIT = constants.OBJECT_BIT\n\n\n var maskCache = new Int16Array(16)\n var aomaskCache = new Uint16Array(16)\n\n\n\n\n this.mesh = function (arr, getMaterial, getColor, doAO, aoValues, revAoVal) {\n\n // return object, holder for Submeshes\n var subMeshes = {}\n\n // precalc how to apply AO packing in first masking function\n var skipReverseAO = (doAO && (revAoVal === aoValues[0]))\n var aoPackFcn\n if (doAO) aoPackFcn = (skipReverseAO) ? packAOMaskNoReverse : packAOMask\n\n\n //Sweep over each axis, mapping axes to [d,u,v]\n for (var d = 0; d < 3; ++d) {\n var u = (d + 1) % 3\n var v = (d + 2) % 3\n\n // make transposed ndarray so index i is the axis we're sweeping\n var arrT = arr.transpose(d, u, v).lo(1, 1, 1).hi(arr.shape[d] - 2, arr.shape[u] - 2, arr.shape[v] - 2)\n\n // shorten len0 by 1 so faces at edges don't get drawn in both chunks\n var len0 = arrT.shape[0] - 1\n var len1 = arrT.shape[1]\n var len2 = arrT.shape[2]\n\n // create bigger mask arrays as needed\n if (maskCache.length < len1 * len2) {\n maskCache = new Int16Array(len1 * len2)\n aomaskCache = new Uint16Array(len1 * len2)\n }\n\n // iterate along current major axis..\n for (var i = 0; i <= len0; ++i) {\n\n // fills mask and aomask arrays with values\n constructMeshMasks(i, d, arrT, getMaterial, aoPackFcn)\n profile_hook('built masks')\n\n // parses the masks to do greedy meshing\n constructMeshDataFromMasks(i, d, u, v, len1, len2,\n doAO, subMeshes, getColor, aoValues, revAoVal)\n\n profile_hook('build submeshes')\n }\n }\n\n // done, return array of submeshes\n return subMeshes\n }\n\n\n\n\n\n\n\n // Greedy meshing inner loop one\n //\n // iterating across ith 2d plane, with n being index into masks\n\n function constructMeshMasks(i, d, arrT, getMaterial, aoPackFcn) {\n var len = arrT.shape[1]\n var mask = maskCache\n var aomask = aomaskCache\n // set up for quick array traversals\n var n = 0\n var data = arrT.data\n var dbase = arrT.index(i - 1, 0, 0)\n var istride = arrT.stride[0]\n var jstride = arrT.stride[1]\n var kstride = arrT.stride[2]\n\n for (var k = 0; k < len; ++k) {\n var d0 = dbase\n dbase += kstride\n for (var j = 0; j < len; j++ , n++ , d0 += jstride) {\n\n // mask[n] will represent the face needed between i-1,j,k and i,j,k\n // for now, assume we never have two faces in both directions\n\n // IDs at i-1,j,k and i,j,k\n var id0 = data[d0]\n var id1 = data[d0 + istride]\n\n var faceDir = getFaceDir(id0, id1)\n if (faceDir) {\n // set regular mask value to material ID, sign indicating direction\n mask[n] = (faceDir > 0) ?\n getMaterial(id0 & ID_MASK, d * 2) :\n -getMaterial(id1 & ID_MASK, d * 2 + 1)\n\n // if doing AO, precalculate AO level for each face into second mask\n if (aoPackFcn) {\n // i values in direction face is/isn't pointing\n var ipos = (faceDir > 0) ? i : i - 1\n var ineg = (faceDir > 0) ? i - 1 : i\n\n // this got so big I rolled it into a function\n aomask[n] = aoPackFcn(arrT, ipos, ineg, j, k)\n }\n } else {\n // unneeded, mesher zeroes out mask as it goes\n // mask[n] = 0\n }\n\n }\n }\n }\n\n\n\n function getFaceDir(id0, id1) {\n // no face if both blocks are opaque, or if ids match\n if (id0 === id1) return 0\n var op0 = id0 & OPAQUE_BIT\n var op1 = id1 & OPAQUE_BIT\n if (op0 && op1) return 0\n // if either block is opaque draw a face for it\n if (op0) return 1\n if (op1) return -1\n // if one block is air or an object block draw face for the other\n if (id1 === 0 || (id1 & OBJECT_BIT)) return 1\n if (id0 === 0 || (id0 & OBJECT_BIT)) return -1\n // only remaining case is two different non-opaque non-air blocks that are adjacent\n // really we should draw both faces here; draw neither for now\n return 0\n }\n\n\n\n\n\n\n\n // Greedy meshing inner loop two\n //\n // construct data for mesh using the masks\n\n function constructMeshDataFromMasks(i, d, u, v, len1, len2,\n doAO, submeshes, getColor, aoValues, revAoVal) {\n var n = 0\n var mask = maskCache\n var aomask = aomaskCache\n\n // some logic is broken into helper functions for AO and non-AO\n // this fixes deopts in Chrome (for reasons unknown)\n var maskCompareFcn = (doAO) ? maskCompare : maskCompare_noAO\n var meshColorFcn = (doAO) ? pushMeshColors : pushMeshColors_noAO\n\n for (var k = 0; k < len2; ++k) {\n var w = 1\n var h = 1\n for (var j = 0; j < len1; j += w, n += w) {\n\n var maskVal = mask[n] | 0\n if (!maskVal) {\n w = 1\n continue\n }\n var ao = aomask[n] | 0\n\n // Compute width and height of area with same mask/aomask values\n for (w = 1; w < len1 - j; ++w) {\n if (!maskCompareFcn(n + w, mask, maskVal, aomask, ao)) break\n }\n\n OUTER:\n for (h = 1; h < len2 - k; ++h) {\n for (var m = 0; m < w; ++m) {\n var ix = n + m + h * len1\n if (!maskCompareFcn(ix, mask, maskVal, aomask, ao)) break OUTER\n }\n }\n\n // for testing: doing the following will disable greediness\n //w=h=1\n\n // material and mesh for this face\n var matID = Math.abs(maskVal)\n if (!submeshes[matID]) submeshes[matID] = new Submesh(matID)\n var mesh = submeshes[matID]\n var colors = mesh.colors\n var c = getColor(matID)\n\n // colors are pushed in helper function - avoids deopts\n // tridir is boolean for which way to split the quad into triangles\n\n var triDir = meshColorFcn(colors, c, ao, aoValues, revAoVal)\n\n\n //Add quad, vertices = x -> x+du -> x+du+dv -> x+dv\n var x = [0, 0, 0]\n x[d] = i\n x[u] = j\n x[v] = k\n var du = [0, 0, 0]; du[u] = w;\n var dv = [0, 0, 0]; dv[v] = h;\n\n var pos = mesh.positions\n pos.push(\n x[0], x[1], x[2],\n x[0] + du[0], x[1] + du[1], x[2] + du[2],\n x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2],\n x[0] + dv[0], x[1] + dv[1], x[2] + dv[2])\n\n\n // add uv values, with the order and sign depending on \n // axis and direction so as to avoid mirror-image textures\n var dir = (maskVal > 0) ? 1 : -1\n\n if (d === 2) {\n mesh.uvs.push(\n 0, h,\n -dir * w, h,\n -dir * w, 0,\n 0, 0)\n } else {\n mesh.uvs.push(\n 0, w,\n 0, 0,\n dir * h, 0,\n dir * h, w)\n }\n\n\n // Add indexes, ordered clockwise for the facing direction;\n\n var vs = pos.length / 3 - 4\n\n if (maskVal < 0) {\n if (triDir) {\n mesh.indices.push(vs, vs + 1, vs + 2, vs, vs + 2, vs + 3)\n } else {\n mesh.indices.push(vs + 1, vs + 2, vs + 3, vs, vs + 1, vs + 3)\n }\n } else {\n if (triDir) {\n mesh.indices.push(vs, vs + 2, vs + 1, vs, vs + 3, vs + 2)\n } else {\n mesh.indices.push(vs + 3, vs + 1, vs, vs + 3, vs + 2, vs + 1)\n }\n }\n\n\n // norms depend on which direction the mask was solid in..\n var norm0 = d === 0 ? dir : 0\n var norm1 = d === 1 ? dir : 0\n var norm2 = d === 2 ? dir : 0\n\n // same norm for all vertices\n mesh.normals.push(\n norm0, norm1, norm2,\n norm0, norm1, norm2,\n norm0, norm1, norm2,\n norm0, norm1, norm2)\n\n\n //Zero-out mask\n for (var hx = 0; hx < h; ++hx) {\n for (var wx = 0; wx < w; ++wx) {\n mask[n + wx + hx * len1] = 0\n }\n }\n\n }\n }\n }\n\n\n\n // Two helper functions with AO and non-AO implementations:\n\n function maskCompare(index, mask, maskVal, aomask, aoVal) {\n if (maskVal !== mask[index]) return false\n if (aoVal !== aomask[index]) return false\n return true\n }\n\n function maskCompare_noAO(index, mask, maskVal, aomask, aoVal) {\n if (maskVal !== mask[index]) return false\n return true\n }\n\n function pushMeshColors_noAO(colors, c, ao, aoValues, revAoVal) {\n colors.push(c[0], c[1], c[2], 1)\n colors.push(c[0], c[1], c[2], 1)\n colors.push(c[0], c[1], c[2], 1)\n colors.push(c[0], c[1], c[2], 1)\n return true // triangle direction doesn't matter for non-AO\n }\n\n function pushMeshColors(colors, c, ao, aoValues, revAoVal) {\n var ao00 = unpackAOMask(ao, 0, 0)\n var ao10 = unpackAOMask(ao, 1, 0)\n var ao11 = unpackAOMask(ao, 1, 1)\n var ao01 = unpackAOMask(ao, 0, 1)\n pushAOColor(colors, c, ao00, aoValues, revAoVal)\n pushAOColor(colors, c, ao10, aoValues, revAoVal)\n pushAOColor(colors, c, ao11, aoValues, revAoVal)\n pushAOColor(colors, c, ao01, aoValues, revAoVal)\n\n // this bit is pretty magical..\n var triDir = true\n if (ao00 === ao11) {\n triDir = (ao01 === ao10) ? (ao01 == 2) : true\n } else {\n triDir = (ao01 === ao10) ? false : (ao00 + ao11 > ao01 + ao10)\n }\n return triDir\n }\n\n\n\n\n\n /* \n * packAOMask:\n *\n * For a given face, find occlusion levels for each vertex, then\n * pack 4 such (2-bit) values into one Uint8 value\n * \n * Occlusion levels:\n * 1 is flat ground, 2 is partial occlusion, 3 is max (corners)\n * 0 is \"reverse occlusion\" - an unoccluded exposed edge \n * Packing order var(bit offset):\n * a01(2) - a11(6) ^ K\n * - - +> J\n * a00(0) - a10(4)\n */\n\n // when skipping reverse AO, uses this simpler version of the function:\n\n function packAOMaskNoReverse(data, ipos, ineg, j, k) {\n var a00 = 1\n var a01 = 1\n var a10 = 1\n var a11 = 1\n var solidBit = SOLID_BIT\n\n // facing into a solid (non-opaque) block?\n var facingSolid = (solidBit & data.get(ipos, j, k))\n\n // inc occlusion of vertex next to obstructed side\n if (data.get(ipos, j + 1, k) & solidBit) { ++a10; ++a11 }\n if (data.get(ipos, j - 1, k) & solidBit) { ++a00; ++a01 }\n if (data.get(ipos, j, k + 1) & solidBit) { ++a01; ++a11 }\n if (data.get(ipos, j, k - 1) & solidBit) { ++a00; ++a10 }\n\n // treat corners differently based when facing a solid block\n if (facingSolid) {\n // always 2, or 3 in corners\n a11 = (a11 == 3 || data.get(ipos, j + 1, k + 1) & solidBit) ? 3 : 2\n a01 = (a01 == 3 || data.get(ipos, j - 1, k + 1) & solidBit) ? 3 : 2\n a10 = (a10 == 3 || data.get(ipos, j + 1, k - 1) & solidBit) ? 3 : 2\n a00 = (a00 == 3 || data.get(ipos, j - 1, k - 1) & solidBit) ? 3 : 2\n } else {\n // treat corner as occlusion 3 only if not occluded already\n if (a11 === 1 && (data.get(ipos, j + 1, k + 1) & solidBit)) { a11 = 2 }\n if (a01 === 1 && (data.get(ipos, j - 1, k + 1) & solidBit)) { a01 = 2 }\n if (a10 === 1 && (data.get(ipos, j + 1, k - 1) & solidBit)) { a10 = 2 }\n if (a00 === 1 && (data.get(ipos, j - 1, k - 1) & solidBit)) { a00 = 2 }\n }\n\n return a11 << 6 | a10 << 4 | a01 << 2 | a00\n }\n\n // more complicated AO packing when doing reverse AO on corners\n\n function packAOMask(data, ipos, ineg, j, k) {\n var a00 = 1\n var a01 = 1\n var a10 = 1\n var a11 = 1\n var solidBit = SOLID_BIT\n\n // facing into a solid (non-opaque) block?\n var facingSolid = (solidBit & data.get(ipos, j, k))\n\n // inc occlusion of vertex next to obstructed side\n if (data.get(ipos, j + 1, k) & solidBit) { ++a10; ++a11 }\n if (data.get(ipos, j - 1, k) & solidBit) { ++a00; ++a01 }\n if (data.get(ipos, j, k + 1) & solidBit) { ++a01; ++a11 }\n if (data.get(ipos, j, k - 1) & solidBit) { ++a00; ++a10 }\n\n if (facingSolid) {\n // always 2, or 3 in corners\n a11 = (a11 == 3 || data.get(ipos, j + 1, k + 1) & solidBit) ? 3 : 2\n a01 = (a01 == 3 || data.get(ipos, j - 1, k + 1) & solidBit) ? 3 : 2\n a10 = (a10 == 3 || data.get(ipos, j + 1, k - 1) & solidBit) ? 3 : 2\n a00 = (a00 == 3 || data.get(ipos, j - 1, k - 1) & solidBit) ? 3 : 2\n } else {\n\n // check each corner, and if not present do reverse AO\n if (a11 === 1) {\n if (data.get(ipos, j + 1, k + 1) & solidBit) { a11 = 2 }\n else if (!(data.get(ineg, j, k + 1) & solidBit) ||\n !(data.get(ineg, j + 1, k) & solidBit) ||\n !(data.get(ineg, j + 1, k + 1) & solidBit)) {\n a11 = 0\n }\n }\n\n if (a10 === 1) {\n if (data.get(ipos, j + 1, k - 1) & solidBit) { a10 = 2 }\n else if (!(data.get(ineg, j, k - 1) & solidBit) ||\n !(data.get(ineg, j + 1, k) & solidBit) ||\n !(data.get(ineg, j + 1, k - 1) & solidBit)) {\n a10 = 0\n }\n }\n\n if (a01 === 1) {\n if (data.get(ipos, j - 1, k + 1) & solidBit) { a01 = 2 }\n else if (!(data.get(ineg, j, k + 1) & solidBit) ||\n !(data.get(ineg, j - 1, k) & solidBit) ||\n !(data.get(ineg, j - 1, k + 1) & solidBit)) {\n a01 = 0\n }\n }\n\n if (a00 === 1) {\n if (data.get(ipos, j - 1, k - 1) & solidBit) { a00 = 2 }\n else if (!(data.get(ineg, j, k - 1) & solidBit) ||\n !(data.get(ineg, j - 1, k) & solidBit) ||\n !(data.get(ineg, j - 1, k - 1) & solidBit)) {\n a00 = 0\n }\n }\n }\n\n return a11 << 6 | a10 << 4 | a01 << 2 | a00\n }\n\n\n\n // unpack (2 bit) ao value from ao mask\n // see above for details\n function unpackAOMask(aomask, jpos, kpos) {\n var offset = jpos ? (kpos ? 6 : 4) : (kpos ? 2 : 0)\n return aomask >> offset & 3\n }\n\n\n // premultiply vertex colors by value depending on AO level\n // then push them into color array\n function pushAOColor(colors, baseCol, ao, aoVals, revAoVal) {\n var mult = (ao === 0) ? revAoVal : aoVals[ao - 1]\n colors.push(baseCol[0] * mult, baseCol[1] * mult, baseCol[2] * mult, 1)\n }\n\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar profile_hook = (function () {\n if (!PROFILE) return function () { }\n var every = 50\n var timer = new (__webpack_require__(/*! ./util */ \"../../src/lib/util.js\").Timer)(every, 'Terrain meshing')\n return function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/terrainMesher.js?"); - -/***/ }), - -/***/ "../../src/lib/util.js": -/*!************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/util.js ***! - \************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n\nmodule.exports = {\n Timer: Timer,\n removeUnorderedListItem: removeUnorderedListItem,\n}\n\n\n\n\n// helper to swap item to end and pop(), instead of splice()ing\nfunction removeUnorderedListItem(list, item) {\n var i = list.indexOf(item)\n if (i < 0) { return }\n if (i === list.length - 1) {\n list.pop()\n } else {\n list[i] = list.pop()\n }\n}\n\n\n\n\n// simple thing for reporting time split up between several activities\nfunction Timer(_every, _title) {\n var title = _title || ''\n var every = _every || 1\n var times = []\n var names = []\n var started = 0\n var last = 0\n var iter = 0\n var total = 0\n var clearNext = true\n\n this.start = function () {\n if (clearNext) {\n times.length = names.length = 0\n clearNext = false\n }\n started = last = performance.now()\n iter++\n }\n this.add = function (name) {\n var t = performance.now()\n if (names.indexOf(name) < 0) names.push(name)\n var i = names.indexOf(name)\n if (!times[i]) times[i] = 0\n times[i] += t - last\n last = t\n }\n this.report = function () {\n total += performance.now() - started\n if (iter === every) {\n var head = title + ' total ' + (total / every).toFixed(2) + 'ms (avg, ' + every + ' runs) '\n console.log(head, names.map(function (name, i) {\n return name + ': ' + (times[i] / every).toFixed(2) + 'ms '\n }).join(''))\n clearNext = true\n iter = 0\n total = 0\n }\n }\n}\n\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/util.js?"); - -/***/ }), - -/***/ "../../src/lib/world.js": -/*!*************************************************!*\ - !*** /Users/andy/dev/game/noa/src/lib/world.js ***! - \*************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nvar ndHash = __webpack_require__(/*! ndarray-hash */ \"../../node_modules/ndarray-hash/ndhash.js\")\nvar EventEmitter = __webpack_require__(/*! events */ \"../../node_modules/events/events.js\").EventEmitter\nvar Chunk = __webpack_require__(/*! ./chunk */ \"../../src/lib/chunk.js\")\n\n\nmodule.exports = function (noa, opts) {\n return new World(noa, opts)\n}\n\n\nvar PROFILE = 0\nvar PROFILE_QUEUES = 0\n\n\nvar defaultOptions = {\n chunkSize: 24,\n chunkAddDistance: 3,\n chunkRemoveDistance: 4\n}\n\n/**\n * Module for managing the world, and its chunks\n * @class noa.world\n * \n * Emits:\n * * worldDataNeeded (id, ndarray, x, y, z)\n * * chunkAdded (chunk)\n * * chunkChanged (chunk)\n * * chunkBeingRemoved (id, ndarray, userData)\n */\n\nfunction World(noa, opts) {\n this.noa = noa\n opts = Object.assign({}, defaultOptions, opts)\n\n this.userData = null\n this.playerChunkLoaded = false\n this.Chunk = Chunk\n\n this.chunkSize = opts.chunkSize\n this.chunkAddDistance = opts.chunkAddDistance\n this.chunkRemoveDistance = opts.chunkRemoveDistance\n if (this.chunkRemoveDistance < this.chunkAddDistance) {\n this.chunkRemoveDistance = this.chunkAddDistance\n }\n\n // internals\n this._chunkIDsToAdd = []\n this._chunkIDsToRemove = []\n this._chunkIDsInMemory = []\n this._chunkIDsToCreate = []\n this._chunkIDsToMesh = []\n this._chunkIDsToMeshFirst = []\n this._maxChunksPendingCreation = 20\n this._maxChunksPendingMeshing = 20\n this._maxProcessingPerTick = 9 // ms\n this._maxProcessingPerRender = 5 // ms\n\n // triggers a short visit to the meshing queue before renders\n var self = this\n noa.on('beforeRender', function () { beforeRender(self) })\n\n // actual chunk storage - hash size hard coded for now\n this._chunkHash = ndHash([1024, 1024, 1024])\n\n // instantiate coord conversion functions based on the chunk size\n // use bit twiddling if chunk size is a power of 2\n var cs = this.chunkSize\n if (cs & cs - 1 === 0) {\n var shift = Math.log2(cs) | 0\n var mask = (cs - 1) | 0\n worldCoordToChunkCoord = coord => (coord >> shift) | 0\n worldCoordToChunkIndex = coord => (coord & mask) | 0\n } else {\n worldCoordToChunkCoord = coord => Math.floor(coord / cs) | 0\n worldCoordToChunkIndex = coord => (((coord % cs) + cs) % cs) | 0\n }\n\n}\nWorld.prototype = Object.create(EventEmitter.prototype)\n\nvar worldCoordToChunkCoord\nvar worldCoordToChunkIndex\n\n\n\n\n/*\n * PUBLIC API \n*/\n\n\n\n/** @param x,y,z */\nWorld.prototype.getBlockID = function (x, y, z) {\n var chunk = this._getChunkByCoords(x, y, z)\n if (!chunk) return 0\n\n var ix = worldCoordToChunkIndex(x)\n var iy = worldCoordToChunkIndex(y)\n var iz = worldCoordToChunkIndex(z)\n return chunk.get(ix, iy, iz)\n}\n\n/** @param x,y,z */\nWorld.prototype.getBlockSolidity = function (x, y, z) {\n var chunk = this._getChunkByCoords(x, y, z)\n if (!chunk) return 0\n\n var ix = worldCoordToChunkIndex(x)\n var iy = worldCoordToChunkIndex(y)\n var iz = worldCoordToChunkIndex(z)\n return !!chunk.getSolidityAt(ix, iy, iz)\n}\n\n/** @param x,y,z */\nWorld.prototype.getBlockOpacity = function (x, y, z) {\n var id = this.getBlockID(x, y, z)\n return this.noa.registry.getBlockOpacity(id)\n}\n\n/** @param x,y,z */\nWorld.prototype.getBlockFluidity = function (x, y, z) {\n var id = this.getBlockID(x, y, z)\n return this.noa.registry.getBlockFluidity(id)\n}\n\n/** @param x,y,z */\nWorld.prototype.getBlockProperties = function (x, y, z) {\n var id = this.getBlockID(x, y, z)\n return this.noa.registry.getBlockProps(id)\n}\n\n/** @param x,y,z */\nWorld.prototype.getBlockObjectMesh = function (x, y, z) {\n var chunk = this._getChunkByCoords(x, y, z)\n if (!chunk) return 0\n\n var ix = worldCoordToChunkIndex(x)\n var iy = worldCoordToChunkIndex(y)\n var iz = worldCoordToChunkIndex(z)\n return chunk.getObjectMeshAt(ix, iy, iz)\n}\n\n\n/** @param x,y,z */\nWorld.prototype.setBlockID = function (val, x, y, z) {\n var i = worldCoordToChunkCoord(x)\n var j = worldCoordToChunkCoord(y)\n var k = worldCoordToChunkCoord(z)\n var ix = worldCoordToChunkIndex(x)\n var iy = worldCoordToChunkIndex(y)\n var iz = worldCoordToChunkIndex(z)\n\n // if update is on chunk border, update neighbor's padding data too\n _updateChunkAndBorders(this, i, j, k, this.chunkSize, ix, iy, iz, val)\n}\n\n\n/** @param x,y,z */\nWorld.prototype.isBoxUnobstructed = function (box) {\n var base = box.base\n var max = box.max\n for (var i = Math.floor(base[0]); i < max[0] + 1; i++) {\n for (var j = Math.floor(base[1]); j < max[1] + 1; j++) {\n for (var k = Math.floor(base[2]); k < max[2] + 1; k++) {\n if (this.getBlockSolidity(i, j, k)) return false\n }\n }\n }\n return true\n}\n\n\n\n\n\nWorld.prototype.tick = function () {\n profile_hook('start')\n\n // check player position and needed/unneeded chunks\n var pos = getPlayerChunkCoords(this)\n var chunkID = getChunkID(pos[0], pos[1], pos[2])\n if (chunkID != this._lastPlayerChunkID) {\n this.emit('playerEnteredChunk', pos[0], pos[1], pos[2])\n buildChunkAddQueue(this, pos[0], pos[1], pos[2])\n buildChunkRemoveQueue(this, pos[0], pos[1], pos[2])\n }\n this._lastPlayerChunkID = chunkID\n profile_hook('build queues')\n\n // process (create or mesh) some chunks. If fast enough, do several\n profile_queues(this, 'start')\n var cutoff = performance.now() + this._maxProcessingPerTick\n var done = false\n while (!done && (performance.now() < cutoff)) {\n var d1 = processMeshingQueues(this, false)\n var d2 = processChunkQueues(this)\n if (!d2) d2 = processChunkQueues(this)\n done = d1 && d2\n }\n profile_queues(this, 'end')\n\n\n // track whether the player's local chunk is loaded and ready or not\n var pChunk = getChunk(this, pos[0], pos[1], pos[2])\n var okay = !!(pChunk && pChunk.isGenerated && !pChunk.isInvalid)\n this.playerChunkLoaded = okay\n\n profile_hook('end')\n}\n\n\n\nfunction beforeRender(self) {\n // on render, quickly process the high-priority meshing queue\n // to help avoid flashes of background while neighboring chunks update\n var cutoff = performance.now() + self._maxProcessingPerRender\n var done = false\n while (!done && (performance.now() < cutoff)) {\n done = processMeshingQueues(self, true)\n }\n}\n\n\n\n\n/** client should call this after creating a chunk's worth of data (as an ndarray) \n * If userData is passed in it will be attached to the chunk\n * @param id\n * @param array\n * @param userData\n */\nWorld.prototype.setChunkData = function (id, array, userData) {\n profile_queues(this, 'received')\n var arr = parseChunkID(id)\n var chunk = getChunk(this, arr[0], arr[1], arr[2])\n // ignore if chunk was invalidated while being prepared\n if (!chunk || chunk.isInvalid) return\n chunk.array = array\n if (userData) chunk.userData = userData\n chunk.initData()\n enqueueID(id, this._chunkIDsInMemory)\n unenqueueID(id, this._chunkIDsToCreate)\n\n // chunk can now be meshed...\n this.noa.rendering.prepareChunkForRendering(chunk)\n enqueueID(id, this._chunkIDsToMesh)\n this.emit('chunkAdded', chunk)\n}\n\n\n\n\n/*\n * Calling this causes all world chunks to get unloaded and recreated \n * (after receiving new world data from the client). This is useful when\n * you're teleporting the player to a new world, e.g.\n*/\nWorld.prototype.invalidateAllChunks = function () {\n var toInval = this._chunkIDsInMemory.concat(this._chunkIDsToCreate)\n for (var id of toInval) {\n var loc = parseChunkID(id)\n var chunk = getChunk(this, loc[0], loc[1], loc[2])\n chunk.isInvalid = true\n }\n // this causes chunk queues to get rebuilt next tick\n this._lastPlayerChunkID = ''\n}\n\n\n\n// debugging\nWorld.prototype.report = function () {\n console.log('World report - playerChunkLoaded: ', this.playerChunkLoaded)\n _report(this, ' to add ', this._chunkIDsToAdd)\n _report(this, ' to remove: ', this._chunkIDsToRemove)\n _report(this, ' in memory: ', this._chunkIDsInMemory, true)\n _report(this, ' creating: ', this._chunkIDsToCreate)\n _report(this, ' meshing: ', this._chunkIDsToMesh.concat(this._chunkIDsToMeshFirst))\n}\nfunction _report(world, name, arr, ext) {\n var ct = 0, full = 0, empty = 0\n for (var id of arr) {\n if (id.size) {\n if (id.isInvalid) ct++\n continue\n }\n var loc = parseChunkID(id)\n var chunk = getChunk(world, loc[0], loc[1], loc[2])\n if (chunk.isInvalid) ct++\n if (chunk.isFull) full++\n if (chunk.isEmpty) empty++\n }\n var len = (arr.length + ' ').substr(0, 6)\n var es = (ext) ? [', ', full, ' full, ', empty, ' empty'].join('') : ''\n console.log(name, len, ct, 'invalid' + es)\n}\n\n\n\n\n/*\n * INTERNALS\n*/\n\n\n// canonical string ID handling for the i,j,k-th chunk\nfunction getChunkID(i, j, k) {\n return i + '|' + j + '|' + k\n}\nfunction parseChunkID(id) {\n var arr = id.split('|')\n return [parseInt(arr[0]), parseInt(arr[1]), parseInt(arr[2])]\n}\n\n// canonical functions to store/retrieve a chunk held in memory\nfunction getChunk(world, i, j, k) {\n var mi = (i | 0) & 1023\n var mj = (j | 0) & 1023\n var mk = (k | 0) & 1023\n return world._chunkHash.get(mi, mj, mk)\n}\n\nfunction setChunk(world, i, j, k, value) {\n var mi = (i | 0) & 1023\n var mj = (j | 0) & 1023\n var mk = (k | 0) & 1023\n world._chunkHash.set(mi, mj, mk, value)\n}\n\n\n\nfunction getPlayerChunkCoords(world) {\n var pos = world.noa.getPlayerPosition()\n var i = worldCoordToChunkCoord(pos[0])\n var j = worldCoordToChunkCoord(pos[1])\n var k = worldCoordToChunkCoord(pos[2])\n return [i, j, k]\n}\n\n\n// for internal use\nWorld.prototype._getChunkByCoords = function (x, y, z) {\n var i = worldCoordToChunkCoord(x)\n var j = worldCoordToChunkCoord(y)\n var k = worldCoordToChunkCoord(z)\n return getChunk(this, i, j, k)\n}\n\n\n\n\n// run through chunk tracking queues looking for work to do next\nfunction processChunkQueues(self) {\n var done = true\n // both queues are sorted by ascending distance\n if (self._chunkIDsToRemove.length) {\n var remove = parseChunkID(self._chunkIDsToRemove.pop())\n removeChunk(self, remove[0], remove[1], remove[2])\n profile_queues(self, 'removed')\n profile_hook('removed')\n done = false\n }\n if (self._chunkIDsToCreate.length >= self._maxChunksPendingCreation) return done\n // if (self._chunkIDsToMesh.length >= self._maxChunksPendingMeshing) return done\n if (self._chunkIDsToAdd.length) {\n var id = self._chunkIDsToAdd.shift()\n requestNewChunk(self, id)\n profile_hook('requested')\n profile_queues(self, 'requested')\n done = false\n }\n return done\n}\n\n\n// similar to above but for chunks waiting to be meshed\nfunction processMeshingQueues(self, firstOnly) {\n var id\n if (self._chunkIDsToMeshFirst.length) {\n id = self._chunkIDsToMeshFirst.pop()\n } else if (firstOnly) {\n return true\n } else if (self._chunkIDsToMesh.length) {\n id = self._chunkIDsToMesh.pop()\n } else return true\n\n var arr = parseChunkID(id)\n var chunk = getChunk(self, arr[0], arr[1], arr[2])\n if (chunk.isInvalid) return\n if (!chunk.isGenerated) {\n // client code triggered a remesh too early, requeue it\n self._chunkIDsToMesh.unshift(id)\n return\n }\n chunk.updateMeshes()\n\n profile_queues(self, 'meshed')\n profile_hook('meshed')\n return false\n}\n\n\n\n\n\n\n\n\n\n\n// make a new chunk and emit an event for it to be populated with world data\nfunction requestNewChunk(world, id) {\n var pos = parseChunkID(id)\n var i = pos[0]\n var j = pos[1]\n var k = pos[2]\n var size = world.chunkSize\n var chunk = new Chunk(world.noa, id, i, j, k, size)\n setChunk(world, i, j, k, chunk)\n var x = i * size - 1\n var y = j * size - 1\n var z = k * size - 1\n enqueueID(id, world._chunkIDsToCreate)\n world.emit('worldDataNeeded', id, chunk.array, x, y, z)\n}\n\n\n\n\n// remove a chunk that wound up in the remove queue\nfunction removeChunk(world, i, j, k) {\n var chunk = getChunk(world, i, j, k)\n world.emit('chunkBeingRemoved', chunk.id, chunk.array, chunk.userData)\n world.noa.rendering.disposeChunkForRendering(chunk)\n chunk.dispose()\n setChunk(world, i, j, k, 0)\n unenqueueID(chunk.id, world._chunkIDsInMemory)\n unenqueueID(chunk.id, world._chunkIDsToMesh)\n unenqueueID(chunk.id, world._chunkIDsToMeshFirst)\n // when removing a chunk because it was invalid, arrange for chunk queues to get rebuilt\n if (chunk.isInvalid) world._lastPlayerChunkID = ''\n}\n\n\n\n\n\n// for a given chunk (i/j/k) and local location (x/y/z), \n// update all chunks that need it (including border chunks with the \n// changed block in their 1-block padding)\n\nfunction _updateChunkAndBorders(world, i, j, k, size, x, y, z, val) {\n var ilocs = [0]\n var jlocs = [0]\n var klocs = [0]\n if (x === 0) { ilocs.push(-1) } else if (x === size - 1) { ilocs.push(1) }\n if (y === 0) { jlocs.push(-1) } else if (y === size - 1) { jlocs.push(1) }\n if (z === 0) { klocs.push(-1) } else if (z === size - 1) { klocs.push(1) }\n\n for (var di of ilocs) {\n var lx = [size, x, -1][di + 1]\n for (var dj of jlocs) {\n var ly = [size, y, -1][dj + 1]\n for (var dk of klocs) {\n var lz = [size, z, -1][dk + 1]\n _modifyBlockData(world,\n i + di, j + dj, k + dk,\n lx, ly, lz, val)\n }\n }\n }\n}\n\n\n\n// internal function to modify a chunk's block\n\nfunction _modifyBlockData(world, i, j, k, x, y, z, val) {\n var chunk = getChunk(world, i, j, k)\n if (!chunk) return\n chunk.set(x, y, z, val)\n enqueueID(chunk.id, world._chunkIDsToMeshFirst)\n world.emit('chunkChanged', chunk)\n}\n\n\n\n\n// rebuild queue of chunks to be added around (ci,cj,ck)\nfunction buildChunkAddQueue(world, ci, cj, ck) {\n var add = Math.ceil(world.chunkAddDistance)\n var pending = world._chunkIDsToCreate\n var queue = []\n var distArr = []\n\n var addDistSq = world.chunkAddDistance * world.chunkAddDistance\n for (var i = ci - add; i <= ci + add; ++i) {\n for (var j = cj - add; j <= cj + add; ++j) {\n for (var k = ck - add; k <= ck + add; ++k) {\n var di = i - ci\n var dj = j - cj\n var dk = k - ck\n var distSq = di * di + dj * dj + dk * dk\n if (distSq > addDistSq) continue\n\n if (getChunk(world, i, j, k)) continue\n var id = getChunkID(i, j, k)\n if (pending.indexOf(id) > -1) continue\n queue.push(id)\n distArr.push(distSq)\n }\n }\n }\n world._chunkIDsToAdd = sortByReferenceArray(queue, distArr)\n}\n\n\n// rebuild queue of chunks to be removed from around (ci,cj,ck)\nfunction buildChunkRemoveQueue(world, ci, cj, ck) {\n var remDistSq = world.chunkRemoveDistance * world.chunkRemoveDistance\n var list = world._chunkIDsInMemory\n var queue = []\n var distArr = []\n\n for (var i = 0; i < list.length; i++) {\n var id = list[i]\n var loc = parseChunkID(id)\n var di = loc[0] - ci\n var dj = loc[1] - cj\n var dk = loc[2] - ck\n var distSq = di * di + dj * dj + dk * dk\n if (distSq < remDistSq) {\n var chunk = getChunk(world, loc[0], loc[1], loc[2])\n if (!chunk.isInvalid) continue\n distSq *= -1 // rig sort so that invalidated chunks get removed first\n }\n queue.push(id)\n distArr.push(distSq)\n }\n world._chunkIDsToRemove = sortByReferenceArray(queue, distArr)\n}\n\n\n\n// sorts [A, B, C] and [3, 1, 2] into [B, C, A]\nfunction sortByReferenceArray(data, ref) {\n var ind = Object.keys(ref)\n ind.sort((i, j) => ref[i] - ref[j])\n return ind.map(i => data[i])\n}\n\n\n\n\n\n// uniquely enqueue a string id into an array of them\nfunction enqueueID(id, queue) {\n var i = queue.indexOf(id)\n if (i >= 0) return\n queue.push(id)\n}\n\n// remove string id from queue if it exists\nfunction unenqueueID(id, queue) {\n var i = queue.indexOf(id)\n if (i >= 0) queue.splice(i, 1)\n}\n\n\n\n\n\nvar profile_queues = function (w, s) { }\nif (PROFILE_QUEUES) (function () {\n var every = 100\n var iter = 0\n var t, nrem, nreq, totalrec, nmesh\n var reqcts, remcts, meshcts\n var qadd, qrem, qmem, qgen, qmesh\n profile_queues = function (world, state) {\n if (state === 'start') {\n if (iter === 0) {\n t = performance.now()\n qadd = qrem = qmem = qgen = qmesh = 0\n totalrec = 0\n remcts = []\n reqcts = []\n meshcts = []\n }\n iter++\n nrem = nreq = nmesh = 0\n } else if (state === 'removed') {\n nrem++\n } else if (state === 'received') {\n totalrec++\n } else if (state === 'requested') {\n nreq++\n } else if (state === 'meshed') {\n nmesh++\n } else if (state === 'end') {\n // counts for frames that were fully worked\n if (world._chunkIDsToAdd.length) reqcts.push(nreq)\n if (world._chunkIDsToRemove.length) remcts.push(nrem)\n if (world._chunkIDsToMesh.length + world._chunkIDsToMeshFirst.length) meshcts.push(nmesh)\n // avg queue sizes\n qadd += world._chunkIDsToAdd.length\n qrem += world._chunkIDsToRemove.length\n qmem += world._chunkIDsInMemory.length\n qgen += world._chunkIDsToCreate.length\n qmesh += world._chunkIDsToMesh.length + world._chunkIDsToMeshFirst.length\n // on end\n if (iter === every) {\n var dt = (performance.now() - t) / 1000\n console.log('world chunk queues:',\n 'made', rnd(totalrec / dt), 'cps',\n '- avg queuelen: ',\n 'add', qadd / every,\n 'rem', qrem / every,\n 'mem', qmem / every,\n 'gen', qgen / every,\n 'mesh', qmesh / every,\n '- work/frame: ',\n 'req', rnd(reqcts.reduce(sum, 0) / reqcts.length),\n 'rem', rnd(remcts.reduce(sum, 0) / remcts.length),\n 'mesh', rnd(meshcts.reduce(sum, 0) / meshcts.length)\n )\n iter = 0\n }\n }\n }\n var sum = function (num, prev) { return num + prev }\n var rnd = function (n) { return Math.round(n * 10) / 10 }\n})()\n\n\nvar profile_hook = function (s) { }\nif (PROFILE) (function () {\n var every = 200\n var timer = new (__webpack_require__(/*! ./util */ \"../../src/lib/util.js\").Timer)(every, 'world ticks')\n profile_hook = function (state) {\n if (state === 'start') timer.start()\n else if (state === 'end') timer.report()\n else timer.add(state)\n }\n})()\n\n\n\n\n//# sourceURL=webpack:////Users/andy/dev/game/noa/src/lib/world.js?"); - -/***/ }), - -/***/ "./index.js": -/*!******************!*\ - !*** ./index.js ***! - \******************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/* globals BABYLON */\n\n\n\n/** \n * Testbed.\n*/\n\n\nvar noaEngine = __webpack_require__(/*! ../.. */ \"../../src/index.js\")\n\nvar opts = {\n\tdebug: true,\n\tshowFPS: true,\n\tinverseY: true,\n\tchunkSize: 32,\n\tchunkAddDistance: 2,\n\tchunkRemoveDistance: 3,\n\tblockTestDistance: 50,\n\ttexturePath: 'textures/',\n\tplayerStart: [0.5, 5, 0.5],\n\tplayerHeight: 1.4,\n\tplayerWidth: 0.6,\n\tplayerAutoStep: true,\n\tuseAO: true,\n\tAOmultipliers: [0.92, 0.8, 0.5],\n\treverseAOmultiplier: 1.0,\n}\n\n\n\n// create engine\nvar noa = noaEngine(opts)\n\n\n//\t\tWorld generation\n\n\n// block materials\nvar brownish = [0.45, 0.36, 0.22]\nvar greenish = [0.1, 0.8, 0.2]\nnoa.registry.registerMaterial('grass', greenish, null)\nnoa.registry.registerMaterial('dirt', brownish, null, false)\nvar strs = ['a', 'b', 'c', 'd', '1', '2']\nfor (var i = 0; i < 6; i++) {\n\tvar s = strs[i]\n\tnoa.registry.registerMaterial(s, null, s + '.png')\n\tnoa.registry.registerMaterial('t' + s, null, 't' + s + '.png', true)\n}\nnoa.registry.registerMaterial('water', [0.5, 0.5, 0.8, 0.7], null)\n\n\n// register a shinyDirt block with a custom render material\nvar shinyMat = noa.rendering.makeStandardMaterial('shinyDirtMat')\nshinyMat.specularColor.copyFromFloats(1, 1, 1)\nshinyMat.specularPower = 32\nshinyMat.bumpTexture = new BABYLON.Texture('textures/stone.png', scene);\nnoa.registry.registerMaterial('shinyDirt', brownish, null, false, shinyMat)\n\n\n// object block mesh\nvar mesh = BABYLON.Mesh.CreateBox('b', 1, noa.rendering.getScene())\nvar mat = BABYLON.Matrix.Scaling(0.2, 1, 0.2)\nmat.setTranslation(new BABYLON.Vector3(0, 0.5, 0))\nmesh.bakeTransformIntoVertices(mat)\n\n\n// block types registration\nvar _id = 1\nvar dirtID = noa.registry.registerBlock(_id++, { material: 'dirt' })\nvar shinyDirtID = noa.registry.registerBlock(_id++, { material: 'shinyDirt' })\nvar grassID = noa.registry.registerBlock(_id++, { material: 'grass' })\nvar testID1 = noa.registry.registerBlock(_id++, { material: ['b', 'd', '1', '2', 'c', 'a',] })\nvar testID2 = noa.registry.registerBlock(_id++, {\n\tmaterial: ['tb', 'td', 't1', 't2', 'tc', 'ta',],\n\topaque: false,\n})\nvar testID3 = noa.registry.registerBlock(_id++, { material: ['1', '2', 'a',] })\nvar waterID = noa.registry.registerBlock(_id++, {\n\tmaterial: 'water',\n\tfluid: true\n})\nvar customID = noa.registry.registerBlock(_id++, {\n\tblockMesh: mesh,\n\topaque: false,\n\tonCustomMeshCreate: function (mesh, x, y, z) {\n\t\tmesh.rotation.y = ((x + 0.234) * 1.234 + (z + 0.567) * 6.78) % (2 * Math.PI)\n\t},\n})\n\n\n\n\n\n// add a listener for when the engine requests a new world chunk\n// `data` is an ndarray - see https://github.com/scijs/ndarray\nnoa.world.on('worldDataNeeded', function (id, data, x, y, z) {\n\t// populate ndarray with world data (block IDs or 0 for air)\n\tfor (var i = 0; i < data.shape[0]; ++i) {\n\t\tfor (var k = 0; k < data.shape[2]; ++k) {\n\t\t\tvar height = getHeightMap(x + i, z + k)\n\t\t\tfor (var j = 0; j < data.shape[1]; ++j) {\n\t\t\t\tvar b = decideBlock(x + i, y + j, z + k, height)\n\t\t\t\tif (b) data.set(i, j, k, b)\n\t\t\t}\n\t\t}\n\t}\n\t// pass the finished data back to the game engine\n\tnoa.world.setChunkData(id, data)\n})\n\n// worldgen - return a heightmap for a given [x,z]\nfunction getHeightMap(x, z) {\n\tvar xs = 0.8 + 2 * Math.sin(x / 10)\n\tvar zs = 0.4 + 2 * Math.sin(z / 15 + x / 30)\n\treturn xs + zs\n}\n\nfunction decideBlock(x, y, z, height) {\n\t// flat area to NE\n\tif (x > 0 && z > 0) {\n\t\tvar h = 1\n\t\tif (z == 63 || x == 63) h = 20\n\t\treturn (y < h) ? grassID : 0\n\t}\n\t// general stuff\n\tif (y < height) {\n\t\treturn (y < 0) ? dirtID : grassID\n\t} else {\n\t\treturn (y < 1) ? waterID : 0\n\t}\n}\n\n\n\nsetTimeout(function () {\n\taddWorldFeatures()\n}, 1000)\n\nfunction addWorldFeatures() {\n\tnoa.setBlock(testID1, -6, 5, 6)\n\tnoa.setBlock(testID2, -4, 5, 6)\n\tnoa.setBlock(testID3, -2, 5, 6)\n\n\tvar z = 5\n\tmakeRows(10, 5, z, shinyDirtID)\n\tmakeRows(10, 5, z + 2, dirtID)\n\tmakeRows(10, 5, z + 5, dirtID)\n\tmakeRows(10, 5, z + 9, dirtID)\n\tmakeRows(10, 5, z + 14, dirtID)\n\tz += 18\n\tmakeRows(10, 5, z, customID)\n\tmakeRows(10, 5, z + 2, customID)\n\tmakeRows(10, 5, z + 5, customID)\n\tmakeRows(10, 5, z + 9, customID)\n\tmakeRows(10, 5, z + 14, customID)\n}\n\nfunction makeRows(length, x, z, block) {\n\tfor (var i = 0; i < length; i++) {\n\t\tnoa.setBlock(block, x + i, 1, z + i)\n\t\tnoa.setBlock(block, length * 2 + x - i, 1, z + i)\n\t}\n}\n\n\n// \t\tadd a mesh to represent the player\n\n\n// get the player entity's ID and other info (aabb, size)\nvar eid = noa.playerEntity\nvar dat = noa.entities.getPositionData(eid)\nvar w = dat.width\nvar h = dat.height\n\n// make a Babylon.js mesh and scale it, etc.\nvar scene = noa.rendering.getScene() // Babylon's \"Scene\" object\nvar playerMesh = BABYLON.Mesh.CreateBox('player', 1, scene)\nplayerMesh.scaling.x = playerMesh.scaling.z = w\nplayerMesh.scaling.y = h\n\n// offset of mesh relative to the entity's \"position\" (center of its feet)\nvar offset = [0, h / 2, 0]\n\n// a \"mesh\" component to the player entity\nnoa.entities.addComponent(eid, noa.entities.names.mesh, {\n\tmesh: playerMesh,\n\toffset: offset\n})\n\n\n\n\n// \t\tInteractivity:\n\n\n// on left mouse, set targeted block to be air\nnoa.inputs.down.on('fire', function () {\n\tif (noa.targetedBlock) noa.setBlock(0, noa.targetedBlock.position)\n})\n\n// place block on alt-fire (RMB/E)\nnoa.inputs.down.on('alt-fire', function () {\n\tif (noa.targetedBlock) noa.addBlock(pickedID, noa.targetedBlock.adjacent)\n})\nvar pickedID = grassID\n\n// pick block on middle fire (MMB/Q)\nnoa.inputs.down.on('mid-fire', function () {\n\tif (noa.targetedBlock) pickedID = noa.targetedBlock.blockID\n})\n\n\n// each tick, consume any scroll events and use them to zoom camera\nvar zoom = 0\nnoa.on('tick', function (dt) {\n\tvar scroll = noa.inputs.state.scrolly\n\tif (scroll === 0) return\n\n\t// handle zoom controls\n\tzoom += (scroll > 0) ? 1 : -1\n\tif (zoom < 0) zoom = 0\n\tif (zoom > 10) zoom = 10\n\tnoa.rendering.zoomDistance = zoom\n})\n\n\n\n// pausing\n\nnoa.inputs.bind('pause', 'P')\nnoa.inputs.down.on('pause', function () {\n\tpaused = !paused\n\tnoa.setPaused(paused)\n})\nvar paused = false\n\n\n\n// world swapping test\n\nfunction setWorld(switched) {\n\tdirtID = (switched) ? 2 : 1\n\tgrassID = (switched) ? 1 : 2\n}\n\nnoa.inputs.bind('swap-world', 'O')\nnoa.inputs.down.on('swap-world', function () {\n\tswapped = !swapped\n\tsetWorld(swapped)\n\tnoa.world.invalidateAllChunks()\n})\nvar swapped = false\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./index.js?"); - -/***/ }) - -/******/ }); \ No newline at end of file diff --git a/docs/test/index.js b/docs/test/index.js index 9428a990..577a1bb9 100644 --- a/docs/test/index.js +++ b/docs/test/index.js @@ -12,7 +12,7 @@ var noaEngine = require('../..') var opts = { debug: true, showFPS: true, - inverseY: true, + inverseY: false, chunkSize: 32, chunkAddDistance: 2, chunkRemoveDistance: 3, @@ -25,6 +25,7 @@ var opts = { useAO: true, AOmultipliers: [0.92, 0.8, 0.5], reverseAOmultiplier: 1.0, + thirdPerson: true, } @@ -179,6 +180,12 @@ var playerMesh = BABYLON.Mesh.CreateBox('player', 1, scene) playerMesh.scaling.x = playerMesh.scaling.z = w playerMesh.scaling.y = h +noa.rendering._camera.target = mesh +noa.rendering._camera.lockedTarget = mesh +noa.rendering._camera.radius = 16 +noa.rendering._camera.heightOffset = 16 +noa.rendering._camera.rotationOffset = 180 + // offset of mesh relative to the entity's "position" (center of its feet) var offset = [0, h / 2, 0] diff --git a/src/index.js b/src/index.js index 232c78b8..71f3101b 100644 --- a/src/index.js +++ b/src/index.js @@ -72,7 +72,9 @@ function Engine(opts) { this._tickRate = opts.tickRate this._paused = false this._dragOutsideLock = opts.dragCameraOutsidePointerLock + this._thirdPerson = opts.thirdPerson var self = this + console.log("third person?", this._thirdPerson) // container (html/div) manager this.container = createContainer(this, opts) @@ -374,7 +376,7 @@ Engine.prototype.getCameraVector = function () { var _camVec = vec3.create() - +var PI_2 = Math.PI / 2 /** * Raycast through the world, returning a result object for any non-air block * @param pos @@ -392,10 +394,19 @@ Engine.prototype.pick = function (pos, vec, dist, blockIdTestFunction) { } pos = pos || this.getPlayerEyePosition() vec = vec || this.getCameraVector() + var moved = new Float32Array([vec[0], vec[1], vec[2]]) + if(this._thirdPerson) { + var [pitch, yaw] = this.rendering.getCameraRotation() + yaw *= -0.75 + var x = Math.cos(yaw) * Math.cos(pitch) + var z = Math.sin(yaw) * Math.cos(pitch) + var y = Math.sin(pitch) + moved = new Float32Array([x, y, z]) + } dist = dist || this.blockTestDistance var rpos = _hitResult.position var rnorm = _hitResult.normal - var hit = raycast(testVoxel, pos, vec, dist, rpos, rnorm) + var hit = raycast(testVoxel, pos, moved, dist, rpos, rnorm) if (!hit) return null // position is right on a voxel border - adjust it so flooring will work as expected for (var i = 0; i < 3; i++) rpos[i] -= 0.01 * rnorm[i] diff --git a/src/lib/rendering.js b/src/lib/rendering.js index 140b2751..a307cf8b 100644 --- a/src/lib/rendering.js +++ b/src/lib/rendering.js @@ -41,7 +41,7 @@ var defaults = { reverseAOmultiplier: 1.0, useOctreesForDynamicMeshes: true, preserveDrawingBuffer: true, - cameraClass: BABYLON.FreeCamera, + thirdPerson: false, } @@ -64,6 +64,7 @@ function Rendering(noa, opts, canvas) { this.meshingCutoffTime = 6 // ms this._dynamicMeshOctrees = opts.useOctreesForDynamicMeshes this._resizeDebounce = 250 // ms + this._thirdPerson = opts.thirdPerson // set up babylon scene initScene(this, canvas, opts) @@ -76,7 +77,12 @@ function Rendering(noa, opts, canvas) { // Constructor helper - set up the Babylon.js scene and basic components function initScene(self, canvas, opts) { if (!BABYLON) throw new Error('BABYLON.js engine not found!') - var CameraClass = opts.cameraClass + var CameraClass + if(opts.thirdPerson) { + CameraClass = BABYLON.FollowCamera + } else { + CameraClass = BABYLON.FreeCamera + } // init internal properties self._engine = new BABYLON.Engine(canvas, opts.antiAlias, {