├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── demo
├── .eslintrc.js
├── animation-test.html
├── clean-slate.html
├── entity-viewer.html
├── globe.html
├── link-test.html
├── portal-test.html
├── sandbox.html
├── scripts
│ └── capture.js
└── snapshots.html
├── dist
└── ingress-model-viewer.js
├── docma.config.json
├── docs
├── content
│ └── readme.html
├── css
│ ├── docma.css
│ └── styles.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ ├── glyphicons-halflings-regular.woff2
│ ├── icomoon.svg
│ ├── icomoon.ttf
│ └── icomoon.woff
├── index.html
└── js
│ ├── app.min.js
│ ├── docma-web.js
│ └── highlight.pack.js
├── manifest
├── abaddon1.json
├── abaddon2.json
├── amar.json
├── assets.json
├── helios.json
├── jarvis.json
├── lightman.json
├── shard2017.json
└── shonin.json
├── package-lock.json
├── package.json
├── snapshot.json
├── src
├── OculusRiftEffect.js
├── animation
│ ├── animation.js
│ ├── animator.js
│ └── easing.js
├── asset-loader.js
├── asset-manager.js
├── camera.js
├── constants.js
├── drawable.js
├── drawable
│ ├── atmosphere.js
│ ├── bicolored.js
│ ├── glowramp.js
│ ├── inventory.js
│ ├── link.js
│ ├── ornament.js
│ ├── particle-portal.js
│ ├── particle.js
│ ├── portal-link.js
│ ├── resonator-link.js
│ ├── resource.js
│ ├── shield-effect.js
│ ├── spherical-portal-link.js
│ ├── textured-sphere.js
│ ├── textured.js
│ ├── world.js
│ └── xm.js
├── engine.js
├── entity.js
├── entity
│ ├── inventory.js
│ └── portal.js
├── geometry
│ ├── field.js
│ ├── parametric.js
│ └── particle-portal.js
├── gl-bound.js
├── gl
│ ├── gl-attribute.js
│ ├── gl-buffer.js
│ └── gl-index.js
├── ingress-model-viewer.js
├── mesh.js
├── mesh
│ ├── file.js
│ ├── particle-portal.js
│ ├── plane.js
│ ├── portal-link.js
│ ├── resonator-link.js
│ ├── sphere.js
│ └── spherical-portal-link.js
├── orbit-controls.js
├── polyfill.js
├── program.js
├── program
│ ├── glowramp.js
│ └── opaque.js
├── renderer.js
├── renderer
│ ├── object.js
│ └── portal.js
├── texture.js
├── utils.js
└── vertex-attribute.js
├── static
├── atmosphere.glsl.frag
├── atmosphere.glsl.vert
├── link3d.glsl.frag
├── link3d.glsl.vert
└── world.jpg
├── test
├── all.html
├── all.js
└── lib
│ ├── qunit.css
│ └── qunit.js
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | temp/
4 | tmp/
5 | logs/
6 | log/
7 | results/
8 | assets
9 | !docs/assets
10 |
11 | *.seed
12 | *.log
13 | *.csv
14 | *.dat
15 | *.out
16 | *.pid
17 | *.gz
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Daniel Benton
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ingress-model-viewer
2 |
3 | Rendering engine for Ingress game models
4 |
5 | ## About
6 |
7 | Capable of rendering Ingress game models in the browser, with no conversion required. However, this does require a copy of the `assets` directory from some version of the Ingress apk. Not all models are found in all versions of Ingress; you may have to look in older versions to find some assets. Shards, in particular, have been added and removed over time.
8 |
9 | A JavaScript library by Daniel Benton.
10 |
11 | ## Installation
12 |
13 | Grab the [compiled library](https://github.com/DeviateFish/ingress-model-viewer/blob/master/dist/ingress-model-viewer.js). Alternatively, build the library yourself, from source (see below).
14 |
15 | ## Usage
16 |
17 | Before starting, be sure to install dependencies with `npm install`.
18 |
19 | ### Starting development server
20 | Use `npm run serve` to start a development server. This will watch for changes and rebuild as necessary.
21 |
22 | ### Running ESLint
23 | Use `npm run lint` to run ESLint on all source files
24 |
25 | ### Adding assets
26 | By default, all demos will look for a directory named `assets` in the root directory. This can be the assets directory straight from a version of the ingress apk, extracted with your tool of choice. This library is capable of reading and parsing assets from the ingress apk without any conversion required.
27 |
28 | ### Demos
29 | Note that as of this writing, not all demos are functional or self-explanatory. They are mostly sandboxes for development at this time, but some are nifty demos of custom shaders/models (e.g. [`demos/globe.html`](https://github.com/DeviateFish/ingress-model-viewer/blob/master/demo/globe.html)). These sometimes will attempt to include external resources, such as [`FileSaver`](https://rawgit.com/eligrey/FileSaver.js/).
30 |
31 | ### Building documentation
32 | Use `npm run docs` to rebuild the documentation pages.
33 |
34 | ## Documentation
35 |
36 | See the [documentation site](https://deviatefish.github.io/ingress-model-viewer/)
37 |
38 | ## License
39 |
40 | MIT. See [`LICENSE`](https://github.com/DeviateFish/ingress-model-viewer/blob/master/LICENSE)
41 |
--------------------------------------------------------------------------------
/demo/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": false
5 | },
6 | "globals": {
7 | "IMV": true,
8 | "vec3": true,
9 | "vec4": true
10 | },
11 | "rules": {
12 | "no-console": false
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/demo/animation-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/demo/clean-slate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
15 |
16 |
17 |
18 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/demo/entity-viewer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ingress Model Sandbox - Inventory viewer
6 |
7 |
8 |
9 |
10 |
34 |
35 |
36 |
37 |
54 |
55 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/demo/globe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/demo/link-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/demo/portal-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/demo/sandbox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
21 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/demo/snapshots.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docma.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "src": {
3 | "engine": [
4 | "./src/engine.js",
5 | "./src/camera.js",
6 | "./src/asset-manager.js"
7 | ],
8 | "assets": "./src/asset-loader.js",
9 | "utilities": [
10 | "./src/utils.js",
11 | "./src/animation/animation.js",
12 | "./src/animation/easing.js"
13 | ],
14 | "drawables": [
15 | "./src/drawable/*.js",
16 | "./src/drawable.js"
17 | ],
18 | "constants": "./src/constants.js",
19 | "internals": "./src/**/*.js",
20 | "readme": "./README.md"
21 | },
22 | "dest": "./docs",
23 | "app": {
24 | "title": "Ingress Model Viewer",
25 | "routing": "query",
26 | "entrance": "content:readme",
27 | "server": "github",
28 | "base": "/ingress-model-viewer"
29 | },
30 | "jsdoc": {
31 | "module": false,
32 | "sort": "grouped"
33 | },
34 | "template": {
35 | "options": {
36 | "outline": "tree",
37 | "navItems": [
38 | {
39 | "label": "Documentation",
40 | "iconClass": "ico-book",
41 | "items": [
42 | {
43 | "label": "Readme",
44 | "href": "?content=readme"
45 | },
46 | {
47 | "label": "Core",
48 | "href": "?api=engine"
49 | },
50 | {
51 | "label": "AssetLoader",
52 | "href": "?api=assets"
53 | },
54 | {
55 | "label": "Utilities",
56 | "href": "?api=utilities"
57 | },
58 | {
59 | "label": "Drawables",
60 | "href": "?api=drawables"
61 | },
62 | {
63 | "label": "Constants",
64 | "href": "?api=constants"
65 | },
66 | {
67 | "label": "Internals",
68 | "href": "?api=internals"
69 | }
70 | ]
71 | },
72 | {
73 | "label": "Github",
74 | "href": "https://github.com/DeviateFish/ingress-model-viewer/",
75 | "iconClass": "ico-github"
76 | }
77 | ]
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/docs/content/readme.html:
--------------------------------------------------------------------------------
1 |
2 | ingress-model-viewer
3 |
4 | Rendering engine for Ingress game models
5 | About
6 | Capable of rendering Ingress game models in the browser, with no conversion required. However, this does require a copy of the assets
directory from some version of the Ingress apk. Not all models are found in all versions of Ingress; you may have to look in older versions to find some assets. Shards, in particular, have been added and removed over time.
7 | A JavaScript library by Daniel Benton.
8 | Installation
9 | Grab the compiled library . Alternatively, build the library yourself, from source (see below).
10 | Usage
11 | Before starting, be sure to install dependencies with npm install
.
12 | Starting development server
13 | Use npm run serve
to start a development server. This will watch for changes and rebuild as necessary.
14 | Running ESLint
15 | Use npm run lint
to run ESLint on all source files
16 | Adding assets
17 | By default, all demos will look for a directory named assets
in the root directory. This can be the assets directory straight from a version of the ingress apk, extracted with your tool of choice. This library is capable of reading and parsing assets from the ingress apk without any conversion required.
18 | Demos
19 | Note that as of this writing, not all demos are functional or self-explanatory. They are mostly sandboxes for development at this time, but some are nifty demos of custom shaders/models (e.g. demos/globe.html
). These sometimes will attempt to include external resources, such as FileSaver
.
20 | Building documentation
21 | Use npm run docs
to rebuild the documentation pages.
22 | Documentation
23 | See the documentation site
24 | License
25 | MIT. See LICENSE
26 |
--------------------------------------------------------------------------------
/docs/css/docma.css:
--------------------------------------------------------------------------------
1 | img.docma{display:inline-block;border:0}img.docma.emoji,img.docma.emoji-1x,img.docma.emoji-sm{height:1em;width:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}img.docma.emoji-md{height:1.33em;width:1.33em;margin:0 .0665em 0 .133em;vertical-align:-.133em}img.docma.emoji-lg{height:1.66em;width:1.66em;margin:0 .083em 0 .166em;vertical-align:-.166em}img.docma .emoji-2x{height:2em;width:2em;margin:0 .1em 0 .2em;vertical-align:-.2em}img.docma .emoji-3x{height:3em;width:3em;margin:0 .15em 0 .3em;vertical-align:-.3em}img.docma .emoji-4x{height:4em;width:4em;margin:0 .2em 0 .4em;vertical-align:-.4em}img.docma .emoji-5x{height:5em;width:5em;margin:0 .25em 0 .5em;vertical-align:-.5em}ul.docma.task-list,ul.docma.task-list>li.docma.task-item{padding-left:0;margin-left:0}ul.docma.task-list{list-style:none}
--------------------------------------------------------------------------------
/docs/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/docs/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/docs/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/docs/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/docs/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/docs/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/docs/fonts/icomoon.woff
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Viewer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/js/app.min.js:
--------------------------------------------------------------------------------
1 | !function(){"use strict";function a(a){function e(a,e,t){return''}return a?docma.utils.isClass(a)?e("diamond bg-green","Class","C"):docma.utils.isNamespace(a)?e("diamond bg-red","Namespace","N"):docma.utils.isModule(a)?e("diamond bg-pink","Module","M"):docma.utils.isEnum(a)?e("square bg-purple","Enum","E"):docma.utils.isGlobal(a)?docma.utils.isMethod(a)?e("diamond bg-accent","Global Function","G"):e("diamond bg-red","Global Object","G"):docma.utils.isInner(a)?docma.utils.isMethod(a)?e("circle bg-gray-dark","Inner Method","M"):e("circle bg-gray-dark","Inner","I"):docma.utils.isStaticProperty(a)?e("square bg-orange","Static Property","P"):docma.utils.isInstanceProperty(a)?e("circle bg-yellow","Instance Property","P"):docma.utils.isStaticMethod(a)?e("square bg-accent","Static Method","M"):docma.utils.isInstanceMethod(a)?e("circle bg-cyan","Instance Method","M"):"":""}function e(a){return a.replace(/[.#~:]/g,'$& ')}function t(a,t,i,s){var r=/[.#~:]/,n=i.split(r),o=n[n.length-1],d=20*(n.length-1);return o=i.slice(-(o.length+1)),'"}function i(){var a=o.val().trim().toLowerCase();if(""===a)return r.show(),void n.hide();n.show();var e;r.each(function(){e=$(this).attr("data-keywords"),e.indexOf(a)<0?$(this).hide():$(this).show()})}function s(a){for(var e=l;a.width()>215&&e>=d;)e--,a.css("font-size",e+"px")}docma.addFilter("$color_ops",function(a){return e(a)}).addFilter("$dot_prop",function(a){var t=/(.*)([.#~:]\w+)/g,i=t.exec(a);return i?''+e(i[1])+" "+e(i[2]):""+a+" "}).addFilter("$author",function(a){return(Array.isArray(a)?a:a.author||[]).join(", ")}).addFilter("$type",function(a){if(docma.utils.isConstructor(a))return"";if("function"===a.kind){var e=docma.utils.getReturnTypes(a);return e||""}var t=docma.utils.getTypes(a);return t||""}).addFilter("$type_sep",function(a){return docma.utils.isConstructor(a)?"":"function"===a.kind?"⇒":"class"===a.kind?":":a.type||a.returns?":":""}).addFilter("$param_desc",function(a){var e=a.optional?"":'Required ';return e+=a.description,docma.utils.parse(e)}).addFilter("$longname",function(a){return"string"==typeof a?a:(docma.utils.isConstructor(a)?"new ":"")+a.$longname}).addFilter("$longname_params",function(a){var t=docma.utils.isConstructor(a),i=e(a.$longname);if("function"===a.kind||t){var s,r="",n=t?"new ":"",o=n+i+"(";if(Array.isArray(a.params)){o+=a.params.reduce(function(a,e){return-1===e.name.indexOf(".")&&(s=e.hasOwnProperty("defaultvalue")?String(e.defaultvalue):"undefined",r=e.optional?'='+s+" ":"",a.push(e.name+r)),a},[]).join(", ")}return o+")"}return i}).addFilter("$extends",function(a){var e=Array.isArray(a)?a:a.augments;return docma.utils.listType(e)}).addFilter("$returns",function(a){var e=Array.isArray(a)?a:a.returns;return docma.utils.listTypeDesc(e)}).addFilter("$exceptions",function(a){var e=Array.isArray(a)?a:a.exceptions;return docma.utils.listTypeDesc(e)}).addFilter("$tags",function(a){var e='',t=" ",i=[];docma.utils.isDeprecated(a)&&i.push('deprecated'+t),docma.utils.isGlobal(a)&&!docma.utils.isConstructor(a)&&i.push(e+"global"+t),docma.utils.isStatic(a)&&i.push('static'+t),!1===docma.utils.isPublic(a)&&i.push(''+a.access+t),docma.utils.isNamespace(a)&&i.push(e+"namespace"+t),docma.utils.isReadOnly(a)&&i.push('readonly'+t);var s=Array.isArray(a)?a:a.tags||[],r=s.map(function(a){return''+a.originalTitle+t});return i=i.concat(r),i.length?" "+i.join(" "):""}).addFilter("$menuitem",function(e){var i=docma.documentation,s=docma.utils.getSymbolByName(i,e);if(!s)return e;var r=dust.filters.$id(s),n=docma.utils.getKeywords(s),o=docma.template.options.badges?a(s):"• ";return"tree"===docma.template.options.outline?t(r,o,e,n):'"}),hljs.configure({tabReplace:" ",useBR:!1});var r,n,o,d=10,l=14;docma.template.options.title||(docma.template.options.title=docma.app.title||"Documentation"),docma.on("render",function(a){$('[data-toggle="tooltip"]').tooltip({container:"body",placement:"bottom"}),docma.template.options.navbar||($("body, html").css("padding",0),$("#sidebar-wrapper").css("margin-top",0),$(".symbol-container").css({"padding-top":0,"margin-top":0})),docma.currentRoute&&"api"===docma.currentRoute.type&&(docma.template.options.search?(r=$("ul.sidebar-nav .sidebar-item"),n=$(".sidebar-search-clean"),o=$("#txt-search"),n.hide(),o.on("keyup",i),o.on("change",i),n.on("click",function(){o.val("").focus(),r.show(),n.hide()})):$(".sidebar-nav").css("top","65px"),$(".sidebar-nav .item-label").each(function(){s($(this))}));var e=$("#page-content-wrapper").find(".row").first();docma.template.options.sidebar?(docma.template.options.collapsed&&$("#wrapper").addClass("toggled"),$(".sidebar-toggle").click(function(a){if(a.preventDefault(),$("#wrapper").toggleClass("toggled"),!docma.template.options.navbar){var t=$("#wrapper").hasClass("toggled")?"+=30px":"-=30px";e.animate({"margin-left":t},300)}})):$("#wrapper").addClass("toggled");var t=50,d=15;docma.currentRoute&&"api"===docma.currentRoute.type||($("table").addClass("table table-striped table-bordered"),t=0,d=0),$(".navbar-brand").css({"margin-left":t+"px","padding-left":d+"px","padding-right":d+"px"}),$("#docma-main pre > code").each(function(a,e){hljs.highlightBlock(e)})})}();
--------------------------------------------------------------------------------
/manifest/amar.json:
--------------------------------------------------------------------------------
1 | {
2 | "texture": {
3 | "ArtifactAmarTexture": { "path": "scanner/artifacts/artifact_amar.png", "minFilter": "Linear", "magFilter": "Linear", "wrapS": "ClampToEdge", "wrapT": "ClampToEdge" }
4 | },
5 | "mesh": {
6 | "Amar1": { "path": "scanner/artifacts/artifact_amar_fragment_1.obj" },
7 | "AmarFrozen1": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_1.obj" },
8 | "Amar2": { "path": "scanner/artifacts/artifact_amar_fragment_2.obj" },
9 | "AmarFrozen2": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_2.obj" },
10 | "Amar3": { "path": "scanner/artifacts/artifact_amar_fragment_3.obj" },
11 | "AmarFrozen3": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_3.obj" },
12 | "Amar4": { "path": "scanner/artifacts/artifact_amar_fragment_4.obj" },
13 | "AmarFrozen4": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_4.obj" },
14 | "Amar5": { "path": "scanner/artifacts/artifact_amar_fragment_5.obj" },
15 | "AmarFrozen5": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_5.obj" },
16 | "Amar6": { "path": "scanner/artifacts/artifact_amar_fragment_6.obj" },
17 | "AmarFrozen6": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_6.obj" },
18 | "Amar7": { "path": "scanner/artifacts/artifact_amar_fragment_7.obj" },
19 | "AmarFrozen7": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_7.obj" },
20 | "Amar8": { "path": "scanner/artifacts/artifact_amar_fragment_8.obj" },
21 | "AmarFrozen8": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_8.obj" },
22 | "Amar9": { "path": "scanner/artifacts/artifact_amar_fragment_9.obj" },
23 | "AmarFrozen9": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_9.obj" },
24 | "Amar10": { "path": "scanner/artifacts/artifact_amar_fragment_10.obj" },
25 | "AmarFrozen10": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_10.obj" },
26 | "Amar11": { "path": "scanner/artifacts/artifact_amar_fragment_11.obj" },
27 | "AmarFrozen11": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_11.obj" },
28 | "Amar12": { "path": "scanner/artifacts/artifact_amar_fragment_12.obj" },
29 | "AmarFrozen12": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_12.obj" },
30 | "Amar13": { "path": "scanner/artifacts/artifact_amar_fragment_13.obj" },
31 | "AmarFrozen13": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_13.obj" },
32 | "Amar14": { "path": "scanner/artifacts/artifact_amar_fragment_14.obj" },
33 | "AmarFrozen14": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_14.obj" },
34 | "Amar15": { "path": "scanner/artifacts/artifact_amar_fragment_15.obj" },
35 | "AmarFrozen15": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_15.obj" },
36 | "Amar16": { "path": "scanner/artifacts/artifact_amar_fragment_16.obj" },
37 | "AmarFrozen16": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_16.obj" },
38 | "Amar17": { "path": "scanner/artifacts/artifact_amar_fragment_17.obj" },
39 | "AmarFrozen17": { "path": "scanner/artifacts/artifact_frozen_amar_fragment_17.obj" }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/manifest/helios.json:
--------------------------------------------------------------------------------
1 | {
2 | "texture": {
3 | "ArtifactHeliosTexture": { "path": "scanner/artifacts/artifact_helios.png", "minFilter": "Linear", "magFilter": "Linear", "wrapS": "ClampToEdge", "wrapT": "ClampToEdge" }
4 | },
5 | "mesh": {
6 | "Helios1": { "path": "scanner/artifacts/artifact_helios_fragment_1.obj" },
7 | "HeliosFrozen1": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_1.obj" },
8 | "Helios2": { "path": "scanner/artifacts/artifact_helios_fragment_2.obj" },
9 | "HeliosFrozen2": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_2.obj" },
10 | "Helios3": { "path": "scanner/artifacts/artifact_helios_fragment_3.obj" },
11 | "HeliosFrozen3": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_3.obj" },
12 | "Helios4": { "path": "scanner/artifacts/artifact_helios_fragment_4.obj" },
13 | "HeliosFrozen4": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_4.obj" },
14 | "Helios5": { "path": "scanner/artifacts/artifact_helios_fragment_5.obj" },
15 | "HeliosFrozen5": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_5.obj" },
16 | "Helios6": { "path": "scanner/artifacts/artifact_helios_fragment_6.obj" },
17 | "HeliosFrozen6": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_6.obj" },
18 | "Helios7": { "path": "scanner/artifacts/artifact_helios_fragment_7.obj" },
19 | "HeliosFrozen7": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_7.obj" },
20 | "Helios8": { "path": "scanner/artifacts/artifact_helios_fragment_8.obj" },
21 | "HeliosFrozen8": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_8.obj" },
22 | "Helios9": { "path": "scanner/artifacts/artifact_helios_fragment_9.obj" },
23 | "HeliosFrozen9": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_9.obj" },
24 | "Helios10": { "path": "scanner/artifacts/artifact_helios_fragment_10.obj" },
25 | "HeliosFrozen10": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_10.obj" },
26 | "Helios11": { "path": "scanner/artifacts/artifact_helios_fragment_11.obj" },
27 | "HeliosFrozen11": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_11.obj" },
28 | "Helios12": { "path": "scanner/artifacts/artifact_helios_fragment_12.obj" },
29 | "HeliosFrozen12": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_12.obj" },
30 | "Helios13": { "path": "scanner/artifacts/artifact_helios_fragment_13.obj" },
31 | "HeliosFrozen13": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_13.obj" },
32 | "Helios14": { "path": "scanner/artifacts/artifact_helios_fragment_14.obj" },
33 | "HeliosFrozen14": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_14.obj" },
34 | "Helios15": { "path": "scanner/artifacts/artifact_helios_fragment_15.obj" },
35 | "HeliosFrozen15": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_15.obj" },
36 | "Helios16": { "path": "scanner/artifacts/artifact_helios_fragment_16.obj" },
37 | "HeliosFrozen16": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_16.obj" },
38 | "Helios17": { "path": "scanner/artifacts/artifact_helios_fragment_17.obj" },
39 | "HeliosFrozen17": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_17.obj" },
40 | "Helios18": { "path": "scanner/artifacts/artifact_helios_fragment_18.obj" },
41 | "HeliosFrozen18": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_18.obj" },
42 | "Helios19": { "path": "scanner/artifacts/artifact_helios_fragment_19.obj" },
43 | "HeliosFrozen19": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_19.obj" },
44 | "Helios20": { "path": "scanner/artifacts/artifact_helios_fragment_20.obj" },
45 | "HeliosFrozen20": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_20.obj" },
46 | "Helios21": { "path": "scanner/artifacts/artifact_helios_fragment_21.obj" },
47 | "HeliosFrozen21": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_21.obj" },
48 | "Helios22": { "path": "scanner/artifacts/artifact_helios_fragment_22.obj" },
49 | "HeliosFrozen22": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_22.obj" },
50 | "Helios23": { "path": "scanner/artifacts/artifact_helios_fragment_23.obj" },
51 | "HeliosFrozen23": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_23.obj" },
52 | "Helios24": { "path": "scanner/artifacts/artifact_helios_fragment_24.obj" },
53 | "HeliosFrozen24": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_24.obj" },
54 | "Helios25": { "path": "scanner/artifacts/artifact_helios_fragment_25.obj" },
55 | "HeliosFrozen25": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_25.obj" },
56 | "Helios26": { "path": "scanner/artifacts/artifact_helios_fragment_26.obj" },
57 | "HeliosFrozen26": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_26.obj" },
58 | "Helios27": { "path": "scanner/artifacts/artifact_helios_fragment_27.obj" },
59 | "HeliosFrozen27": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_27.obj" },
60 | "Helios28": { "path": "scanner/artifacts/artifact_helios_fragment_28.obj" },
61 | "HeliosFrozen28": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_28.obj" },
62 | "Helios29": { "path": "scanner/artifacts/artifact_helios_fragment_29.obj" },
63 | "HeliosFrozen29": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_29.obj" },
64 | "Helios30": { "path": "scanner/artifacts/artifact_helios_fragment_30.obj" },
65 | "HeliosFrozen30": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_30.obj" },
66 | "Helios31": { "path": "scanner/artifacts/artifact_helios_fragment_31.obj" },
67 | "HeliosFrozen31": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_31.obj" },
68 | "Helios32": { "path": "scanner/artifacts/artifact_helios_fragment_32.obj" },
69 | "HeliosFrozen32": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_32.obj" },
70 | "Helios33": { "path": "scanner/artifacts/artifact_helios_fragment_33.obj" },
71 | "HeliosFrozen33": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_33.obj" },
72 | "Helios34": { "path": "scanner/artifacts/artifact_helios_fragment_34.obj" },
73 | "HeliosFrozen34": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_34.obj" },
74 | "Helios35": { "path": "scanner/artifacts/artifact_helios_fragment_35.obj" },
75 | "HeliosFrozen35": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_35.obj" },
76 | "Helios36": { "path": "scanner/artifacts/artifact_helios_fragment_36.obj" },
77 | "HeliosFrozen36": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_36.obj" },
78 | "Helios37": { "path": "scanner/artifacts/artifact_helios_fragment_37.obj" },
79 | "HeliosFrozen37": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_37.obj" },
80 | "Helios38": { "path": "scanner/artifacts/artifact_helios_fragment_38.obj" },
81 | "HeliosFrozen38": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_38.obj" },
82 | "Helios39": { "path": "scanner/artifacts/artifact_helios_fragment_39.obj" },
83 | "HeliosFrozen39": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_39.obj" },
84 | "Helios40": { "path": "scanner/artifacts/artifact_helios_fragment_40.obj" },
85 | "HeliosFrozen40": { "path": "scanner/artifacts/artifact_frozen_helios_fragment_40.obj" }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/manifest/jarvis.json:
--------------------------------------------------------------------------------
1 | {
2 | "texture": {
3 | "ArtifactJarvisTexture": { "path": "scanner/artifacts/artifact_jarvis.png", "minFilter": "Linear", "magFilter": "Linear", "wrapS": "ClampToEdge", "wrapT": "ClampToEdge" }
4 | },
5 | "mesh": {
6 | "Jarvis1": { "path": "scanner/artifacts/artifact_jarvis_fragment_1.obj" },
7 | "Jarvis2": { "path": "scanner/artifacts/artifact_jarvis_fragment_2.obj" },
8 | "Jarvis3": { "path": "scanner/artifacts/artifact_jarvis_fragment_3.obj" },
9 | "Jarvis4": { "path": "scanner/artifacts/artifact_jarvis_fragment_4.obj" },
10 | "Jarvis5": { "path": "scanner/artifacts/artifact_jarvis_fragment_5.obj" },
11 | "Jarvis6": { "path": "scanner/artifacts/artifact_jarvis_fragment_6.obj" },
12 | "Jarvis7": { "path": "scanner/artifacts/artifact_jarvis_fragment_7.obj" },
13 | "Jarvis8": { "path": "scanner/artifacts/artifact_jarvis_fragment_8.obj" },
14 | "Jarvis9": { "path": "scanner/artifacts/artifact_jarvis_fragment_9.obj" },
15 | "Jarvis10": { "path": "scanner/artifacts/artifact_jarvis_fragment_10.obj" },
16 | "Jarvis11": { "path": "scanner/artifacts/artifact_jarvis_fragment_11.obj" },
17 | "Jarvis12": { "path": "scanner/artifacts/artifact_jarvis_fragment_12.obj" },
18 | "Jarvis13": { "path": "scanner/artifacts/artifact_jarvis_fragment_13.obj" }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/manifest/lightman.json:
--------------------------------------------------------------------------------
1 | {
2 | "texture": {
3 | "ArtifactLightmanTexture": { "path": "scanner/artifacts/artifact_lightman.png", "minFilter": "Linear", "magFilter": "Linear", "wrapS": "ClampToEdge", "wrapT": "ClampToEdge" }
4 | },
5 | "mesh": {
6 | "Lightman1": { "path": "scanner/artifacts/artifact_lightman_fragment_1.obj" },
7 | "LightmanFrozen1": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_1.obj" },
8 | "Lightman2": { "path": "scanner/artifacts/artifact_lightman_fragment_2.obj" },
9 | "LightmanFrozen2": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_2.obj" },
10 | "Lightman3": { "path": "scanner/artifacts/artifact_lightman_fragment_3.obj" },
11 | "LightmanFrozen3": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_3.obj" },
12 | "Lightman4": { "path": "scanner/artifacts/artifact_lightman_fragment_4.obj" },
13 | "LightmanFrozen4": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_4.obj" },
14 | "Lightman5": { "path": "scanner/artifacts/artifact_lightman_fragment_5.obj" },
15 | "LightmanFrozen5": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_5.obj" },
16 | "Lightman6": { "path": "scanner/artifacts/artifact_lightman_fragment_6.obj" },
17 | "LightmanFrozen6": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_6.obj" },
18 | "Lightman7": { "path": "scanner/artifacts/artifact_lightman_fragment_7.obj" },
19 | "LightmanFrozen7": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_7.obj" },
20 | "Lightman8": { "path": "scanner/artifacts/artifact_lightman_fragment_8.obj" },
21 | "LightmanFrozen8": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_8.obj" },
22 | "Lightman9": { "path": "scanner/artifacts/artifact_lightman_fragment_9.obj" },
23 | "LightmanFrozen9": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_9.obj" },
24 | "Lightman10": { "path": "scanner/artifacts/artifact_lightman_fragment_10.obj" },
25 | "LightmanFrozen10": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_10.obj" },
26 | "Lightman11": { "path": "scanner/artifacts/artifact_lightman_fragment_11.obj" },
27 | "LightmanFrozen11": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_11.obj" },
28 | "Lightman12": { "path": "scanner/artifacts/artifact_lightman_fragment_12.obj" },
29 | "LightmanFrozen12": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_12.obj" },
30 | "Lightman13": { "path": "scanner/artifacts/artifact_lightman_fragment_13.obj" },
31 | "LightmanFrozen13": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_13.obj" },
32 | "Lightman14": { "path": "scanner/artifacts/artifact_lightman_fragment_14.obj" },
33 | "LightmanFrozen14": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_14.obj" },
34 | "Lightman15": { "path": "scanner/artifacts/artifact_lightman_fragment_15.obj" },
35 | "LightmanFrozen15": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_15.obj" },
36 | "Lightman16": { "path": "scanner/artifacts/artifact_lightman_fragment_16.obj" },
37 | "LightmanFrozen16": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_16.obj" },
38 | "Lightman17": { "path": "scanner/artifacts/artifact_lightman_fragment_17.obj" },
39 | "LightmanFrozen17": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_17.obj" },
40 | "Lightman18": { "path": "scanner/artifacts/artifact_lightman_fragment_18.obj" },
41 | "LightmanFrozen18": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_18.obj" },
42 | "Lightman19": { "path": "scanner/artifacts/artifact_lightman_fragment_19.obj" },
43 | "LightmanFrozen19": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_19.obj" },
44 | "Lightman20": { "path": "scanner/artifacts/artifact_lightman_fragment_20.obj" },
45 | "LightmanFrozen20": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_20.obj" },
46 | "Lightman21": { "path": "scanner/artifacts/artifact_lightman_fragment_21.obj" },
47 | "LightmanFrozen21": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_21.obj" },
48 | "Lightman22": { "path": "scanner/artifacts/artifact_lightman_fragment_22.obj" },
49 | "LightmanFrozen22": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_22.obj" },
50 | "Lightman23": { "path": "scanner/artifacts/artifact_lightman_fragment_23.obj" },
51 | "LightmanFrozen23": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_23.obj" },
52 | "Lightman24": { "path": "scanner/artifacts/artifact_lightman_fragment_24.obj" },
53 | "LightmanFrozen24": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_24.obj" },
54 | "Lightman25": { "path": "scanner/artifacts/artifact_lightman_fragment_25.obj" },
55 | "LightmanFrozen25": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_25.obj" },
56 | "Lightman26": { "path": "scanner/artifacts/artifact_lightman_fragment_26.obj" },
57 | "LightmanFrozen26": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_26.obj" },
58 | "Lightman27": { "path": "scanner/artifacts/artifact_lightman_fragment_27.obj" },
59 | "LightmanFrozen27": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_27.obj" },
60 | "Lightman28": { "path": "scanner/artifacts/artifact_lightman_fragment_28.obj" },
61 | "LightmanFrozen28": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_28.obj" },
62 | "Lightman29": { "path": "scanner/artifacts/artifact_lightman_fragment_29.obj" },
63 | "LightmanFrozen29": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_29.obj" },
64 | "Lightman30": { "path": "scanner/artifacts/artifact_lightman_fragment_30.obj" },
65 | "LightmanFrozen30": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_30.obj" },
66 | "Lightman31": { "path": "scanner/artifacts/artifact_lightman_fragment_31.obj" },
67 | "LightmanFrozen31": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_31.obj" },
68 | "Lightman32": { "path": "scanner/artifacts/artifact_lightman_fragment_32.obj" },
69 | "LightmanFrozen32": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_32.obj" },
70 | "Lightman33": { "path": "scanner/artifacts/artifact_lightman_fragment_33.obj" },
71 | "LightmanFrozen33": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_33.obj" },
72 | "Lightman34": { "path": "scanner/artifacts/artifact_lightman_fragment_34.obj" },
73 | "LightmanFrozen34": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_34.obj" },
74 | "Lightman35": { "path": "scanner/artifacts/artifact_lightman_fragment_35.obj" },
75 | "LightmanFrozen35": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_35.obj" },
76 | "Lightman36": { "path": "scanner/artifacts/artifact_lightman_fragment_36.obj" },
77 | "LightmanFrozen36": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_36.obj" },
78 | "Lightman37": { "path": "scanner/artifacts/artifact_lightman_fragment_37.obj" },
79 | "LightmanFrozen37": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_37.obj" },
80 | "Lightman38": { "path": "scanner/artifacts/artifact_lightman_fragment_38.obj" },
81 | "LightmanFrozen38": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_38.obj" },
82 | "Lightman39": { "path": "scanner/artifacts/artifact_lightman_fragment_39.obj" },
83 | "LightmanFrozen39": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_39.obj" },
84 | "Lightman40": { "path": "scanner/artifacts/artifact_lightman_fragment_40.obj" },
85 | "LightmanFrozen40": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_40.obj" },
86 | "Lightman41": { "path": "scanner/artifacts/artifact_lightman_fragment_41.obj" },
87 | "LightmanFrozen41": { "path": "scanner/artifacts/artifact_frozen_lightman_fragment_41.obj" }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/manifest/shonin.json:
--------------------------------------------------------------------------------
1 | {
2 | "texture": {
3 | "ArtifactShoninTexture": { "path": "scanner/artifacts/artifact_shonin.png", "minFilter": "Linear", "magFilter": "Linear", "wrapS": "ClampToEdge", "wrapT": "ClampToEdge" }
4 | },
5 | "mesh": {
6 | "Shonin1": { "path": "scanner/artifacts/artifact_shonin_fragment_1.obj" },
7 | "ShoninFrozen1": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_1.obj" },
8 | "Shonin2": { "path": "scanner/artifacts/artifact_shonin_fragment_2.obj" },
9 | "ShoninFrozen2": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_2.obj" },
10 | "Shonin3": { "path": "scanner/artifacts/artifact_shonin_fragment_3.obj" },
11 | "ShoninFrozen3": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_3.obj" },
12 | "Shonin4": { "path": "scanner/artifacts/artifact_shonin_fragment_4.obj" },
13 | "ShoninFrozen4": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_4.obj" },
14 | "Shonin5": { "path": "scanner/artifacts/artifact_shonin_fragment_5.obj" },
15 | "ShoninFrozen5": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_5.obj" },
16 | "Shonin6": { "path": "scanner/artifacts/artifact_shonin_fragment_6.obj" },
17 | "ShoninFrozen6": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_6.obj" },
18 | "Shonin7": { "path": "scanner/artifacts/artifact_shonin_fragment_7.obj" },
19 | "ShoninFrozen7": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_7.obj" },
20 | "Shonin8": { "path": "scanner/artifacts/artifact_shonin_fragment_8.obj" },
21 | "ShoninFrozen8": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_8.obj" },
22 | "Shonin9": { "path": "scanner/artifacts/artifact_shonin_fragment_9.obj" },
23 | "ShoninFrozen9": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_9.obj" },
24 | "Shonin10": { "path": "scanner/artifacts/artifact_shonin_fragment_10.obj" },
25 | "ShoninFrozen10": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_10.obj" },
26 | "Shonin11": { "path": "scanner/artifacts/artifact_shonin_fragment_11.obj" },
27 | "ShoninFrozen11": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_11.obj" },
28 | "Shonin12": { "path": "scanner/artifacts/artifact_shonin_fragment_12.obj" },
29 | "ShoninFrozen12": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_12.obj" },
30 | "Shonin13": { "path": "scanner/artifacts/artifact_shonin_fragment_13.obj" },
31 | "ShoninFrozen13": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_13.obj" },
32 | "Shonin14": { "path": "scanner/artifacts/artifact_shonin_fragment_14.obj" },
33 | "ShoninFrozen14": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_14.obj" },
34 | "Shonin15": { "path": "scanner/artifacts/artifact_shonin_fragment_15.obj" },
35 | "ShoninFrozen15": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_15.obj" },
36 | "Shonin16": { "path": "scanner/artifacts/artifact_shonin_fragment_16.obj" },
37 | "ShoninFrozen16": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_16.obj" },
38 | "Shonin17": { "path": "scanner/artifacts/artifact_shonin_fragment_17.obj" },
39 | "ShoninFrozen17": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_17.obj" },
40 | "Shonin18": { "path": "scanner/artifacts/artifact_shonin_fragment_18.obj" },
41 | "ShoninFrozen18": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_18.obj" },
42 | "Shonin19": { "path": "scanner/artifacts/artifact_shonin_fragment_19.obj" },
43 | "ShoninFrozen19": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_19.obj" },
44 | "Shonin20": { "path": "scanner/artifacts/artifact_shonin_fragment_20.obj" },
45 | "ShoninFrozen20": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_20.obj" },
46 | "Shonin21": { "path": "scanner/artifacts/artifact_shonin_fragment_21.obj" },
47 | "ShoninFrozen21": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_21.obj" },
48 | "Shonin22": { "path": "scanner/artifacts/artifact_shonin_fragment_22.obj" },
49 | "ShoninFrozen22": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_22.obj" },
50 | "Shonin23": { "path": "scanner/artifacts/artifact_shonin_fragment_23.obj" },
51 | "ShoninFrozen23": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_23.obj" },
52 | "Shonin24": { "path": "scanner/artifacts/artifact_shonin_fragment_24.obj" },
53 | "ShoninFrozen24": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_24.obj" },
54 | "Shonin25": { "path": "scanner/artifacts/artifact_shonin_fragment_25.obj" },
55 | "ShoninFrozen25": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_25.obj" },
56 | "Shonin26": { "path": "scanner/artifacts/artifact_shonin_fragment_26.obj" },
57 | "ShoninFrozen26": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_26.obj" },
58 | "Shonin27": { "path": "scanner/artifacts/artifact_shonin_fragment_27.obj" },
59 | "ShoninFrozen27": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_27.obj" },
60 | "Shonin28": { "path": "scanner/artifacts/artifact_shonin_fragment_28.obj" },
61 | "ShoninFrozen28": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_28.obj" },
62 | "Shonin29": { "path": "scanner/artifacts/artifact_shonin_fragment_29.obj" },
63 | "ShoninFrozen29": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_29.obj" },
64 | "Shonin30": { "path": "scanner/artifacts/artifact_shonin_fragment_30.obj" },
65 | "ShoninFrozen30": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_30.obj" },
66 | "Shonin31": { "path": "scanner/artifacts/artifact_shonin_fragment_31.obj" },
67 | "ShoninFrozen31": { "path": "scanner/artifacts/artifact_frozen_shonin_fragment_31.obj" }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ingress-model-viewer",
3 | "version": "0.22.2",
4 | "main": "src/ingress-model-viewer.js",
5 | "description": "Rendering engine for Ingress game models",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/DeviateFish/ingress-model-viewer.git"
9 | },
10 | "scripts": {
11 | "build": "webpack",
12 | "lint": "eslint ./src/ --ext .js -c .eslintrc.js",
13 | "serve": "webpack-dev-server --open",
14 | "docs": "docma -c docma.config.json"
15 | },
16 | "keywords": ["ingress", "renderer", "model viewer"],
17 | "author": "DeviateFish",
18 | "license": "MIT",
19 | "readmeFilename": "README.md",
20 | "dependencies": {
21 | "es6-promises": "1.0.10",
22 | "gl-matrix": "2.3.2",
23 | "java-deserializer": "0.3.0",
24 | "libtga": "0.4.0"
25 | },
26 | "devDependencies": {
27 | "babel-core": "^6.22.1",
28 | "babel-loader": "^6.2.10",
29 | "babel-preset-es2015": "^6.22.0",
30 | "docma": "^1.5.1",
31 | "eslint": "^3.19.0",
32 | "eslint-loader": "^1.7.1",
33 | "webpack": "^2.2.1",
34 | "webpack-dev-server": "^2.3.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/animation/animation.js:
--------------------------------------------------------------------------------
1 | import Ease from './easing';
2 | /**
3 | * Simple class for hooking up animations to drawables.
4 | *
5 | * Animations refers specifically to things like moving objects/cameras around.
6 | * Animations handled by the existing shaders should be implemented that way, instead.
7 | */
8 |
9 | class Animation {
10 |
11 | /**
12 | * Create an animation for a drawable
13 | *
14 | * @chainable
15 | * @param {Number} duration Duration of one cycle of the animation
16 | * @param {Function} transform Animation callback
17 | * Parameter: Number t
18 | * Parameter: Drawable drawable
19 | * @param {Function} timing Timing function (i.e. easing) Defaults. to Ease.linear
20 | * @param {Boolean} loop Whether or not to loop the animation
21 | * @return {this} The animation
22 | */
23 | constructor(duration, transform, timing, loop) {
24 | this.elapsed = 0;
25 | this.duration = duration;
26 | this.transform = transform;
27 | this.timing = timing || Ease.linear;
28 | this.loop = loop;
29 | this.running = false;
30 | this.next = [];
31 | return this;
32 | }
33 |
34 | /**
35 | * Starts the animation
36 | *
37 | * @chainable
38 | * @return {this} Returns `this`
39 | */
40 | start() {
41 | if(!this.running) {
42 | this.running = true;
43 | }
44 | return this;
45 | }
46 |
47 | /**
48 | * Stops the animation, and resets the elasped time to 0
49 | *
50 | * @chainable
51 | * @return {this} Returns `this`
52 | */
53 | stop() {
54 | this.elapsed = 0;
55 | return this.pause();
56 | }
57 |
58 | /**
59 | * Pauses the running animation
60 | *
61 | * @chainable
62 | * @return {this} Returns `this`
63 | */
64 | pause() {
65 | if(this.running) {
66 | this.running = false;
67 | }
68 | return this;
69 | }
70 |
71 | /**
72 | * Perform a step of the animation
73 | * @param {Number} delta Time elasped since last frame
74 | * @param {Drawable} drawable The drawable to operate on
75 | * @return {Boolean} Return true to signal the end of the animation
76 | */
77 | step(delta, drawable) {
78 | if(!this.running) {
79 | return false;
80 | }
81 | this.elapsed += delta;
82 | // if we're done with the animation
83 | if (this.elapsed > this.duration && !this.loop) {
84 | let t = this.timing(1);
85 | this.transform(t, drawable);
86 | this.stop();
87 | return true;
88 | }
89 | let t = this.timing((this.elapsed / this.duration) % 1);
90 | this.transform(t, drawable);
91 | return false;
92 | }
93 |
94 | /**
95 | * Allows for chaining of animations
96 | *
97 | * @chainable
98 | * @param {Animation} animation The animation to queue after this one
99 | * completes. Note that this isn't really
100 | * valid for looping animations
101 | * @return {this} Returns `this`
102 | */
103 | chain(animation) {
104 | if (!(animation instanceof Animation)) {
105 | throw new Error('New animation should be an instance of an Animation');
106 | }
107 | this.next.push(animation);
108 | return this;
109 | }
110 | }
111 |
112 | export default Animation;
113 |
--------------------------------------------------------------------------------
/src/animation/animator.js:
--------------------------------------------------------------------------------
1 | import Animation from './animation';
2 |
3 | /**
4 | * This class handles running animations on animatable objects.
5 | *
6 | * This is generally composed into a class (e.g. Camera or Drawable)
7 | */
8 | class Animator {
9 | constructor() {
10 | this._animations = [];
11 | }
12 |
13 | /**
14 | * Adds an animation.
15 | *
16 | * Note that this does not start the animation.
17 | *
18 | * @chainable
19 | * @param {Animation} animation The animation to be run.
20 | * This will need to be started independently, or
21 | * prior to being added.
22 | * @return {this} Returns `this`
23 | */
24 | addAnimation(animation) {
25 | if (!(animation instanceof Animation)) {
26 | throw new Error('New animation should be an instance of an Animation');
27 | }
28 | this._animations.unshift(animation);
29 | return this;
30 | }
31 |
32 | /**
33 | * @param {Number} delta Time since last update
34 | * @param {Object} subject Object to animate
35 | * @return {void}
36 | */
37 | runAnimations(delta, subject) {
38 | let i = this._animations.length - 1;
39 | for(; i >= 0; i--) {
40 | let animation = this._animations[i];
41 | if(animation.running && animation.step(delta, subject)) {
42 | this._animations.splice.apply(
43 | this._animations,
44 | [i, 1].concat(animation.next)
45 | );
46 | }
47 | }
48 | }
49 | }
50 |
51 | export default Animator;
52 |
--------------------------------------------------------------------------------
/src/asset-loader.js:
--------------------------------------------------------------------------------
1 | import libtga from 'libtga';
2 | import Promise from 'es6-promises';
3 |
4 | /**
5 | * An AssetLoader manages loading one or more assets. It handles debouncing of
6 | * of multiple requests for the same asset, etc.
7 | *
8 | * @class
9 | */
10 | class AssetLoader {
11 |
12 | /**
13 | * Noop.
14 | */
15 | constructor() {
16 | this._callbacks = {};
17 | this._assets = {};
18 | }
19 |
20 | /**
21 | * Loads a single asset.
22 | *
23 | * @param {String} url The url of the asset to load.
24 | * @param {String} type The type of asset being requested
25 | *
26 | * @returns { Promise } Returns a promise. Resolves immediately
27 | * if the asset it already loaded.
28 | * @see AssetLoader.loadResource
29 | */
30 | loadAsset(url, type) {
31 | var name = '_' + encodeURIComponent(url);
32 | if(this._assets[name])
33 | {
34 | return Promise.resolve(this._assets[name]);
35 | }
36 | else
37 | {
38 | return new Promise((resolve, reject) => {
39 | this._callbacks[name] = this._callbacks[name] || [];
40 | this._callbacks[name].push({resolve, reject});
41 | if(!this._assets.hasOwnProperty(name))
42 | {
43 | this._assets[name] = false;
44 | AssetLoader.loadResource(url, type).then((value) => {
45 | this._assets[name] = value;
46 | var cb;
47 | while((cb = this._callbacks[name].shift()))
48 | {
49 | cb.resolve(value);
50 | }
51 | }).catch((err) => {
52 | var cb;
53 | while((cb = this._callbacks[name].shift()))
54 | {
55 | cb.reject(err);
56 | }
57 | });
58 | }
59 | });
60 | }
61 | }
62 |
63 | /**
64 | * Load a set of assets in parallel
65 | * @param {Array} urls Array of urls of resources
66 | * @param {Array} types Array of types of resources
67 | * @return {Promise} A Promise that resolves when all assets are loaded,
68 | * or rejects when any fail.
69 | * @see AssetLoader.loadResource
70 | */
71 | loadAssetGroup(urls, types) {
72 | if(urls.length !== types.length)
73 | {
74 | throw 'Incompatible types: types.length = ' + types.length + '; urls.length = ' + urls.length;
75 | }
76 | return Promise.all(
77 | urls.map((url, i) => {
78 | return this.loadAsset(url, types[i]);
79 | })
80 | );
81 | }
82 |
83 | /**
84 | * Directly retrieve an asset from the cache
85 | * @param {String} name The cache key
86 | * @return {mixed} The cached asset, if it exists.
87 | */
88 | getAsset(name) {
89 | return this._assets[name];
90 | }
91 |
92 | /**
93 | * Loads a resource via xhr or Image
94 | *
95 | * @static
96 | * @param {String} url href of the resource to fetch
97 | * @param {String} type One of XHMLHttpRequest's supported responseType
98 | * values (arraybuffer, blob, document, json, text)
99 | * or 'image' or 'image.co' (for a cross-origin image)
100 | * @return {Promise} Returns a promise that resolves on success, or rejects
101 | * on failure.
102 | */
103 | static loadResource(url, type) {
104 | return new Promise(function(resolve, reject) {
105 | if(type === 'image' || type === 'image.co')
106 | {
107 | if(/\.tga$/.test(url))
108 | {
109 | libtga.loadFile(url, function(err, tga) {
110 | if(err)
111 | {
112 | reject(err);
113 | return;
114 | }
115 | var canvas = document.createElement('canvas');
116 | var context = canvas.getContext('2d');
117 | var imageData = context.createImageData(tga.width, tga.height);
118 | imageData.data.set(tga.imageData);
119 | canvas.height = tga.height;
120 | canvas.width = tga.width;
121 | context.putImageData(imageData, 0, 0);
122 | var image = new Image();
123 | image.onload = function() {
124 | resolve(this);
125 | };
126 | image.onerror = function(e) {
127 | reject(e);
128 | };
129 | image.src = canvas.toDataURL();
130 | });
131 | }
132 | else
133 | {
134 | var i = new Image();
135 | // cross-origin image:
136 | if(type === 'image.co')
137 | {
138 | i.crossOrigin = 'anoymous';
139 | }
140 | i.onload = function()
141 | {
142 | resolve(this);
143 | };
144 | i.onerror = function(e)
145 | {
146 | reject(e);
147 | };
148 | i.src = url;
149 | }
150 | }
151 | else
152 | {
153 | var xhr = new XMLHttpRequest();
154 | xhr.open('GET', url);
155 | xhr.responseType = type;
156 | xhr.onload = function() {
157 | resolve(this.response);
158 | };
159 | xhr.onerror = function(e) {
160 | reject(e);
161 | };
162 |
163 | xhr.send();
164 | }
165 | });
166 | }
167 | }
168 |
169 | export default AssetLoader;
170 |
--------------------------------------------------------------------------------
/src/camera.js:
--------------------------------------------------------------------------------
1 | import { mat4, vec3 } from 'gl-matrix';
2 | import Animator from './animation/animator';
3 |
4 | /**
5 | * A Camera is a class to manage view of the scene.
6 | *
7 | * @class
8 | * @chainable
9 | * @param {Number} width The width of the viewport
10 | * @param {Number} height The height of the viewport
11 | * @return {this} The new Camera
12 | */
13 | class Camera {
14 |
15 | constructor(width, height) {
16 | this.position = vec3.create();
17 | this.view = mat4.create();
18 | this.project = mat4.create();
19 | this.viewProject = mat4.create();
20 | this.hFoV = Math.PI / 4;
21 | this.near = 0.1;
22 | this.far = 100;
23 | this.width = width;
24 | this.height = height;
25 | this.focus = vec3.create();
26 | this.up = vec3.fromValues(0, 1, 0);
27 | this.animator = new Animator();
28 | return this._updateProjection()._updateView();
29 | }
30 |
31 | /**
32 | * Generates a view matrix, as if the camera is looking at the specified point.
33 | *
34 | * @chainable
35 | * @param {vec3} point The point to look at
36 | * @return {this} Returns `this`
37 | */
38 | lookAt(point) {
39 | vec3.copy(this.focus, point);
40 | return this._updateView();
41 | }
42 |
43 | /**
44 | * Moves the camera's position in some direction
45 | *
46 | * Maintains the camera's current focus.
47 | *
48 | * @chainable
49 | * @param {vec3} vec The vector to translate by
50 | * @return {this} Returns `this`
51 | */
52 | translate(vec) {
53 | vec3.translate(this.position, this.position, vec);
54 | return this._updateView();
55 | }
56 |
57 | /**
58 | * Sets the camera's position
59 | *
60 | * @chainable
61 | * @param {vec3} position Camera position
62 | * @return {this} Returns `this`
63 | */
64 | setPosition(position) {
65 | vec3.copy(this.position, position);
66 | return this._updateView();
67 | }
68 |
69 | /**
70 | * Set the viewport dimensions and update the projection matrix
71 | *
72 | * @chainable
73 | * @param {Number} width Viewport width
74 | * @param {Number} height Viewport height
75 | * @return {this} Returns `this`
76 | */
77 | setDimensions(width, height) {
78 | this.width = width;
79 | this.height = height;
80 | return this._updateProjection();
81 | }
82 |
83 | /**
84 | * Set the horizontal field of view
85 | *
86 | * @chainable
87 | * @param {Number} fov Field of view, in radians
88 | * @return {this} Returns `this`
89 | */
90 | setFieldOfView(fov) {
91 | this.hFoV = fov;
92 | return this._updateProjection();
93 | }
94 |
95 | /**
96 | * Sets the far clip distance
97 | *
98 | * @chainable
99 | * @param {Number} far Max viewable distance
100 | * @return {this} Returns `this`
101 | */
102 | setFar(far) {
103 | this.far = far;
104 | return this._updateProjection();
105 | }
106 |
107 | /**
108 | * Adds an animation
109 | *
110 | * @chainable
111 | * @param {Animation} animation The animation to be run.
112 | * This will need to be started independently, or prior to being added.
113 | * @return {this} Returns `this`
114 | */
115 | addAnimation(animation) {
116 | this.animator.addAnimation(animation);
117 | return this;
118 | }
119 |
120 | /**
121 | * @param {Number} delta The time elapsed since the last draw
122 | * @return {this} Returns `this`
123 | */
124 | updateTime(delta) {
125 | this.animator.runAnimations(delta, this);
126 | return this;
127 | }
128 |
129 | /**
130 | * Updates the camera's view matrix from all parameters.
131 | *
132 | * @chainable
133 | * @private
134 | * @return {this} Returns `this`
135 | */
136 | _updateView() {
137 | mat4.lookAt(this.view, this.position, this.focus, this.up);
138 | return this;
139 | }
140 |
141 | /**
142 | * Update the camera's projection matrix
143 | *
144 | * @chainable
145 | * @private
146 | * @return {this} Returns `this`
147 | */
148 | _updateProjection() {
149 | mat4.perspective(this.project, this.hFoV, this.width / this.height, this.near, this.far);
150 | return this;
151 | }
152 | }
153 |
154 | export default Camera;
155 |
--------------------------------------------------------------------------------
/src/drawable/atmosphere.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import Drawable from '../drawable';
3 | import SphereMesh from '../mesh/sphere';
4 | import { mat3, mat4 } from 'gl-matrix';
5 |
6 | const PROGRAM = Constants.Program.Atmosphere;
7 |
8 | /**
9 | * Creates an "atmosphere" effect.
10 | *
11 | * This is a modified version of the atmosphere program from:
12 | * https://github.com/dataarts/webgl-globe/blob/master/globe/globe.js
13 | * @param {Number} radius Radius of the world.
14 | * This should match the radius of the world mesh the
15 | * atmosphere is being rendered over.
16 | * @param {Number} vSlices Number of vertical slices for the sphere mesh
17 | * @param {Number} hSlices Number of horizontal slices for the sphere mesh
18 | * @param {Number} scaleFactor The percent to scale the mesh
19 | * @return {void}
20 | */
21 | class AtmosphereDrawable extends Drawable {
22 | constructor(radius, vSlices, hSlices, scaleFactor) {
23 | super(PROGRAM, null);
24 | this.radius = radius;
25 | this.vSlices = vSlices;
26 | this.hSlices = hSlices;
27 | this.uniforms.u_normalMatrix = mat3.create();
28 | this.scaleFactor = scaleFactor || 1.1;
29 | this.setScalarScale(this.scaleFactor);
30 | }
31 |
32 | /**
33 | * Updates the view matrices of the model
34 | *
35 | * @chainable
36 | * @see src/drawable/model.js#updateView
37 | * @param {mat4} viewProject combined projection matrix multiplied by view matrix.
38 | * @return {this} Returns `this`
39 | */
40 | updateView(viewProject) {
41 | super.updateView(viewProject);
42 | var invert = mat4.invert(mat4.create(), viewProject),
43 | transpose = mat4.transpose(mat4.create(), invert);
44 | this.uniforms.u_normalMatrix = mat3.fromMat4(mat3.create(), transpose);
45 | return this;
46 | }
47 |
48 | /**
49 | * Initializes the drawable
50 | *
51 | * @see src/drawable.js
52 | * @param {AssetManager} manager The AssetManager containing the required assets.
53 | * @return {Promise} A Promise that resolves when the asset is initialized
54 | */
55 | init(manager) {
56 | this.mesh = new SphereMesh(
57 | manager._gl,
58 | this.radius,
59 | this.vSlices,
60 | this.hSlices
61 | );
62 | return super.init(manager);
63 | }
64 | }
65 |
66 | export default AtmosphereDrawable;
67 |
--------------------------------------------------------------------------------
/src/drawable/bicolored.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import { vec4 } from 'gl-matrix';
4 |
5 | const PROGRAM = Constants.Program.Bicolored;
6 |
7 | /**
8 | * Default quality color.
9 | *
10 | * @private
11 | * @type {vec4}
12 | */
13 | const defaultColor0 = vec4.clone(Constants.qualityColors.VERY_RARE);
14 |
15 | /**
16 | * Default glow color
17 | *
18 | * @private
19 | * @type {vec4}
20 | */
21 | const defaultColor1 = vec4.clone(Constants.xmColors.coreGlow);
22 |
23 | /**
24 | * This is used for items and other renderables that have two visible colors
25 | *
26 | * The specifics of it are basically: if the texture has an opacity less than 0.5,
27 | * the texture color is blended with u_color0
28 | * Otherwise, it's the texture color blended with u_color1
29 | *
30 | * Or something like that.
31 | * @param {String} meshName Internal name of the mesh for this drawable
32 | * @param {String} textureName Internal name of the texture for this drawble
33 | */
34 | class BicoloredDrawable extends TexturedDrawable {
35 |
36 | constructor(meshName, textureName) {
37 | super(PROGRAM, meshName, textureName);
38 | this.uniforms.u_color0 = vec4.clone(defaultColor0);
39 | this.uniforms.u_color1 = vec4.clone(defaultColor1);
40 | }
41 | }
42 |
43 | export default BicoloredDrawable;
44 |
--------------------------------------------------------------------------------
/src/drawable/glowramp.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import { vec4 } from 'gl-matrix';
4 |
5 | const PROGRAM = Constants.Program.Glowramp;
6 |
7 | /**
8 | * Default base color for the glowramp drawable
9 | *
10 | * @private
11 | * @type {vec4}
12 | */
13 | const defaultBaseColor = vec4.clone(Constants.teamColors.NEUTRAL);
14 |
15 | /**
16 | * A "glowramp" refers to the usage of the red, green, and blue channels to create
17 | * a "glowing" texture.
18 | *
19 | * @param {String} meshName Internal name of the mesh
20 | * @param {String} textureName Internal name of the texture
21 | */
22 | class GlowrampDrawable extends TexturedDrawable {
23 |
24 | constructor(meshName, textureName) {
25 | super(PROGRAM, meshName, textureName);
26 | this.uniforms.u_baseColor = vec4.clone(defaultBaseColor);
27 | this.uniforms.u_rotation = 0;
28 | this.uniforms.u_rampTarget = 0;
29 | this.uniforms.u_alpha = 0.6;
30 | }
31 |
32 | /**
33 | * Updates default glowramp variables (rotation, ramp target, elapsed time
34 | * and alpha)
35 | * @param {Number} tick Time delta since last tick
36 | * @return {Boolean} @see src/drawable.js#updateTime
37 | */
38 | updateTime(tick) {
39 | var ret = super.updateTime(tick);
40 | var inc = this.elapsed / 5000;
41 | this.uniforms.u_rotation = inc;
42 | this.uniforms.u_rampTarget = Math.sin(Math.PI / 2 * (inc - Math.floor(inc)));
43 | this.uniforms.u_alpha = Math.sin(inc) * 0.05 + 0.75;
44 | return ret;
45 | }
46 | }
47 |
48 | export default GlowrampDrawable;
49 |
--------------------------------------------------------------------------------
/src/drawable/inventory.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import BicoloredDrawable from './bicolored';
3 | import XmDrawable from './xm';
4 | import TexturedDrawable from './textured';
5 |
6 | /**
7 | * Contains drawable primitives for many of the inventory items.
8 | */
9 | var Inventory = {};
10 |
11 |
12 | var meshes = Constants.Mesh.Inventory;
13 | var textures = Constants.Texture;
14 |
15 | /**
16 | * Creates the outer "shell" for an xm item.
17 | *
18 | * @private
19 | * @param {String} name Internal name of the mesh
20 | * @return {itembase} A BicoloredDrawable with the specified mesh name
21 | * and the flipcard texture
22 | */
23 | function createShell(name) {
24 | class itembase extends BicoloredDrawable {
25 | constructor() {
26 | super(meshes[name], textures.FlipCard);
27 | }
28 | }
29 |
30 | return itembase;
31 | }
32 |
33 | /**
34 | * Creates the xm "core" of an item
35 | *
36 | * @private
37 | * @param {String} name Internal name of the xm mesh
38 | * @return {xmbase} An XmDrawable with the specified mesh name
39 | * and the Xm texture.
40 | */
41 | function createCore(name) {
42 | class xmbase extends XmDrawable {
43 | constructor() {
44 | super(meshes[name], textures.Xm);
45 | }
46 | }
47 |
48 | return xmbase;
49 | }
50 |
51 | /**
52 | * Creates a media item
53 | *
54 | * @private
55 | * @param {String} name Media mesh internal name
56 | * @return {media} A TexturedDrawable with the Textured program,
57 | * the specified mesh, and the flipcard texture.
58 | */
59 | function createMedia(name) {
60 | class media extends TexturedDrawable {
61 | constructor() {
62 | super(
63 | Constants.Program.Textured,
64 | meshes[name],
65 | Constants.Texture.FlipCard
66 | );
67 | }
68 | }
69 |
70 | return media;
71 | }
72 |
73 | for(var i in meshes) {
74 | if(/^Media/.test(i)) {
75 | if(i === 'MediaPlane') {
76 | continue;
77 | }
78 | Inventory[i] = createMedia(i);
79 | }
80 | else {
81 | if(/Xm$/.test(i)) {
82 | Inventory[i] = createCore(i);
83 | }
84 | else {
85 | Inventory[i] = createShell(i);
86 | }
87 | }
88 | }
89 |
90 | export default Inventory;
91 |
--------------------------------------------------------------------------------
/src/drawable/link.js:
--------------------------------------------------------------------------------
1 | import TexturedDrawable from './textured';
2 | import { vec3, mat3, quat } from 'gl-matrix';
3 |
4 | /**
5 | * The LinkDrawable represents the base class for link-type drawables.
6 | *
7 | * @param {String} programName Internal name of the program to use
8 | * @param {String} textureName Internal name of the texture to use
9 | */
10 | class LinkDrawable extends TexturedDrawable {
11 |
12 | constructor(programName, textureName) {
13 | super(programName, null, textureName);
14 | this.uniforms.u_cameraFwd = vec3.fromValues(0, 0, -1);
15 | this.uniforms.u_elapsedTime = 0;
16 | }
17 |
18 | /**
19 | * Updates the camera transforms for the link drawables
20 | * @param {mat4} viewProject Combined view and project matrix
21 | * @param {Camera} camera The camera
22 | * @return {void}
23 | */
24 | updateView(viewProject, camera) {
25 | super.updateView(viewProject, camera);
26 | if(camera) {
27 | var rot = mat3.fromMat4(mat3.create(), camera.view);
28 | var q = quat.fromMat3(quat.create(), rot);
29 | var fwd = vec3.transformQuat(vec3.create(), vec3.fromValues(0, 0, -1), q);
30 | vec3.normalize(fwd, fwd);
31 | this.uniforms.u_cameraFwd = fwd;
32 | }
33 | }
34 |
35 | /**
36 | * Updates default periodic uniforms for links
37 | * @param {Number} delta Time delta since last draw
38 | * @return {Boolean} @see src/drawable.js#updateTime
39 | */
40 | updateTime(delta) {
41 | var ret = super.updateTime(delta);
42 | this.uniforms.u_elapsedTime = ((this.elapsed / 1000) % 300.0) * 0.1;
43 | return ret;
44 | }
45 | }
46 |
47 | export default LinkDrawable;
48 |
--------------------------------------------------------------------------------
/src/drawable/ornament.js:
--------------------------------------------------------------------------------
1 | import TexturedDrawable from './textured';
2 | import Constants from '../constants';
3 | import { vec2, vec4 } from 'gl-matrix';
4 |
5 | const PROGRAM = Constants.Program.RegionTextured;
6 |
7 | /**
8 | * An OrnamentDrawable is a TextuedDrawable that draws an ornament on
9 | * a unit plane.
10 | * @param {String} meshName Internal name of the ornament mesh
11 | * @param {String} textureName Internal name of the texture
12 | */
13 | class OrnamentDrawable extends TexturedDrawable {
14 | constructor(meshName, textureName) {
15 | super(PROGRAM, meshName, textureName);
16 | this.uniforms.u_texCoordBase = vec2.fromValues(0, 0);
17 | this.uniforms.u_texCoordExtent = vec2.fromValues(1, 1);
18 | this.uniforms.u_color = vec4.clone(Constants.teamColors.LOKI);
19 | }
20 | }
21 |
22 | export default OrnamentDrawable;
23 |
--------------------------------------------------------------------------------
/src/drawable/particle-portal.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import ParticleDrawable from './particle';
3 | import ParticlePortalMesh from '../mesh/particle-portal';
4 | import { vec3, vec4 } from 'gl-matrix';
5 |
6 | const PROGRAM = Constants.Program.ParticlePortal;
7 | const MAX_SYSTEMS = 40;
8 |
9 | /**
10 | * A drawable representing a system of particles emanating from a portal
11 | *
12 | * @class
13 | * @extends {ParticleDrawable}
14 | * @param {vec4} color The particle color
15 | * @param {Number} height The height to propagate
16 | * @param {Number} count The number of particles
17 | * @param {Number} spread The spread between particles
18 | * @param {Number} distance The distance
19 | */
20 | class ParticlePortalDrawable extends ParticleDrawable {
21 |
22 | constructor(color, height, count, spread, distance) {
23 | super(PROGRAM);
24 | let modColor = vec4.clone(color);
25 | modColor[3] = count;
26 | // uniforms should be flattened arrays.
27 | // Since they're expected to contain up to 40 systems, we'll need to create
28 | // arrays of 40 * 4 elements each.
29 | this.uniforms.u_color = new Float32Array(MAX_SYSTEMS * 4);
30 | this.uniforms.u_position = new Float32Array(MAX_SYSTEMS * 4);
31 | this.uniforms.u_params = new Float32Array(MAX_SYSTEMS * 4);
32 | // fill in the first 4 slots.
33 | vec4.copy(this.uniforms.u_color, modColor);
34 | vec4.copy(this.uniforms.u_position, vec4.fromValues(0, 0, 0, height));
35 | vec4.copy(this.uniforms.u_params, vec4.fromValues(0, distance, spread, 1));
36 | }
37 |
38 | /**
39 | * Update the view, and uniforms pertaining to the view
40 | * @param {mat4} viewProject Camera's combine view and projection matrix
41 | * @param {Camera} camera The camera
42 | * @return {void}
43 | */
44 | updateView(viewProject, camera) {
45 | super.updateView(viewProject, camera);
46 | if(camera) {
47 | let dist = vec3.length(camera.position);
48 | let scale = Math.pow(dist, 0.2);
49 | this.uniforms.u_params[3] = scale;
50 | }
51 | }
52 |
53 | /**
54 | * Update the time for the system
55 | * @param {Number} delta Time since last tick
56 | * @return {Boolean} Results of onUpdate
57 | */
58 | updateTime(delta) {
59 | let ret = super.updateTime(delta);
60 | this.uniforms.u_params[0] = (this.elapsed / 100000) * this.uniforms.u_params[1];
61 | return ret;
62 | }
63 |
64 | /**
65 | * Initialize the portal particle mesh
66 | * @param {AssetManager} manager AssetManager containing the remaining assets
67 | * @return {Boolean} Success/failure
68 | */
69 | init(manager) {
70 | this.mesh = new ParticlePortalMesh(manager._gl);
71 | return super.init(manager);
72 | }
73 | }
74 |
75 | export default ParticlePortalDrawable;
76 |
--------------------------------------------------------------------------------
/src/drawable/particle.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import { vec3 } from 'gl-matrix';
4 |
5 | const TEXTURE = Constants.Texture.Particle;
6 |
7 | /**
8 | * A ParticleDrawable represents the base class for particles
9 | *
10 | * @extends {TexturedDrawable}
11 | */
12 | class ParticleDrawable extends TexturedDrawable {
13 |
14 | constructor(programName) {
15 | super(programName, null, TEXTURE);
16 | this.uniforms.u_cameraPos = vec3.fromValues(0, 0, 0);
17 | }
18 |
19 | updateView(viewProject, camera) {
20 | super.updateView(viewProject, camera);
21 | if(camera) {
22 | vec3.copy(this.uniforms.u_cameraPos, camera.position);
23 | }
24 | }
25 | }
26 |
27 | export default ParticleDrawable;
28 |
--------------------------------------------------------------------------------
/src/drawable/portal-link.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import LinkDrawable from './link';
3 | import PortalLinkMesh from '../mesh/portal-link';
4 |
5 | /**
6 | * A LinkDrawable that represents a link from one portal to another
7 | * @extends {LinkDrawable}
8 | * @param {vec2} start X, Z of origin portal
9 | * @param {vec2} end X, Z of destination portal
10 | * @param {vec4} color Color of link
11 | * @param {Number} startPercent Percent health of the origin portal
12 | * @param {Number} endPercent Percent health of the destination portal
13 | */
14 | class PortalLinkDrawable extends LinkDrawable {
15 |
16 | constructor(start, end, color, startPercent, endPercent) {
17 | super(Constants.Program.Link, Constants.Texture.PortalLink);
18 | this.start = start;
19 | this.end = end;
20 | this.color = color;
21 | this.startPercent = startPercent;
22 | this.endPercent = endPercent;
23 | }
24 |
25 | /**
26 | * Construct the PortalLinkMesh for this link
27 | * @param {AssetManager} manager AssetManager to look up the program and texture
28 | * @return {Boolean} Success/failure
29 | */
30 | init(manager) {
31 | this.mesh = new PortalLinkMesh(manager._gl, this.start, this.end, this.color, this.startPercent, this.endPercent);
32 | return super.init(manager);
33 | }
34 | }
35 |
36 | export default PortalLinkDrawable;
37 |
--------------------------------------------------------------------------------
/src/drawable/resonator-link.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import LinkDrawable from './link';
3 | import ResonatorLinkMesh from '../mesh/resonator-link';
4 |
5 |
6 | /**
7 | * A ResonatorLinkDrawable is a LinkDrawable that represents a link
8 | * between a portal and a resonator
9 | * @param {vec2} portalPosition X,Z of the portal (usually 0,0)
10 | * @param {Number} slot Slot (0-7)
11 | * @param {Number} distance Usually 0-40
12 | * @param {vec4} color Color of the resonator link (TODO: make this disco)
13 | * @param {Number} resonatorPercent Percent health of the resonator
14 | */
15 | class ResonatorLinkDrawable extends LinkDrawable {
16 |
17 | constructor(portalPosition, slot, distance, color, resonatorPercent) {
18 | super(Constants.Program.Link, Constants.Texture.ResonatorLink);
19 | this.portalPosition = portalPosition;
20 | this.slot = slot;
21 | this.distance = distance;
22 | this.color = color;
23 | this.resonatorPercent = resonatorPercent;
24 | }
25 |
26 | /**
27 | * Creates a ResonatorLinkMesh with the given params, and initializes the
28 | * texture/program
29 | * @param {AssetManager} manager AssetManager containing the required program/texture
30 | * @return {Boolean} Success/failure
31 | */
32 | init(manager) {
33 | this.mesh = new ResonatorLinkMesh(
34 | manager._gl,
35 | this.portalPosition,
36 | this.slot,
37 | this.distance,
38 | this.color,
39 | this.resonatorPercent
40 | );
41 | return super.init(manager);
42 | }
43 | }
44 |
45 | export default ResonatorLinkDrawable;
46 |
--------------------------------------------------------------------------------
/src/drawable/resource.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import BicoloredDrawable from './bicolored';
3 |
4 | var Resource = {};
5 | var meshes = Constants.Mesh.Resource;
6 |
7 | /**
8 | * Creates a resource drawable
9 | *
10 | * @private
11 | * @param {String} name InternalName
12 | * @return {itembase} A BicoloredDrawable representing this resource item
13 | */
14 | function createResource(name) {
15 | class itembase extends BicoloredDrawable {
16 | constructor() {
17 | super(meshes[name], Constants.Texture.FlipCard);
18 | }
19 | }
20 |
21 | return itembase;
22 | }
23 |
24 | for(var i in meshes) {
25 | Resource[name] = createResource(i);
26 | }
27 |
28 | export default Resource;
29 |
--------------------------------------------------------------------------------
/src/drawable/shield-effect.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import { vec2, vec3, vec4 } from 'gl-matrix';
4 |
5 | const PROGRAM = Constants.Program.ShieldEffect;
6 |
7 | // these defaults are whack. Need to find the real
8 | // functions used to update these, too
9 | // As of 1.62.0, that was in ...ingress.common.scanner.b.a.d
10 | // The baksmali is a little jacked up, though.
11 | var defaultColor = vec4.clone(Constants.teamColors.NEUTRAL);
12 | var defaultRampTargetInv = vec2.fromValues(0.5, 1.3);
13 | var defaultContributions = vec3.fromValues(0.5, 0.5, 0.5);
14 |
15 | /**
16 | * Represents the shield idle effect
17 | *
18 | * Note: This probably should actually be generalized differently...
19 | * Apparently all three shield effects use the same texture and mesh, but have
20 | * different programs and variables.
21 | *
22 | * So, perhaps a better way would be to have the base class hardcode the texture
23 | * and mesh internal names, and then the derived classes pick a program and handle
24 | * the variables.
25 | *
26 | * @param {String} meshName Mesh internal name
27 | * @param {String} textureName Texture internal name
28 | */
29 | class ShieldEffectDrawable extends TexturedDrawable {
30 |
31 | constructor(meshName, textureName) {
32 | super(PROGRAM, meshName, textureName);
33 | this.uniforms.u_color = vec4.clone(defaultColor);
34 | this.uniforms.u_rampTargetInvWidth = vec2.clone(defaultRampTargetInv);
35 | this.uniforms.u_contributionsAndAlpha = vec3.clone(defaultContributions);
36 | }
37 |
38 | /**
39 | * Updates the default uniforms
40 | *
41 | * Note: these are nothing like what's in the apk, just some functions that
42 | * happen to look kinda sorta nice
43 | * @param {Number} delta Time since last frame
44 | * @return {Boolean} Returns true to continue the animation.
45 | */
46 | updateTime(delta) {
47 | var ret = super.updateTime(delta);
48 | var inc = this.elapsed / 10000;
49 | // this is so shitty, but again, this java decompiler really doesn't like the file.
50 | // This is nothing close to what's 'supposed' to happen in these uniforms, just a hack
51 | // that's kinda sorta like the actual thing.
52 | this.uniforms.u_rampTargetInvWidth[0] = -(inc - Math.floor(inc));
53 | this.uniforms.u_rampTargetInvWidth[1] = Math.sin((inc - Math.floor(inc)) * Math.PI / 2);
54 | // u_contributionsAndAlpha?
55 | return ret;
56 | }
57 | }
58 |
59 | export default ShieldEffectDrawable;
60 |
--------------------------------------------------------------------------------
/src/drawable/spherical-portal-link.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import LinkDrawable from './link';
3 | import SphericalPortalLinkMesh from '../mesh/spherical-portal-link';
4 |
5 |
6 | /**
7 | * Represents a portal link that follows the surface of a sphere.
8 | *
9 | * Hooray for custom shaders, etc!
10 | *
11 | * @param {Number} sphereRadius Radius of the sphere
12 | * @param {vec2} start Lat,lng of the origin portal
13 | * @param {vec2} end Lat,lng of the destination portal
14 | * @param {vec4} color Color of the link
15 | * @param {Number} startPercent Percent health of the origin portal
16 | * @param {Number} endPercent Percent health of the destination portal
17 | */
18 | class SphericalPortalLinkDrawable extends LinkDrawable {
19 |
20 | constructor(sphereRadius, start, end, color, startPercent, endPercent) {
21 | super(Constants.Program.SphericalLink, Constants.Texture.PortalLink);
22 | this.radius = sphereRadius;
23 | this.start = start;
24 | this.end = end;
25 | this.color = color;
26 | this.startPercent = startPercent;
27 | this.endPercent = endPercent;
28 | this.uniforms.u_model = this._model;
29 | }
30 |
31 | /**
32 | * Constructs a mesh for the link, then initializes the remaining assets.
33 | * @param {AssetManager} manager AssetManager containing the program/texture
34 | * @return {Boolean} Success/failure
35 | */
36 | init(manager) {
37 | this.mesh = new SphericalPortalLinkMesh(
38 | manager._gl,
39 | this.radius,
40 | this.start,
41 | this.end,
42 | this.color,
43 | this.startPercent,
44 | this.endPercent
45 | );
46 | return super.init(manager);
47 | }
48 |
49 | updateView(viewProject, camera) {
50 | super.updateView(viewProject, camera);
51 | this.uniforms.u_model = this._model;
52 | }
53 | }
54 |
55 | export default SphericalPortalLinkDrawable;
56 |
--------------------------------------------------------------------------------
/src/drawable/textured-sphere.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import SphereMesh from '../mesh/sphere';
4 |
5 | const PROGRAM = Constants.Program.Textured;
6 |
7 | /**
8 | * A sphere with a texture mapped to it
9 | *
10 | * @param {String} textureName Internal name of the texture to use
11 | * @param {Number} radius Radius of the sphere
12 | * @param {Number} vSlices Number of vertical slices
13 | * @param {Number} hSlices Number of horizontal slices
14 | */
15 | class TexturedSphereDrawable extends TexturedDrawable {
16 | constructor(textureName, radius, vSlices, hSlices) {
17 | super(PROGRAM, null, textureName);
18 | this.radius = radius;
19 | this.vSlices = vSlices;
20 | this.hSlices = hSlices;
21 | }
22 |
23 | /**
24 | * Create a sphere mesh and initialize the other resources
25 | * @param {AssetManager} manager AssetManager containing the texture/program
26 | * @return {Boolean} Success/failure
27 | */
28 | init(manager) {
29 | this.mesh = new SphereMesh(
30 | manager._gl,
31 | this.radius,
32 | this.vSlices,
33 | this.hSlices
34 | );
35 | return super.init(manager);
36 | }
37 | }
38 |
39 | export default TexturedSphereDrawable;
40 |
--------------------------------------------------------------------------------
/src/drawable/textured.js:
--------------------------------------------------------------------------------
1 | import Drawable from '../drawable';
2 |
3 | /**
4 | * A TexturedDrawable is a Drawable with a specific texture
5 | *
6 | * @param {String} programName Program internal name
7 | * @param {String} meshName Mesh internal name
8 | * @param {String} textureName Texture internal name
9 | */
10 | class TexturedDrawable extends Drawable {
11 | constructor(programName, meshName, textureName) {
12 | super(programName, meshName);
13 | this.textureName = textureName;
14 | this.texture = null;
15 | }
16 |
17 | /**
18 | * Draw the textured object
19 | *
20 | * @return {void}
21 | */
22 | draw() {
23 | if(this.ready) {
24 | this.texture.use(0);
25 | this.uniforms.u_texture = 0;
26 | super.draw();
27 | }
28 | }
29 |
30 | _loadAssets(manager) {
31 | let promises = super._loadAssets(manager);
32 | promises.push(
33 | manager.loadTexture(this.textureName).then((texture) => {
34 | this.texture = texture;
35 | }).catch((err) => {
36 | console.warn('missing texture ' + this.textureName); // eslint-disable-line no-console
37 | return Promise.reject(err);
38 | })
39 | );
40 | return promises;
41 | }
42 | }
43 |
44 | export default TexturedDrawable;
45 |
--------------------------------------------------------------------------------
/src/drawable/world.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import GlowrampDrawable from './glowramp';
3 | import BicoloredDrawable from './bicolored';
4 | import ShieldEffectDrawable from './shield-effect';
5 | import OrnamentDrawable from './ornament';
6 |
7 | /**
8 | * Various world drawables
9 | *
10 | * Includes Portal, ShieldEffect, waypoints, resonators, and artifact glows
11 | * @type {Object}
12 | */
13 | var World = {};
14 |
15 | var meshes = Constants.Mesh.World;
16 | var textures = Constants.Texture;
17 |
18 | function makeGlowramp(mesh, texture) {
19 | class glowrampbase extends GlowrampDrawable {
20 | constructor() {
21 | super(mesh, texture);
22 | }
23 | }
24 |
25 | return glowrampbase;
26 | }
27 |
28 | function makeBicolored(mesh, texture) {
29 | class bicoloredbase extends BicoloredDrawable {
30 | constructor() {
31 | super(mesh, texture);
32 | }
33 | }
34 |
35 | return bicoloredbase;
36 | }
37 |
38 | function makeShieldEffect(mesh, texture) {
39 | class shieldeffectbase extends ShieldEffectDrawable {
40 | constructor() {
41 | super(mesh, texture);
42 | }
43 | }
44 |
45 | return shieldeffectbase;
46 | }
47 |
48 | function makeOrnament(mesh, texture) {
49 | class ornamentbase extends OrnamentDrawable {
50 | constructor() {
51 | super(mesh, texture);
52 | }
53 | }
54 |
55 | return ornamentbase;
56 | }
57 |
58 | World.Portal = makeGlowramp(meshes.Portal, textures.Glowramp);
59 | World.Waypoint = makeGlowramp(meshes.Waypoint, textures.Waypoint);
60 | World.ArtifactsRedGlow = makeGlowramp(meshes.ArtifactsRedGlow, textures.ColorGlow);
61 | World.ArtifactsGreenGlow = makeGlowramp(meshes.ArtifactsGreenGlow, textures.ColorGlow);
62 | World.ArtifactsPurpleGlow = makeGlowramp(meshes.ArtifactsPurpleGlow, textures.ColorGlow);
63 | World.ArtifactsTargetGlow = makeGlowramp(meshes.ArtifactsTargetGlow, textures.TargetGlow);
64 |
65 | World.Shield = makeShieldEffect(meshes.Shield, textures.ShieldEffect);
66 | World.Resonator = makeBicolored(meshes.Resonator, textures.FlipCard);
67 |
68 | World.OrnamentMeetupPoint = makeOrnament(meshes.OrnamentMeetupPoint, textures.OrnamentMeetupPoint);
69 | World.OrnamentFinishPoint = makeOrnament(meshes.OrnamentFinishPoint, textures.OrnamentFinishPoint);
70 | World.OrnamentCluster = makeOrnament(meshes.OrnamentCluster, textures.OrnamentCluster);
71 | World.OrnamentVolatile = makeOrnament(meshes.OrnamentVolatile, textures.OrnamentVolatile);
72 |
73 | export default World;
74 |
--------------------------------------------------------------------------------
/src/drawable/xm.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import TexturedDrawable from './textured';
3 | import { vec4 } from 'gl-matrix';
4 |
5 |
6 | const PROGRAM = Constants.Program.Xm;
7 | const defaultTeamColor = vec4.clone(Constants.xmColors.coreGlow);
8 | const defaultAltColor = vec4.clone(Constants.xmColors.coreGlowAlt);
9 |
10 | /**
11 | * An XmDrawable is a drawable representing the animate "xm core" of inventory items
12 | *
13 | * @param {String} meshName Mesh internal name
14 | * @param {String} textureName Texture internal name
15 | * @param {vec4} teamColor Color of the xm glow.
16 | * @return {void}
17 | */
18 | class XmDrawable extends TexturedDrawable {
19 |
20 | constructor(meshName, textureName, teamColor) {
21 | super(PROGRAM, meshName, textureName);
22 | this.uniforms.u_elapsedTime = 0;
23 | this.uniforms.u_teamColor = vec4.clone(teamColor || defaultTeamColor);
24 | this.uniforms.u_altColor = vec4.clone(defaultAltColor);
25 | }
26 |
27 | /**
28 | * Animates the xm core
29 | * @param {Number} delta Time since last frame
30 | * @return {Boolean} Returns true to continue the animation.
31 | */
32 | updateTime(delta) {
33 | var ret = super.updateTime(delta);
34 | this.uniforms.u_elapsedTime = ((this.elapsed / 1000) % 300.0) * 0.1;
35 | return ret;
36 | }
37 | }
38 |
39 | export default XmDrawable;
40 |
--------------------------------------------------------------------------------
/src/engine.js:
--------------------------------------------------------------------------------
1 | import AssetManager from './asset-manager';
2 | import ObjectRenderer from './renderer/object';
3 | import { resetGL } from './utils';
4 | import World from './drawable/world';
5 | import Resource from './drawable/resource';
6 | import Inventory from './drawable/inventory';
7 | import InventoryItems from './entity/inventory';
8 | import PortalEntity from './entity/portal';
9 | import Camera from './camera';
10 | import { vec3, mat4 } from 'gl-matrix';
11 |
12 | /**
13 | * The Engine provides nearly all the mechanics for actually drawing things to a canvas.
14 | *
15 | * Also includes a few simple functions for demoing various entities/drawables. This
16 | * will probably go away in a future release.
17 | *
18 | * @param {HTMLCanvas} canvas A Canvas element
19 | * @param {Object} assets A manifest to pass to the internal AssetManager
20 | * @see AssetManager
21 | * @param {Boolean} enableSnapshots If set to true, the canvas will preserve its drawing
22 | * buffer, to allow for accurate .toDataURL calls.
23 | * This will have a performance impact.
24 | */
25 | class Engine {
26 |
27 | constructor(canvas, assets, enableSnapshots) {
28 | this.canvas = canvas;
29 | var opt = {};
30 | if(enableSnapshots) {
31 | opt.preserveDrawingBuffer = true;
32 | }
33 | this.canScreenshot = enableSnapshots && !!canvas.toBlob;
34 | var gl = canvas.getContext('webgl', opt) || canvas.getContext('experimental-webgl', opt);
35 | if(!gl)
36 | {
37 | throw 'Could not initialize webgl';
38 | }
39 | gl.clearColor(0.0, 0.0, 0.0, 1.0);
40 | this.gl = gl;
41 | this.camera = new Camera(canvas.width, canvas.height);
42 | this.camera.setPosition(
43 | vec3.fromValues(0.0, 20.0, 25.0)
44 | ).lookAt(
45 | vec3.fromValues(0.0, 10.0, 0.0)
46 | );
47 |
48 | this.assetManager = new AssetManager(this.gl, assets);
49 | this.objectRenderer = new ObjectRenderer(this.gl, this.assetManager);
50 | this._start = this._last = null;
51 | this._frame = null;
52 | this.scale = 1;
53 | this.resize();
54 | }
55 |
56 | /**
57 | * Resize the canvas and viewport to new dimensions.
58 | * Uses the canvas' clientWidth and clientHeight to determine viewport size,
59 | * if not provided.
60 | *
61 | * @chainable
62 | * @param {Number} width (optional) width
63 | * @param {Number} height (optional) height
64 | * @return {this} Returns `this`
65 | */
66 | resize(width, height) {
67 | let devicePixels = window.devicePixelRatio;
68 | if(!width) {
69 | width = this.canvas.clientWidth;
70 | }
71 | if (!height) {
72 | height = this.canvas.clientHeight;
73 | }
74 | let targetWidth = Math.floor(width * this.scale * devicePixels);
75 | let targetHeight = Math.floor(height * this.scale * devicePixels);
76 | this.canvas.width = targetWidth;
77 | this.canvas.height = targetHeight;
78 | this.camera.setDimensions(targetWidth, targetHeight);
79 | this.gl.viewport(0, 0, targetWidth, targetHeight);
80 | return this.updateView();
81 | }
82 |
83 | /**
84 | * Sets the scaling factor for the canvas.
85 | *
86 | * @chainable
87 | * @param {Number} factor The scale factor
88 | * @return {this} Returns `this`
89 | */
90 | rescale(factor) {
91 | this.scale = factor;
92 | return this.resize();
93 | }
94 |
95 | /**
96 | * Updates the current drawing viewport to the canvas' current dimensions
97 | *
98 | * @chainable
99 | * @return {this} Returns `this`
100 | */
101 | updateView() {
102 | this.objectRenderer.updateView(this.camera);
103 | return this;
104 | }
105 |
106 | /**
107 | * Stops the render loop, if it's running.
108 | *
109 | * @chainable
110 | * @return {this} Returns `this`
111 | */
112 | stop() {
113 | this._last = this._start = null;
114 | if(this._frame) {
115 | window.cancelAnimationFrame(this._frame);
116 | }
117 | return this;
118 | }
119 |
120 | /**
121 | * Adds one of each inventory item, and a portal, to the scene
122 | * @return {void}
123 | */
124 | demoEntities() {
125 | var x = -5, y = 0, z = 4;
126 | var i, item;
127 | for(i in InventoryItems) {
128 | item = new InventoryItems[i](this);
129 | if(item) {
130 | item.translate(vec3.fromValues(x, y, z));
131 | x++;
132 | if(x > 5) {
133 | x = -5;
134 | z--;
135 | }
136 | }
137 | }
138 | var portal = new PortalEntity(this);
139 | portal.translate(vec3.fromValues(x, y, z));
140 | }
141 |
142 | /**
143 | * Adds one of each drawable to the scene
144 | * @return {void}
145 | */
146 | demo() {
147 | var x = -5, y = 0, z = 4;
148 | var i, item;
149 | for(i in Inventory) {
150 | item = new Inventory[i]();
151 | if(item) {
152 | mat4.translate(item.world, item.world, vec3.fromValues(x, y, z));
153 | x++;
154 | if(x > 5) {
155 | x = -5;
156 | z--;
157 | }
158 | this.objectRenderer.addDrawable(item);
159 | }
160 | }
161 |
162 | for(i in Resource) {
163 | item = new Resource[i]();
164 | if(item) {
165 | mat4.translate(item.world, item.world, vec3.fromValues(x, y, z));
166 | x++;
167 | if(x > 5) {
168 | x = -5;
169 | z--;
170 | }
171 | this.objectRenderer.addDrawable(item);
172 | }
173 | }
174 |
175 | for(i in World) {
176 | item = new World[i]();
177 | if(item) {
178 | mat4.translate(item.world, item.world, vec3.fromValues(x, y, z));
179 | x++;
180 | if(x > 5) {
181 | x = -5;
182 | z--;
183 | }
184 | this.objectRenderer.addDrawable(item);
185 | }
186 | }
187 | }
188 |
189 | /**
190 | * Draw a single frame, with a specified time since last draw
191 | * @param {Number} delta Time since last render
192 | * @return {void}
193 | */
194 | draw(delta) {
195 | var gl = this.gl;
196 | // default setup stuff:
197 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
198 | resetGL(gl);
199 | //gl.enable(gl.BLEND);
200 | //gl.depthMask(false);
201 |
202 | // render passes:
203 | this.objectRenderer.render();
204 |
205 | // run animations
206 | this.objectRenderer.updateTime(delta);
207 | this.camera.updateTime(delta);
208 | }
209 |
210 | /**
211 | * Start the render loop.
212 | * @param {Number} tick Time since last tick (optional)
213 | * @return {void}
214 | */
215 | render(tick) {
216 | var delta = 0;
217 | if(!this._start) {
218 | this._start = tick;
219 | this._last = tick;
220 | }
221 | else if (tick) {
222 | delta = tick - this._last;
223 | this._last = tick;
224 | }
225 | this.draw(delta);
226 | // queue up next frame:
227 | this._frame = window.requestAnimationFrame(this.render.bind(this));
228 | }
229 |
230 | /**
231 | * Preloads all assets
232 | * @param {Function} callback Callback to invoke on completion
233 | * @return {void}
234 | */
235 | preload() {
236 | return this.assetManager.loadAll();
237 | }
238 |
239 | /**
240 | * Captures a screenshot, if enabled
241 | *
242 | * @param {String} mimeType The mime type of the image
243 | * @param {Number} quality Quality, if applicable (applies to image/jpeg)
244 | * @return {Promise} A promise that resolves when the screenshot is complete
245 | */
246 | capture(mimeType, quality) {
247 | if (this.canScreenshot) {
248 | this.stop();
249 | let promise = new Promise((resolve, reject) => {
250 | try {
251 | this.canvas.toBlob((blob) => {
252 | resolve(blob);
253 | }, mimeType, quality);
254 | } catch (e) {
255 | reject(e);
256 | }
257 | });
258 | // promise.then(() => {
259 | // this.render();
260 | // }, () => {
261 | // this.render();
262 | // });
263 | return promise;
264 | } else {
265 | return Promise.reject(new Error('Screenshots not enabled. Initialize engine with `enableSnapshots` and ensure `canvas.toBlob` is supported by your browser.'));
266 | }
267 | }
268 | }
269 |
270 | export default Engine;
271 |
--------------------------------------------------------------------------------
/src/entity.js:
--------------------------------------------------------------------------------
1 | import { mat4 } from 'gl-matrix';
2 |
3 | // TODO: Deprecate
4 | class Entity {
5 | constructor(engine) {
6 | this.drawables = {};
7 | this.transform = mat4.create();
8 | this.engine = engine;
9 | }
10 |
11 | addDrawable(name, drawable) {
12 | // add dispose if this already exists.
13 | this.removeDrawable(name);
14 | this.drawables[name] = drawable;
15 | this.engine.objectRenderer.addDrawable(drawable);
16 | }
17 |
18 | removeDrawable(name, destroy) {
19 | // dispose stuffs.
20 | if(this.drawables[name]) {
21 | this.engine.objectRenderer.removeDrawable(this.drawables[name], destroy);
22 | }
23 | }
24 |
25 | applyTransform() {
26 | for(var i in this.drawables)
27 | {
28 | this.drawables[i].updateWorld(this.transform);
29 | }
30 | }
31 |
32 | translate(vec) {
33 | mat4.translate(this.transform, this.transform, vec);
34 | this.applyTransform();
35 | }
36 |
37 | rotate(quat) {
38 | var rotate = mat4.create();
39 | mat4.fromQuat(rotate, quat);
40 | mat4.multiply(this.transform, this.transform, rotate);
41 | this.applyTransform();
42 | }
43 |
44 | setAnimation(animate) {
45 | for(var i in this.drawables)
46 | {
47 | this.drawables[i].onUpdate = animate;
48 | }
49 | }
50 | }
51 |
52 | export default Entity;
53 |
--------------------------------------------------------------------------------
/src/entity/inventory.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import Entity from '../entity';
3 | import Inventory from '../drawable/inventory';
4 | import { vec4 } from 'gl-matrix';
5 |
6 | // TODO: Deprecate in favor of a proper scene graph
7 | var InventoryItems = {};
8 |
9 | var simple = {
10 | Xmp: 'L8',
11 | Ultrastrike: 'L8',
12 | ResShield: 'VERY_RARE',
13 | PowerCube: 'L8',
14 | LinkAmp: 'EXTREMELY_RARE',
15 | HeatSink: 'VERY_RARE',
16 | MultiHack: 'VERY_RARE',
17 | ForceAmp: 'RARE',
18 | Turret: 'RARE',
19 | Resonator: 'L8',
20 | Capsule: 'RARE'
21 | };
22 |
23 | export function createItemEntity(name, color) {
24 |
25 | class entitybase extends Entity {
26 | constructor(engine) {
27 | super(engine);
28 | this.addDrawable(name, new Inventory[name]());
29 | this.addDrawable(name + 'Xm', new Inventory[name + 'Xm']());
30 | this.drawables[name].uniforms.u_color0 = vec4.clone(color);
31 | }
32 | }
33 |
34 | return entitybase;
35 | }
36 |
37 | for(var i in simple) {
38 | InventoryItems[i] = createItemEntity(i, Constants.qualityColors[simple[i]]);
39 | }
40 |
41 | class FlipCardAda extends Entity {
42 | constructor(engine) {
43 | super(engine);
44 | this.addDrawable('FlipCardAda', new Inventory.FlipCardAda());
45 | this.addDrawable('FlipCardXm', new Inventory.FlipCardXm());
46 | this.drawables.FlipCardXm.uniforms.u_teamColor = vec4.clone(Constants.teamColors.RESISTANCE);
47 | this.drawables.FlipCardAda.uniforms.u_color1 = vec4.clone(Constants.teamColors.RESISTANCE);
48 | this.drawables.FlipCardAda.uniforms.u_color0 = vec4.clone(Constants.qualityColors.VERY_RARE);
49 | }
50 | }
51 |
52 | InventoryItems.FlipCardAda = FlipCardAda;
53 |
54 | class FlipCardJarvis extends Entity {
55 | constructor(engine) {
56 | super(engine);
57 | this.addDrawable('FlipCardJarvis', new Inventory.FlipCardJarvis());
58 | this.addDrawable('FlipCardXm', new Inventory.FlipCardXm());
59 | this.drawables.FlipCardXm.uniforms.u_teamColor = vec4.clone(Constants.teamColors.ENLIGHTENED);
60 | this.drawables.FlipCardJarvis.uniforms.u_color1 = vec4.clone(Constants.teamColors.ENLIGHTENED);
61 | this.drawables.FlipCardJarvis.uniforms.u_color0 = vec4.clone(Constants.qualityColors.VERY_RARE);
62 | }
63 | }
64 |
65 | InventoryItems.FlipCardJarvis = FlipCardJarvis;
66 |
67 | class ExtraShield extends Entity {
68 | constructor(engine) {
69 | super(engine);
70 | this.addDrawable('ExtraShield', new Inventory.ExtraShield());
71 | this.addDrawable('ResShieldXm', new Inventory.ResShieldXm());
72 | this.drawables.ExtraShield.uniforms.u_color0 = vec4.clone(Constants.qualityColors.VERY_RARE);
73 | }
74 | }
75 |
76 | InventoryItems.ExtraShield = ExtraShield;
77 |
78 | class InterestCapsule extends Entity {
79 | constructor(engine) {
80 | super(engine);
81 | this.addDrawable('InterestCapsule', new Inventory.InterestCapsule());
82 | this.addDrawable('CapsuleXm', new Inventory.CapsuleXm());
83 | this.drawables.InterestCapsule.uniforms.u_color0 = vec4.clone(Constants.qualityColors.VERY_RARE);
84 | }
85 | }
86 |
87 | InventoryItems.InterestCapsule = InterestCapsule;
88 |
89 | class PortalKeyResourceUnit extends Entity {
90 | constructor(engine){
91 | super(engine);
92 | this.addDrawable('PortalKey', new Inventory.PortalKeyResourceUnit());
93 | }
94 | }
95 |
96 | InventoryItems.PortalKeyResourceUnit = PortalKeyResourceUnit;
97 |
98 | class KeyCapsule extends Entity {
99 | constructor(engine) {
100 | super(engine);
101 | this.addDrawable('KeyCapsule', new Inventory.KeyCapsule());
102 | this.addDrawable('KeyCapsuleXm', new Inventory.KeyCapsuleXm());
103 | this.drawables.KeyCapsule.uniforms.u_color0 = vec4.clone(Constants.keyCapsuleColors.blue[0]);
104 | this.drawables.KeyCapsule.uniforms.u_color1 = vec4.clone(Constants.keyCapsuleColors.blue[1]);
105 | this.drawables.KeyCapsuleXm.uniforms.u_teamColor = vec4.clone(Constants.xmColors.coreGlowChaotic);
106 | this.drawables.KeyCapsuleXm.uniforms.u_altColor = vec4.clone(Constants.xmColors.coreGlowChaoticAlt);
107 | }
108 | }
109 |
110 | InventoryItems.KeyCapsule = KeyCapsule;
111 |
112 | export default InventoryItems;
113 |
--------------------------------------------------------------------------------
/src/entity/portal.js:
--------------------------------------------------------------------------------
1 | import Constants from '../constants';
2 | import Entity from '../entity';
3 | import World from '../drawable/world';
4 | import ResonatorLink from '../drawable/resonator-link';
5 | import { vec3, vec4, mat4 } from 'gl-matrix';
6 |
7 |
8 | // TODO: Deprecate in favor of a proper scene graph
9 | class PortalEntity extends Entity {
10 | constructor(engine) {
11 | super(engine);
12 | this.addDrawable('Portal', new World.Portal());
13 | // why 6? I dunno, ask Niantic
14 | mat4.scale(this.drawables.Portal.local, this.drawables.Portal.local, vec3.fromValues(6, 6, 6));
15 | this.setColor(vec4.clone(Constants.teamColors.LOKI));
16 | }
17 |
18 | setColor(color) {
19 | this.color = vec4.clone(color);
20 | this.drawables.Portal.uniforms.u_baseColor = this.color;
21 | if(this.drawables.Shield) {
22 | this.drawables.Shield.uniforms.u_color = this.color;
23 | }
24 | if(this.drawables.ArtifactsGreenGlow) {
25 | this.drawables.ArtifactsGreenGlow.u_baseColor = this.color;
26 | }
27 | /*for(var i = 0; i < 8; i++) {
28 | this._redrawLink(i);sd
29 | }*/
30 | }
31 |
32 | addResonator(level, slot, range, percent) {
33 | if(percent === undefined) {
34 | percent = 1.0;
35 | }
36 | if(+slot < 0 || +slot > 8) {
37 | throw new Error('slot out of bounds for resonator');
38 | }
39 | if(!(level in Constants.qualityColors)) {
40 | throw new Error('level must be one of ' + Object.keys(Constants.qualityColors).join(' '));
41 | }
42 | range = range === undefined ? 40 : range;
43 | var resonatorName = 'Resonator' + (+slot);
44 | var linkName = 'Link' + (+slot);
45 | var theta = slot / 8 * 2 * Math.PI;
46 | var resonator = new World.Resonator();
47 | var x = range * Math.cos(theta);
48 | var y = range * Math.sin(theta);
49 | var link = new ResonatorLink(
50 | [0,0],
51 | slot,
52 | range,
53 | vec4.clone(this.color),
54 | 1.0
55 | );
56 | resonator.uniforms.u_color0 = vec4.clone(Constants.qualityColors[level]);
57 | resonator.world = mat4.clone(this.drawables.Portal.local);
58 | //link.local = mat4.clone(this.drawables.Portal.local);
59 | mat4.translate(
60 | resonator.world,
61 | resonator.world,
62 | vec3.fromValues(x / 6, 0, y / 6)
63 | );
64 | resonator.updateMatrix();
65 | link.updateMatrix();
66 | // keep the portal sorted last (this is a terrible way of doing this.)
67 | this.addDrawable(linkName, link);
68 | this.addDrawable(resonatorName, resonator);
69 | this.addDrawable('Portal', this.drawables.Portal);
70 | }
71 |
72 | removeResonator(slot) {
73 | if(+slot < 0 || +slot > 8) {
74 | throw new Error('slot out of bounds for resonator');
75 | }
76 | var name = 'Resonator' + (+slot);
77 | var resonator = this.drawables[name] || null;
78 | if(resonator) {
79 | this.removeDrawable(name);
80 | this._removeResonatorLink(slot);
81 | this.addDrawable('Portal', this.drawables.Portal);
82 | }
83 | }
84 |
85 | addShield() {
86 | if(!('Shield' in this.drawables)) {
87 | this.addDrawable('Shield', new World.Shield());
88 | // why 12? I don't know.
89 | mat4.scale(this.drawables.Shield.local, this.drawables.Shield.local, vec3.fromValues(12, 12, 12));
90 | this.drawables.Shield.updateMatrix();
91 | }
92 | this.drawables.Shield.uniforms.u_color = this.color;
93 | this.applyTransform();
94 | }
95 |
96 | addArtifact(artifact, name) {
97 | var rotate = function(delta/*, elapsed*/) {
98 | mat4.rotateY(this.model, this.model, delta / 1000);
99 | this.updateMatrix();
100 | return true;
101 | };
102 | if(!(name in this.drawables)) {
103 | this.addDrawable(name, artifact);
104 | }
105 | this.drawables[name].onUpdate = rotate;
106 | this.applyTransform();
107 | }
108 |
109 | addGlowMarker(name, color) {
110 | var n = 'Artifacts' + name + 'Glow';
111 | if(!(n in this.drawables)) {
112 | this.addDrawable(n, new World[n]());
113 | }
114 | this.drawables[n].uniforms.u_baseColor = vec4.clone(color);
115 | }
116 | }
117 |
118 | export default PortalEntity;
119 |
--------------------------------------------------------------------------------
/src/geometry/field.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var FieldGeometry = (function(){
3 |
4 | // 5 sets of 4 points, breaking the link into 4 pieces, each providing 4 faces
5 | var fillChunk = function(vert, pos, col, index, v1, f1, f2, v2)
6 | {
7 | var off = index * 4;
8 | vert[index * 3 + 0] = v1.x;
9 | vert[index * 3 + 1] = 0;
10 | vert[index * 3 + 2] = v1.y;
11 | pos[off + 0] = v1.x;
12 | pos[off + 1] = v1.y;
13 | pos[off + 2] = f1;
14 | pos[off + 3] = f2;
15 | col[off + 0] = v2.x;
16 | col[off + 1] = v2.y;
17 | col[off + 2] = v2.z;
18 | col[off + 3] = v2.w;
19 | return index + 1;
20 | };
21 |
22 | var fillVectors = function(vert, pos, col, index, v1, f1, v2, f2, v3, f3, v4)
23 | {
24 | return fillChunk(vert, pos, col, fillChunk(vert, pos, col, fillChunk(vert, pos, col, index, v1, 0, f1, v4), v2, 0, f2, v4), v3, ((v2.x - v1.x) * (v1.y - v3.y) - (v2.y - v1.y) * (v1.x - v3.x)) / v1.distanceTo(v2), f3, v4);
25 | };
26 |
27 | var fieldgeometry = function(options)
28 | {
29 | options = options || {};
30 | options.transparent = true;
31 | Geometry.call(this, options);
32 | this.position = new THREE.BufferAttribute(new Float32Array(), 3);
33 | this.index = new THREE.BufferAttribute(new Uint16Array(), 1);
34 | this.a_position = new THREE.BufferAttribute(new Float32Array(), 4);
35 | this.a_color = new THREE.BufferAttribute(new Float32Array(), 4);
36 | this.geometry.addAttribute('position', this.position);
37 | this.geometry.addAttribute('index', this.index);
38 | this.geometry.addAttribute('a_position', this.a_position);
39 | this.geometry.addAttribute('a_color', this.a_color);
40 | this.attributes = {
41 | "a_position": { type: "v4", values: null },
42 | "a_color": { type: "v4", values: null }
43 | };
44 | this.fieldCount = 0;
45 | this.lastOffset = 0;
46 | };
47 | inherits(fieldgeometry, Geometry);
48 |
49 | fieldgeometry.prototype.extendBuffer = function(attribute, count)
50 | {
51 | var buf = new Float32Array(attribute.length + (count * attribute.itemSize));
52 | buf.set(attribute.array);
53 | attribute.array = buf;
54 | return buf;
55 | };
56 |
57 | fieldgeometry.prototype.addField = function(A, B, C, color, duration /* ? */)
58 | {
59 | duration = 1.0;
60 | var f1 = 1.1,
61 | f2 = A.percent,
62 | f3 = B.percent,
63 | f4 = C.percent;
64 | var vert = this.extendBuffer(this.position, 9),
65 | pos = this.extendBuffer(this.a_position, 9),
66 | col = this.extendBuffer(this.a_color, 9);
67 | if(duration < 1.0)
68 | {
69 | var f6 = Math.max(0.0, duration);
70 | f1 = 1.1 * f6;
71 | var f7 = f6 * f6;
72 | f2 *= f7;
73 | f3 *= f7;
74 | f4 *= f7;
75 | }
76 | var aVec = new THREE.Vector2(A.x, A.y),
77 | bVec = new THREE.Vector2(B.x, B.y),
78 | cVec = new THREE.Vector2(C.x, C.y);
79 | var center = aVec.clone().add(bVec).add(cVec).multiplyScalar(0.333333333333);
80 | var start = this.lastOffset;
81 | this.lastOffset = fillVectors(vert, pos, col, this.lastOffset, aVec, f2, bVec, f3, center, f1, color);
82 | this.lastOffset = fillVectors(vert, pos, col, this.lastOffset, bVec, f3, cVec, f4, center, f1, color);
83 | this.lastOffset = fillVectors(vert, pos, col, this.lastOffset, cVec, f4, aVec, f2, center, f1, color);
84 | var ind = new Uint16Array((this.fieldCount + 1) * 9);
85 | ind.set(this.index.array);
86 | for(var i = 0; i < 9; i++)
87 | {
88 | ind[start + i] = start + i;
89 | }
90 | this.index.array = ind;
91 | this.index.needsUpdate = true;
92 | this.position.needsUpdate = true;
93 | this.a_position.needsUpdate = true;
94 | this.a_color.needsUpdate = true;
95 | this.geometry.needsUpdate = true;
96 | this.fieldCount++;
97 | return this.fieldCount;
98 | };
99 |
100 | return fieldgeometry;
101 | }());
102 |
103 | imv.Geometries = imv.Geometries || {};
104 | imv.Geometries.Field = FieldGeometry;
105 |
--------------------------------------------------------------------------------
/src/geometry/parametric.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var ParametricGeometry = (function(){
3 |
4 | // basic template for our parametric function.
5 | // takes u, v;
6 | // gives array of proper length (options.paramSize)
7 | var linear = function(u, v) {
8 | // x, y, z
9 | return [u, 0, v];
10 | };
11 |
12 | var parametric = function(func, options)
13 | {
14 | Geometry.call(this, options);
15 | func = func || linear;
16 | options = options || {};
17 | var params = {
18 | slices: 1,
19 | paramSize: 3
20 | };
21 | this.options = setParams(params, options);
22 | var t = func(0, 0);
23 | if(!t.length || t.length != this.options.paramSize)
24 | {
25 | throw 'Parametric function returned invalid results, must be array of length ' + this.options.paramSize;
26 | }
27 | this.attributes = {
28 | 'a_position': { type: 'v' + this.options.paramSize, values: null },
29 | 'a_texCoord0': { type: 'v2', values: null }
30 | };
31 | var n = this.options.slices, l = this.options.paramSize;
32 | // (n + 1)^2 points to define n x n squares in u,v space
33 | var len = (n + 1) * (n + 1);
34 | var position = new Float32Array(len * 3);
35 | var a_position = new Float32Array(len * l);
36 | var a_texCoord0 = new Float32Array(len * 2);
37 | // number of square subdivisions is n^2, 3 indices per face, 2 faces per square
38 | var faces = new Uint16Array(n * n * 3 * 2);
39 | var f = 0;
40 | for(var i = 0; i <= n; i++)
41 | {
42 | for(var j = 0; j <= n; j++)
43 | {
44 | var u = i / n, v = j / n;
45 | var slice = func(u, v);
46 | var idx = i * (n + 1) + j;
47 | a_texCoord0[idx * 2] = u;
48 | a_texCoord0[idx * 2 + 1] = v;
49 | for(var k = 0; k < l; k++)
50 | {
51 | a_position[(idx * l) + k] = slice[k];
52 | if(k < 3)
53 | {
54 | position[(idx * l) + k] = slice[k];
55 | }
56 | }
57 | if(l < 3)
58 | {
59 | position[(idx * 3) + 2] = 0.0;
60 | }
61 | if(i < n && j < n)
62 | {
63 | faces[f++] = idx;
64 | faces[f++] = idx + 1;
65 | faces[f++] = idx + n + 1;
66 | faces[f++] = idx + n + 1;
67 | faces[f++] = idx + 1;
68 | faces[f++] = idx + n + 2;
69 | }
70 | }
71 | }
72 | this.geometry.addAttribute('a_position', new THREE.BufferAttribute(a_position, l));
73 | this.geometry.addAttribute('a_texCoord0', new THREE.BufferAttribute(a_texCoord0, 2));
74 | this.geometry.addAttribute('position', new THREE.BufferAttribute(position, 3));
75 | this.geometry.addAttribute('index', new THREE.BufferAttribute(faces, 1));
76 | };
77 | inherits(parametric, Geometry);
78 |
79 | return parametric;
80 | }());
81 |
82 | imv.Geometries = imv.Geometries || {};
83 | imv.Geometries.Parametric = ParametricGeometry;
84 |
--------------------------------------------------------------------------------
/src/geometry/particle-portal.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var ParticlePortalsGeometry = (function(){
3 |
4 | var MAX_SYSTEMS = 40,
5 | NUM_PARTICLES = 96,
6 | NUM_INDICES_PER_PARTICLE = 4,
7 | NUM_INDICES_PER_FACE = 6;
8 |
9 | var U = [0.0, 0.0, 1.0, 1.0];
10 | var V = [1.0, 0.0, 1.0, 0.0];
11 |
12 | var extendBuffer = function(attribute, count)
13 | {
14 | var buf = new Float32Array(attribute.length + (count * attribute.itemSize));
15 | buf.set(attribute.array);
16 | attribute.array = buf;
17 | return buf;
18 | };
19 |
20 | var particlePortalsGeometry = function() {
21 | Geometry.call(this, {transparent: true});
22 | this.count = 0;
23 | this.attributes = {
24 | 'a_position': { type: 'v3', values: null },
25 | 'a_texCoord0': { type: 'v2', values: null },
26 | 'a_scale': { type: "f", values: null },
27 | 'a_speed': { type: "f", values: null },
28 | 'a_portalIndex': { type: "f", values: null },
29 | 'a_index': { type: "f", values: null}
30 | };
31 | this.position = new THREE.BufferAttribute(new Float32Array(), 3);
32 | this.index = new THREE.BufferAttribute(new Uint16Array(), 1);
33 | this.a_position = new THREE.BufferAttribute(new Float32Array(), 3);
34 | this.a_texCoord0 = new THREE.BufferAttribute(new Float32Array(), 2);
35 | this.a_scale = new THREE.BufferAttribute(new Float32Array(), 1);
36 | this.a_speed = new THREE.BufferAttribute(new Float32Array(), 1);
37 | this.a_portalIndex = new THREE.BufferAttribute(new Float32Array(), 1);
38 | this.a_index = new THREE.BufferAttribute(new Float32Array(), 1);
39 | this.seeds = [];
40 | for(var i = 0; i < NUM_PARTICLES; i++)
41 | {
42 | this.seeds.push({
43 | x: Math.random() - 0.5,
44 | y: 0.4 * Math.random() - 0.2,
45 | z: Math.random() - 0.5,
46 | a_scale: 10.0 * (0.1 + 0.9 * Math.random()),
47 | a_speed: 6.0 * (0.5 + 0.5 * Math.random())
48 | });
49 | }
50 | this.geometry.addAttribute('a_position', this.a_position);
51 | this.geometry.addAttribute('a_texCoord0', this.a_texCoord0);
52 | this.geometry.addAttribute('a_scale', this.a_scale);
53 | this.geometry.addAttribute('a_speed', this.a_speed);
54 | this.geometry.addAttribute('a_portalIndex', this.a_portalIndex);
55 | this.geometry.addAttribute('a_index', this.a_index);
56 | this.geometry.addAttribute('position', this.position);
57 | this.geometry.addAttribute('index', this.index);
58 | };
59 | inherits(particlePortalsGeometry, Geometry);
60 |
61 | particlePortalsGeometry.prototype.addSystem = function() {
62 | if(this.count + 1 >= MAX_SYSTEMS)
63 | {
64 | throw 'This system is full';
65 | }
66 | var n = NUM_PARTICLES * NUM_INDICES_PER_PARTICLE;
67 | var a_position = extendBuffer(this.a_position, n);
68 | var a_texCoord0 = extendBuffer(this.a_texCoord0, n);
69 | var a_scale = extendBuffer(this.a_scale, n);
70 | var a_speed = extendBuffer(this.a_speed, n);
71 | var a_portalIndex = extendBuffer(this.a_portalIndex, n);
72 | var a_index = extendBuffer(this.a_index, n);
73 | var position = extendBuffer(this.position, n);
74 | var c = this.count++;
75 | var idx = c * n, seed, i, j;
76 | for(i = 0; i < NUM_PARTICLES; i++)
77 | {
78 | seed = this.seeds[i];
79 | for(j = 0; j < NUM_INDICES_PER_PARTICLE; j++)
80 | {
81 | position[idx * 3 + 0] = 0;//seed.x;
82 | position[idx * 3 + 1] = 0;//seed.y;
83 | position[idx * 3 + 2] = 0;//seed.z;
84 | a_position[idx * 3 + 0] = seed.x;
85 | a_position[idx * 3 + 1] = seed.y;
86 | a_position[idx * 3 + 1] = seed.z;
87 | a_texCoord0[idx * 2 + 0] = U[j];
88 | a_texCoord0[idx * 2 + 1] = V[j];
89 | a_scale[idx] = seed.a_scale;
90 | a_speed[idx] = seed.a_speed;
91 | a_portalIndex[idx] = c;
92 | a_index[idx] = i;
93 | idx++;
94 | }
95 | }
96 |
97 | var index = new Uint16Array((c + 1) * NUM_PARTICLES * NUM_INDICES_PER_FACE);
98 | index.set(this.index.array);
99 | var indices = [0, 1, 2, 1, 3, 2];
100 | idx = c * n;
101 | var f = c * NUM_PARTICLES * NUM_INDICES_PER_FACE;
102 | for(i = 0; i < NUM_PARTICLES; i++)
103 | {
104 | for(j = 0; j < NUM_INDICES_PER_FACE; j++)
105 | {
106 | index[f + j] = idx + indices[j];
107 | }
108 | f += 6;
109 | idx += 4;
110 | }
111 | this.index.array = index;
112 | this.position.needsUpdate = true;
113 | this.index.needsUpdate = true;
114 | this.a_position.needsUpdate = true;
115 | this.a_texCoord0.needsUpdate = true;
116 | this.a_scale.needsUpdate = true;
117 | this.a_speed.needsUpdate = true;
118 | this.a_portalIndex.needsUpdate = true;
119 | this.a_index.needsUpdate = true;
120 | this.geometry.needsUpdate = true;
121 | };
122 |
123 | return particlePortalsGeometry;
124 | }());
125 |
126 | imv.Geometries = imv.Geometries || {};
127 | imv.Geometries.ParticlePortals = ParticlePortalsGeometry;
128 |
--------------------------------------------------------------------------------
/src/gl-bound.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Base class for all things bound to a gl context.
3 | *
4 | * @param {context} gl A webgl context
5 | */
6 | class GLBound {
7 | constructor(gl) {
8 | this._gl = gl;
9 | }
10 | }
11 |
12 | export default GLBound;
13 |
--------------------------------------------------------------------------------
/src/gl/gl-attribute.js:
--------------------------------------------------------------------------------
1 | import GLBuffer from './gl-buffer';
2 |
3 | /**
4 | * A GLAttribute is a GLBuffer that represents vertex attributes
5 | *
6 | * @private
7 | * @extends {GLBuffer}
8 | * @chainable
9 | * @param {context} gl WebGLContext
10 | * @param {Array} attributes An array of VertexAttributes
11 | * @param {ArrayBuffer} values Values to fill the buffer with
12 | * @param {enum} usage Usage @see https://www.khronos.org/registry/webgl/specs/1.0/#5.14.5
13 | * @return {this} The new GLAttribute
14 | */
15 | class GLAttribute extends GLBuffer {
16 | constructor(gl, attributes, values, usage) {
17 | usage = usage || gl.STATIC_DRAW;
18 | super(gl, gl.ARRAY_BUFFER, usage);
19 | this.attributes = attributes;
20 | this.values = values;
21 | this.size = this.count = null;
22 | this._validate = false;
23 | this.size = 0;
24 | this.width = 0;
25 | for(var i = 0, a; i < this.attributes.length; i++)
26 | {
27 | a = this.attributes[i];
28 | this.size += 4 * a.size; // 4 because float is 4 bytes.
29 | this.width += a.size;
30 | }
31 | return this;
32 | }
33 |
34 | /**
35 | * Confirms that the underlying buffer's length is an even multiple
36 | * of total size of the attributes for the buffer
37 | *
38 | * Issues a warning if not.
39 | *
40 | * @return {void}
41 | */
42 | validate() {
43 | if(this._validate) {
44 | if(this.values.length % this.width !== 0)
45 | {
46 | console.warn('values array length is not an even multiple of the total size of the attributes'); // eslint-disable-line no-console
47 | }
48 | }
49 | }
50 |
51 | /**
52 | * Update the values in the buffer and pushes the buffer to the gpu
53 | *
54 | * @chainable
55 | * @param {ArrayBuffer} values New values to write to the buffer
56 | *
57 | * @return {this} Returns `this`
58 | */
59 | updateValues(values) {
60 | this.values = values;
61 | this.validate();
62 | return this.update();
63 | }
64 |
65 | /**
66 | * Given a set of program locations, set up the attribute pointers
67 | *
68 | * @chainable
69 | * @param {Object} locations Map of attribute names to program locations
70 | *
71 | * @return {this} Returns `this`
72 | */
73 | draw(locations) {
74 | var gl = this._gl;
75 | var a, s = 0;
76 | if(!this.glBuf) {
77 | this.update();
78 | } else {
79 | this.bindBuffer();
80 | }
81 | for(var i = 0; i < this.attributes.length; i++)
82 | {
83 | a = this.attributes[i];
84 | if(a.name in locations)
85 | {
86 | gl.enableVertexAttribArray(locations[a.name]);
87 | gl.vertexAttribPointer(locations[a.name], a.size, gl.FLOAT, false, this.size, s);
88 | }
89 | // I don't know if I should suppress this, but if I
90 | // don't, it generates one warning per frame.
91 | //console.warn('Program is missing attribute ' + a.name);
92 | s += 4 * a.size;
93 | }
94 | return this; //.unbindBuffer(); // maybe?
95 | }
96 |
97 | /**
98 | * Perform some operation on each set of values for some attribute
99 | *
100 | * @chainable
101 | * @param {Number} attributeIndex Index of the attribute to select
102 | * @param {Function} callback Callback
103 | *
104 | * @return {this} Returns `this`
105 | */
106 | eachAttribute(attributeIndex, callback) {
107 | var offset = 0, size, i;
108 | if(attributeIndex >= 0 && attributeIndex < this.attributes.length) {
109 | for(i = 0; i < attributeIndex; i++) {
110 | offset += this.attributes[i].size;
111 | }
112 | size = this.attributes[attributeIndex].size;
113 | for(i = offset; i < this.values.length; i += this.width) {
114 | callback(this.values.subarray(i, i + size));
115 | }
116 | }
117 | return this;
118 | }
119 | }
120 |
121 | export default GLAttribute;
122 |
--------------------------------------------------------------------------------
/src/gl/gl-buffer.js:
--------------------------------------------------------------------------------
1 | import GLBound from '../gl-bound';
2 |
3 | /**
4 | * A GLBuffer is a buffer of some sort that will be passed to the gpu
5 | *
6 | * @private
7 | * @extends {GLBound}
8 | * @chainable
9 | * @param {context} gl WebGL context
10 | * @param {enum} target gl target @see https://www.khronos.org/registry/webgl/specs/1.0/#5.14.5
11 | * @param {enum} usage gl usage @see https://www.khronos.org/registry/webgl/specs/1.0/#5.14.5
12 | * @return {this} the GLBuffer
13 | */
14 | class GLBuffer extends GLBound {
15 |
16 | constructor(gl, target, usage) {
17 | super(gl);
18 | this.target = target || gl.ARRAY_BUFFER; // probably shouldn't default this.
19 | this.usage = usage || gl.STATIC_DRAW;
20 | this.glBuf = null;
21 | this.values = null;
22 | return this;
23 | }
24 |
25 | /**
26 | * Binds the buffer to the gpu
27 | *
28 | * @chainable
29 | * @return {this} Returns `this`
30 | */
31 | bindBuffer() {
32 | if(!this.values) {
33 | throw new Error('trying to update a buffer with no values.');
34 | }
35 | if(!this.glBuf) {
36 | this.glBuf = this._gl.createBuffer();
37 | }
38 | this._gl.bindBuffer(this.target, this.glBuf);
39 | return this;
40 | }
41 |
42 | /**
43 | * Unbinds the buffer (NPI)
44 | *
45 | * @chainable
46 | * @return {this} Returns `this`
47 | */
48 | unbindBuffer() {
49 | // this._gl.bindBuffer(this.target, 0); // apparently this makes webgl cranky
50 | return this;
51 | }
52 |
53 | /**
54 | * Update the buffer data on the gpu
55 | *
56 | * @chainable
57 | * @return {this} Returns `this`
58 | */
59 | update() {
60 | this.bindBuffer();
61 | // if I do it this way, does it break?
62 | // if it works, will updating the underlying buffer
63 | // update the buffer without needing to call gl.bufferData again??
64 | this._gl.bufferData(this.target, this.values, this.usage);
65 | return this; // .unbindBuffer(); // apparently this makes webgl angry.
66 | }
67 |
68 | /**
69 | * Sets the buffer contents
70 | *
71 | * @chainable
72 | * @param {ArrayBuffer} values Values to store in the buffer
73 | * @param {Number} offset Offset to write the values
74 | * @return {this} Returns `this`
75 | */
76 | setValues(values, offset) {
77 | if(!this.values) {
78 | this.values = values;
79 | } else {
80 | this.values.set(values, offset);
81 | }
82 | this.update();
83 | return this;
84 | }
85 |
86 | /**
87 | * Deletes a chunk of a buffer
88 | *
89 | * @chainable
90 | * @param {Number} start Start of deletion
91 | * @param {Number} end End of deletion
92 | * @return {this} Returns `this`
93 | */
94 | deleteWithin(start, end) {
95 | if(!this.values) {
96 | throw new Error('Trying to splice a buffer that has no values.');
97 | }
98 | var nValues = end - start;
99 | var empty = new this.values.constructor(nValues);
100 | this.values.set(this.values.subarray(end), start);
101 | this.values.set(empty, this.values.length - nValues);
102 | this.update();
103 | return this;
104 | }
105 |
106 | /**
107 | * Do something with each elemnt of the buffer
108 | *
109 | * @chainable
110 | * @param {Function} callback The callback (values returned will overwrite
111 | * the contents of the buffer at that offset)
112 | * @param {Number} start Offset to start
113 | * @param {Number} end Offset to end
114 | * @return {this} Returns `this`
115 | */
116 | map(callback, start, end) {
117 | start = start === undefined ? 0 : start;
118 | end = end === undefined ? this.values.length : end;
119 | for(var i = start; i < end; i++) {
120 | this.values[i] = callback(this.values[i], i);
121 | }
122 | return this;
123 | }
124 |
125 | /**
126 | * Update a buffer's values, and also update the buffer on the gpu
127 | *
128 | * @chainable
129 | * @param {ArrayBuffer} values New values to fill the buffer with
130 | * @return {this} Returns `this`
131 | */
132 | updateBuffer(values) {
133 | this.values = values;
134 | return this.update();
135 | }
136 | }
137 |
138 | export default GLBuffer;
139 |
--------------------------------------------------------------------------------
/src/gl/gl-index.js:
--------------------------------------------------------------------------------
1 | import GLBuffer from './gl-buffer';
2 |
3 | /**
4 | * A GLIndex is a GLBuffer representing an index buffer of some kind
5 | *
6 | * @private
7 | * @extends {GLBuffer}
8 | * @chainable
9 | * @param {context} gl WebGL context
10 | * @param {ArrayBuffer} values Values to initialize the buffer with
11 | * @param {enum} drawMode Draw mode @see https://www.khronos.org/registry/webgl/specs/1.0/#5.14.11
12 | * @param {enum} usage Usage @see https://www.khronos.org/registry/webgl/specs/1.0/#5.14.5
13 | * @return {this} The new GLIndex
14 | */
15 | class GLIndex extends GLBuffer {
16 |
17 | constructor(gl, values, drawMode, usage) {
18 | usage = usage || gl.STATIC_DRAW;
19 | super(gl, gl.ELEMENT_ARRAY_BUFFER, usage);
20 | this.mode = drawMode;
21 | this.values = values;
22 | this.count = null;
23 | return this;
24 | }
25 |
26 | /**
27 | * Perform a draw call using this index buffer.
28 | *
29 | * @chainable
30 | * @return {this} Returns `this`
31 | */
32 | draw() {
33 | var gl = this._gl;
34 | if(!this.glBuf) {
35 | this.update();
36 | } else {
37 | this.bindBuffer();
38 | }
39 | gl.drawElements(this.mode, this.values.length, gl.UNSIGNED_SHORT, 0);
40 | return this;
41 | }
42 | }
43 |
44 | export default GLIndex;
45 |
--------------------------------------------------------------------------------
/src/ingress-model-viewer.js:
--------------------------------------------------------------------------------
1 | import Constants from './constants';
2 | import Engine from './engine';
3 | import { default as AssetLoader } from './asset-loader';
4 | import Drawable from './drawable';
5 | import Inventory from './drawable/inventory';
6 | import World from './drawable/world';
7 | import PortalLink from './drawable/portal-link';
8 | import ResonatorLink from './drawable/resonator-link';
9 | import SphericalPortalLink from './drawable/spherical-portal-link';
10 | import Atmosphere from './drawable/atmosphere';
11 | import TexturedSphere from './drawable/textured-sphere';
12 | import ParticlePortal from './drawable/particle-portal';
13 |
14 | import InventoryItems from './entity/inventory';
15 | import PortalEntity from './entity/portal';
16 |
17 | import OrbitControls from './orbit-controls';
18 |
19 | import { resetGL, setParams, disco, generateArtifacts, makeArtifact } from './utils';
20 | import Ease from './animation/easing';
21 | import Animation from './animation/animation';
22 | import GLMatrix from 'gl-matrix';
23 | import Promise from 'es6-promises';
24 |
25 | const IMV = {
26 | Constants,
27 | Engine,
28 | AssetLoader,
29 | Utilities: {
30 | resetGL,
31 | setParams,
32 | disco,
33 | generateArtifacts,
34 | makeArtifact,
35 | Ease,
36 | Animation,
37 | GLMatrix,
38 | Promise,
39 | },
40 | Drawables: {
41 | Inventory,
42 | World,
43 | ResonatorLink,
44 | PortalLink,
45 | SphericalPortalLink,
46 | Atmosphere,
47 | TexturedSphere,
48 | ParticlePortal,
49 | Drawable
50 | },
51 | Entities: {
52 | World: {
53 | Portal: PortalEntity
54 | },
55 | Inventory: InventoryItems
56 | },
57 | Controls: {
58 | Orbit: OrbitControls
59 | },
60 | VERSION: '0.22.2'
61 | };
62 |
63 | export default IMV;
64 |
65 | module.exports = IMV; // eslint-disable-line no-undef
66 |
--------------------------------------------------------------------------------
/src/mesh.js:
--------------------------------------------------------------------------------
1 | import GLBound from './gl-bound';
2 |
3 | const MODE_TRIANGLES = 'triangles';
4 | const MODE_LINES = 'lines';
5 |
6 | /**
7 | * Base class for all meshes
8 | *
9 | * @extends {GLBound}
10 | * @param {context} gl A webgl context
11 | * @param {Float32Array} attributes A typed array of vertex attributes
12 | * @param {Uint16Array} faces A typed array of face indices
13 | * @param {Uint16Array} lines A typed array of line indices
14 | */
15 | class Mesh extends GLBound {
16 |
17 | constructor(gl, attributes, faces, lines) {
18 | super(gl);
19 | this.attributes = attributes;
20 | this.faces = faces;
21 | this.lines = lines;
22 | this.bounds = null;
23 | this.center = null;
24 | }
25 |
26 | /**
27 | * Given a set of locations from the currently-active shader, draw this mesh
28 | * @param {Object} locations A hash of locations by name
29 | * @param {String} mode (optional) The draw mode
30 | * Either MODE_TRIANGLES or MODE_LINES
31 | * @return {void}
32 | */
33 | draw(locations, mode) {
34 | mode = mode || MODE_TRIANGLES;
35 | this.attributes.draw(locations);
36 | if(mode === MODE_TRIANGLES) {
37 | this.faces.draw();
38 | } else if (mode === MODE_LINES) {
39 | this.lines.draw();
40 | }
41 | }
42 |
43 | /**
44 | * Calculate the bounding box of the mesh
45 | * @param {Number} coordAttribute Index of the attribute representing vertex position
46 | * @return {Object} An object consisting of two arrays of the same length
47 | * as the coordinate attribute, representing min and max
48 | * coordinates.
49 | */
50 | boundingBox(coordAttribute) {
51 | if(!this.bounds) {
52 | coordAttribute = coordAttribute === undefined ? 0 : coordAttribute;
53 | var bounds = {
54 | max: null,
55 | min: null
56 | };
57 | this.attributes.eachAttribute(coordAttribute, function(arr) {
58 | if(bounds.max) {
59 | bounds.max = bounds.max.map(function(e, i) {
60 | return Math.max(e, arr[i]);
61 | });
62 | } else {
63 | bounds.max = Array.prototype.slice.call(arr);
64 | }
65 | if(bounds.min) {
66 | bounds.min = bounds.min.map(function(e, i) {
67 | return Math.min(e, arr[i]);
68 | });
69 | } else {
70 | bounds.min = Array.prototype.slice.call(arr);
71 | }
72 | });
73 | this.bounds = bounds;
74 | }
75 | return this.bounds;
76 | }
77 |
78 | centerOfMass(coordAttribute) {
79 | if(!this.center) {
80 | coordAttribute = coordAttribute === undefined ? 0 : coordAttribute;
81 | var sum = null,
82 | count = 0;
83 | this.attributes.eachAttribute(coordAttribute, function(arr) {
84 | count++;
85 | if(sum) {
86 | sum = sum.map(function(e, i) {
87 | return e + arr[i];
88 | });
89 | } else {
90 | sum = Array.prototype.slice.call(arr);
91 | }
92 | });
93 | this.center = sum.map(function(e) {
94 | return e / count;
95 | });
96 | }
97 | return this.center;
98 | }
99 |
100 | /**
101 | * Calculate the center of the bounding box.
102 | * @param {Number} coordAttribute Index of the attribute represention vertex position.
103 | * @return {mixed} A vector of the same size as the position attribute,
104 | * representing the center of the bounding box.
105 | */
106 | boundingBoxCenter(coordAttribute) {
107 | if(!this.bounds) {
108 | this.boundingBox(coordAttribute);
109 | }
110 | return this.bounds.max.map(function(e, i) {
111 | return (e - this.bounds.min[i]) / 2;
112 | }.bind(this));
113 | }
114 | }
115 |
116 | /**
117 | * Specifies drawing in `lines` mode
118 | * @type {String}
119 | */
120 | Mesh.MODE_LINES = MODE_LINES;
121 |
122 | /**
123 | * Specifies drawing in `triangles` mode
124 | * @type {String}
125 | */
126 | Mesh.MODE_TRIANGLES = MODE_TRIANGLES;
127 |
128 | export default Mesh;
129 |
--------------------------------------------------------------------------------
/src/mesh/file.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 | import JavaDeserializer from 'java-deserializer';
6 |
7 | function parseAttributes(buf)
8 | {
9 | var v = new DataView(buf.buffer, buf.byteOffset, buf.byteLength), c = 0;
10 | var n = v.getUint32(c), type, size, len, j, name;
11 | c += 4;
12 | var attributes = [];
13 | for(var i = 0; i < n; i++)
14 | {
15 | type = v.getUint32(c);
16 | c += 4;
17 | size = v.getUint32(c);
18 | c += 4;
19 | len = v.getUint16(c);
20 | c += 2;
21 | name = '';
22 | for(j = 0; j < len; j++)
23 | {
24 | name += String.fromCharCode(v.getUint8(c+j));
25 | }
26 | c += len;
27 | attributes.push(new VertexAttribute(name, size, type));
28 | }
29 | return attributes;
30 | }
31 |
32 | /**
33 | * A FileMesh is a Mesh that is loaded from a serialzied Java object,
34 | * as found in the apk.
35 | *
36 | * @extends {Mesh}
37 | */
38 | class FileMesh extends Mesh {
39 |
40 | /**
41 | * Construct the Mesh from the given file
42 | * @param {context} gl WebGL context
43 | * @param {ArrayBuffer} arraybuf ArrayBuffer representing the entire .obj file
44 | */
45 | constructor(gl, arraybuf) {
46 | var jd = new JavaDeserializer(arraybuf);
47 | var blocks = jd.getContents();
48 |
49 | // should be Float32Array
50 | var values = blocks[0].elements;
51 |
52 | // should be ArrayBuffer
53 | var attributeData = blocks[3];
54 |
55 | // array of VertexAttributes
56 | var spec = parseAttributes(attributeData);
57 |
58 | // should be Uint16Array
59 | var faces = new GLIndex(gl, blocks[1].elements, gl.TRIANGLES);
60 | var attributes = new GLAttribute(gl, spec, values);
61 |
62 | // should be Uint16Array
63 | var lines = new GLIndex(gl, blocks[2].elements, gl.LINES);
64 |
65 | super(gl, attributes, faces, lines);
66 | }
67 | }
68 |
69 | export default FileMesh;
70 |
--------------------------------------------------------------------------------
/src/mesh/particle-portal.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 |
6 | // const MAX_SYSTEMS = 40;
7 | const NUM_PARTICLES_PER_SYSTEM = 96;
8 | const NUM_VERTICES_PER_PARTICLE = 4;
9 | const NUM_INDICES_PER_FACE = 6;
10 | const TOTAL_VERTEX_SIZE = 3 + 2 + 1 + 1 + 1 + 1;
11 | const U = [0.0, 0.0, 1.0, 1.0];
12 | const V = [1.0, 0.0, 1.0, 0.0];
13 |
14 | var seeds = [];
15 | for(var i = 0; i < NUM_PARTICLES_PER_SYSTEM; i++)
16 | {
17 | seeds.push({
18 | x: Math.random() - 0.5,
19 | y: 0.4 * Math.random() - 0.2,
20 | z: Math.random() - 0.5,
21 | a_scale: 10.0 * (0.1 + 0.9 * Math.random()),
22 | a_speed: 6.0 * (0.5 + 0.5 * Math.random())
23 | });
24 | }
25 |
26 | /**
27 | * A ParticlePortalMesh is a Mesh that represents a single system or portal particles.
28 | *
29 | * @extends {Mesh}
30 | */
31 | class ParticlePortalMesh extends Mesh {
32 |
33 | /**
34 | * Construct a system of portal particles
35 | * @param {context} gl WebGL context
36 | */
37 | constructor(gl) {
38 | var attributes = [];
39 | attributes.push(new VertexAttribute('a_position', 3));
40 | attributes.push(new VertexAttribute('a_texCoord0', 2));
41 | attributes.push(new VertexAttribute('a_scale', 1));
42 | attributes.push(new VertexAttribute('a_speed', 1));
43 | attributes.push(new VertexAttribute('a_portalIndex', 1));
44 | attributes.push(new VertexAttribute('a_index', 1));
45 | var values = new Float32Array(NUM_PARTICLES_PER_SYSTEM * NUM_VERTICES_PER_PARTICLE * TOTAL_VERTEX_SIZE);
46 | var seed, i, j, idx = 0;
47 | for(i = 0; i < NUM_PARTICLES_PER_SYSTEM; i++)
48 | {
49 | seed = seeds[i];
50 | for(j = 0; j < NUM_VERTICES_PER_PARTICLE; j++)
51 | {
52 | values[idx * TOTAL_VERTEX_SIZE + 0] = seed.x;
53 | values[idx * TOTAL_VERTEX_SIZE + 1] = seed.y;
54 | values[idx * TOTAL_VERTEX_SIZE + 2] = seed.z;
55 | values[idx * TOTAL_VERTEX_SIZE + 3] = U[j];
56 | values[idx * TOTAL_VERTEX_SIZE + 4] = V[j];
57 | values[idx * TOTAL_VERTEX_SIZE + 5] = seed.a_scale;
58 | values[idx * TOTAL_VERTEX_SIZE + 6] = seed.a_speed;
59 | values[idx * TOTAL_VERTEX_SIZE + 7] = 0;
60 | values[idx * TOTAL_VERTEX_SIZE + 8] = i;
61 | idx++;
62 | }
63 | }
64 |
65 | var faces = new Uint16Array(NUM_PARTICLES_PER_SYSTEM * NUM_INDICES_PER_FACE);
66 | var indices = [0, 1, 2, 1, 3, 2];
67 | idx = 0;
68 | var f = 0;
69 | for(i = 0; i < NUM_PARTICLES_PER_SYSTEM; i++)
70 | {
71 | for(j = 0; j < NUM_INDICES_PER_FACE; j++)
72 | {
73 | faces[f + j] = idx + indices[j];
74 | }
75 | f += 6;
76 | idx += 4;
77 | }
78 | super(
79 | gl,
80 | new GLAttribute(gl, attributes, values),
81 | new GLIndex(gl, faces, gl.TRIANGLES)
82 | );
83 | }
84 | }
85 |
86 | export default ParticlePortalMesh;
87 |
--------------------------------------------------------------------------------
/src/mesh/plane.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 |
6 | /**
7 | * A PlaneMesh is a Mesh that represents a unit square plane, centered on
8 | * 0,0. Consists of a single quad.
9 | *
10 | * @extends {Mesh}
11 | */
12 | class PlaneMesh extends Mesh {
13 |
14 | /**
15 | * Construct a sphere
16 | * @param {context} gl WebGL context
17 | */
18 | constructor(gl) {
19 | var attributes = [];
20 | attributes.push(new VertexAttribute('a_position', 3));
21 | attributes.push(new VertexAttribute('a_texCoord0', 2));
22 | var values = new Float32Array(4 * 5); // 4 vertices, 5 bytes each.
23 | var faces = new Uint16Array(2 * 3); // two triangles
24 |
25 | // Upper left:
26 | values[0] = -0.5;
27 | values[1] = 0;
28 | values[2] = 0.5;
29 | values[3] = 0;
30 | values[4] = 0;
31 | // Upper right:
32 | values[5] = 0.5;
33 | values[6] = 0;
34 | values[7] = 0.5;
35 | values[8] = 1;
36 | values[9] = 0;
37 | // Lower left:
38 | values[10] = -0.5;
39 | values[11] = 0;
40 | values[12] = -0.5;
41 | values[13] = 0;
42 | values[14] = 1;
43 | // Lower right:
44 | values[15] = 0.5;
45 | values[16] = 0;
46 | values[17] = -0.5;
47 | values[18] = 1;
48 | values[19] = 1;
49 |
50 | // Faces:
51 | faces[0] = 0;
52 | faces[1] = 1;
53 | faces[2] = 2;
54 | faces[3] = 1;
55 | faces[4] = 3;
56 | faces[5] = 2;
57 | super(
58 | gl,
59 | new GLAttribute(gl, attributes, values),
60 | new GLIndex(gl, faces, gl.TRIANGLES)
61 | );
62 | }
63 | }
64 |
65 | export default PlaneMesh;
66 |
--------------------------------------------------------------------------------
/src/mesh/portal-link.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 | import { vec3, vec4 } from 'gl-matrix';
6 |
7 | // TODO: Parameterize this concept a little better
8 | // this has potential to be a really flexible and powerful way of
9 | // making, essentially, extruded geometry.
10 |
11 | // 9 sets of 6 points, breaking the link into 8 pieces, each providing 6 faces, something like that?
12 | var _len = 9, _size = _len * 6, _chunkSize = 12;
13 | var c = new Array(_len),
14 | d = new Array(_len),
15 | e = new Array(_len);
16 |
17 | var baseColor = vec4.fromValues(0.46, 0.18, 0.18, 1.0);
18 | var baseOffset = vec4.create();
19 |
20 | function clampedSin(f)
21 | {
22 | return Math.sin(Math.PI * Math.max(Math.min(1.0, f), 0) / 2);
23 | }
24 |
25 | for(var i = 0; i < _len; i++)
26 | {
27 | var f = i / 8.0;
28 | c[i] = f;
29 | e[i] = (3.0 + (-1.5 * Math.pow(clampedSin(2.0 * Math.abs(f - 0.5)), 4)));
30 | d[i] = clampedSin(1.0 - 2.0 * Math.abs(f - 0.5));
31 | }
32 |
33 | function fillChunk(buf, index, x, y, z, u, v, normal, f6, color)
34 | {
35 | var off = index * _chunkSize;
36 | buf[off + 0] = x;
37 | buf[off + 1] = y;
38 | buf[off + 2] = z;
39 | buf[off + 3] = f6;
40 | buf[off + 4] = u;
41 | buf[off + 5] = v;
42 | buf[off + 6] = normal[0];
43 | buf[off + 7] = normal[2];
44 | buf[off + 8] = color[0];
45 | buf[off + 9] = color[1];
46 | buf[off + 10] = color[2];
47 | buf[off + 11] = color[3];
48 | }
49 |
50 | function _generateLinkAttributes(start, end, color, startPercent, endPercent) {
51 | startPercent = startPercent === undefined ? 1 : Math.max(Math.min(startPercent, 1), 0);
52 | endPercent = endPercent === undefined ? 1 : Math.max(Math.min(endPercent, 1), 0);
53 | var values = new Float32Array(_size * _chunkSize);
54 | var length = Math.sqrt((end[0] - start[0]) * (end[0] - start[0]) + (end[1] - start[1]) * (end[1] - start[1]));
55 | var yMin = baseOffset[1],
56 | yMax = yMin + Math.min(30.0, 0.08 * length),
57 | avgPercent = (startPercent + endPercent) / 2.0,
58 | f6 = 0.01 * length,
59 | f7 = 0.1 + avgPercent * 0.3;
60 | var vec = vec3.fromValues(end[0], 0, end[1]);
61 | vec3.subtract(vec, vec, vec3.fromValues(start[0], 0, start[1]));
62 | var up = vec3.fromValues(0, 1, 0);
63 | var right = vec3.cross(vec3.create(), vec, up);
64 | vec3.normalize(right, right);
65 | var step = _len * 2;
66 | for(var i = 0; i < _len; i++)
67 | {
68 | var f8 = c[i],
69 | f9 = startPercent + f8 * (endPercent - startPercent),
70 | f10 = 0.6 + 0.35 * f9,
71 | f12 = f8 * f6,
72 | f13 = start[0] + f8 * vec[0],
73 | f14 = start[1] + f8 * vec[2],
74 | f15 = yMin + d[i] * (yMax - yMin),
75 | f16 = e[i];
76 | var cl = vec4.lerp(vec4.create(), baseColor, color, 0.25 + f9 * 0.75);
77 | cl[3] = f10;
78 | fillChunk(values, (i * 2), f13 + f16 * right[0], f15, f14 + f16 * right[2], 0, f12, up, f7, cl);
79 | fillChunk(values, (i * 2) + 1, f13 - f16 * right[0], f15, f14 - f16 * right[2], 0.5, f12, up, f7, cl);
80 | fillChunk(values, step + (i * 2), f13, f15 + f16, f14, 0, f12, right, f7, cl);
81 | fillChunk(values, step + (i * 2) + 1, f13, f15 - f16, f14, 0.5, f12, right, f7, cl);
82 | fillChunk(values, 2 * step + (i * 2), f13, f15 - f16, f14, 0.5, f12, right, f7, cl);
83 | fillChunk(values, 2 * step + (i * 2) + 1, f13, 0, f14, 1.0, f12, right, f7, cl);
84 | }
85 | return values;
86 | }
87 |
88 | function _generateFaces(vertexOffset) {
89 | var ind = new Uint16Array(144),
90 | iOff = 0;
91 | for(var i = 0; i < 3; i++) {
92 |
93 | for(var j = 0; j < _len - 1; j++) {
94 |
95 | ind[iOff + 0] = vertexOffset + 1;
96 | ind[iOff + 1] = vertexOffset + 0;
97 | ind[iOff + 2] = vertexOffset + 2;
98 | ind[iOff + 3] = vertexOffset + 1;
99 | ind[iOff + 4] = vertexOffset + 2;
100 | ind[iOff + 5] = vertexOffset + 3;
101 | vertexOffset += 2;
102 | iOff += 6;
103 | }
104 | vertexOffset += 2;
105 | }
106 |
107 | return ind;
108 | }
109 |
110 | /**
111 | * A PortalLinkMesh represents the mesh for a single portal link.
112 | *
113 | * @extends {Mesh}
114 | */
115 | class PortalLinkMesh extends Mesh {
116 |
117 | /**
118 | * Programatically constructs the mesh for a link between two points
119 | * @param {context} gl WebGL context
120 | * @param {vec2} start X,Z of the origin point
121 | * @param {vec2} end X,Z of the destination point
122 | * @param {vec4} color Color of the link
123 | * @param {Number} startPercent Origin point percentage
124 | * @param {Number} endPercent Destination point percentage
125 | */
126 | constructor(gl, start, end, color, startPercent, endPercent) {
127 | var buf = _generateLinkAttributes(start, end, color, startPercent, endPercent);
128 | var ind = _generateFaces(0);
129 | var attributes = [];
130 | attributes.push(new VertexAttribute('a_position', 4));
131 | attributes.push(new VertexAttribute('a_texCoord0', 4));
132 | attributes.push(new VertexAttribute('a_color', 4));
133 | var attribute = new GLAttribute(gl, attributes, buf, gl.DYNAMIC_DRAW);
134 | var faces = new GLIndex(gl, ind, gl.TRIANGLES);
135 | super(gl, attribute, faces);
136 | }
137 | }
138 |
139 | export default PortalLinkMesh;
140 |
--------------------------------------------------------------------------------
/src/mesh/resonator-link.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 | import { vec2, vec3, vec4 } from 'gl-matrix';
6 |
7 | // TODO: Parameterize this concept a little better
8 | // this has potential to be a really flexible and powerful way of
9 | // making, essentially, extruded geometry.
10 |
11 | // 5 sets of 4 points, breaking the link into 4 pieces, each providing 4 faces
12 | // chunksize is size of each element in the packed vertex array, in bytes
13 | var _len = 5, _size = _len * 4, _chunkSize = 12;
14 | var j = new Array(_len),
15 | k = new Array(_len),
16 | l = new Array(_len);
17 |
18 | function clampedSin(f)
19 | {
20 | return Math.sin(Math.PI * Math.max(Math.min(1.0, f), 0) / 2);
21 | }
22 |
23 | for(var i = 0; i < _len; i++)
24 | {
25 | var f = i / 4.0;
26 | j[i] = f;
27 | l[i] = 3.5 * Math.max(1.0 - Math.pow(clampedSin(2.0 * Math.abs(f - 0.5)), 4.0), 0.2);
28 | k[i] = clampedSin(1.0 - 2.0 * Math.abs(f - 0.5));
29 | }
30 |
31 | var baseColor = vec4.fromValues(0.78, 0.31, 0.31, 1.0);
32 | var resonatorMidOffset = 0;
33 | var portalBaseOffset = 0;
34 | var up = vec3.fromValues(0, 1, 0);
35 |
36 | function fillChunk(buf, index, x, y, z, u, v, normal, f6, color)
37 | {
38 | var off = index * _chunkSize;
39 | buf[off + 0] = x;
40 | buf[off + 1] = y;
41 | buf[off + 2] = z;
42 | buf[off + 3] = f6;
43 | buf[off + 4] = u;
44 | buf[off + 5] = v;
45 | buf[off + 6] = normal[0];
46 | buf[off + 7] = normal[2];
47 | buf[off + 8] = color[0];
48 | buf[off + 9] = color[1];
49 | buf[off + 10] = color[2];
50 | buf[off + 11] = color[3];
51 | }
52 |
53 | function _generateLinkAttributes(portal, resonator, color, resonatorPercent) {
54 | resonatorPercent = resonatorPercent === undefined ? 1 : Math.max(Math.min(resonatorPercent, 1), 0);
55 | var values = new Float32Array(_size * _chunkSize);
56 | var dist = Math.sqrt(
57 | (resonator[0] - portal[0]) * (resonator[0] - portal[0]) +
58 | (resonator[1] - portal[1]) * (resonator[1] - portal[1])
59 | );
60 | var f4 = (2 / 30) * dist,
61 | f5 = 0.9 + 0.1 * resonatorPercent,
62 | f6 = 0.65 + 0.35 * resonatorPercent,
63 | f8 = 0.1 + 0.3 * resonatorPercent;
64 | var cl = vec4.lerp(vec4.create(), baseColor, color, 0.1 + resonatorPercent * 0.85);
65 | cl[3] = 0.75 + 0.25 * resonatorPercent * cl[3];
66 | var vec = vec3.fromValues(resonator[0], 0, resonator[1]);
67 | vec3.subtract(vec, vec, vec3.fromValues(portal[0], 0, portal[1]));
68 | var right = vec3.cross(vec3.create(), vec, up);
69 | vec3.normalize(right, right);
70 | var step = _len * 2;
71 | var f10 = 5.0 * ((portal[0] + portal[1]) - Math.floor(portal[0] + portal[1]));
72 | for(var i = 0; i < _len; i++)
73 | {
74 | var f11 = j[i],
75 | f12 = portal[0] + f11 * vec[0],
76 | f13 = portal[1] + f11 * vec[2],
77 | f14 = portalBaseOffset + f11 * (resonatorMidOffset - portalBaseOffset) + f5 * k[i],
78 | f15 = f6 * l[i],
79 | f16 = f11 * f4;
80 | fillChunk(values, (i * 2) + 0, f12 + f15 * right[0], f14, f13 + f15 * right[2], 0.0, f16 + f10, up, f8, cl);
81 | fillChunk(values, (i * 2) + 1, f12 - f15 * right[0], f14, f13 - f15 * right[2], 1.0, f16 + f10, up, f8, cl);
82 | fillChunk(values, step + (i * 2) + 0, f12, f14 + f15, f13, 0.0, f16 + f10, right, f8, cl);
83 | fillChunk(values, step + (i * 2) + 1, f12, f14 - f15, f13, 1.0, f16 + f10, right, f8, cl);
84 | }
85 | return values;
86 | }
87 |
88 | function _generateFaces(vertexOffset) {
89 | var ind = new Uint16Array(48),
90 | iOff = 0;
91 |
92 | for(i = 0; i < 2; i++)
93 | {
94 | for(var i2 = 0; i2 < _len - 1; i2++)
95 | {
96 | ind[iOff + 0] = vertexOffset + 1;
97 | ind[iOff + 1] = vertexOffset + 0;
98 | ind[iOff + 2] = vertexOffset + 2;
99 | ind[iOff + 3] = vertexOffset + 1;
100 | ind[iOff + 4] = vertexOffset + 2;
101 | ind[iOff + 5] = vertexOffset + 3;
102 | vertexOffset += 2;
103 | iOff += 6;
104 | }
105 | vertexOffset += 2;
106 | }
107 |
108 | return ind;
109 | }
110 |
111 | /**
112 | * A ResonatorLinkMesh is a Mesh that represents a single link between a portal and a resonator
113 | *
114 | * TODO: Make disco
115 | *
116 | * @extends {Mesh}
117 | */
118 | class ResonatorLinkMesh extends Mesh {
119 |
120 | /**
121 | * Construct a resonator link mesh
122 | * @param {context} gl WebGL context
123 | * @param {vec2} portalPosition X,Z of the portal
124 | * @param {Number} slot Resonator slot (0-7)
125 | * @param {Number} distance Distance from the portal
126 | * @param {vec4} color Color of the resonator link
127 | * @param {Number} resonatorPercent Percent health of the resonator
128 | */
129 | constructor(gl, portalPosition, slot, distance, color, resonatorPercent) {
130 | var theta = slot / 8 * 2 * Math.PI;
131 | var end = vec2.create();
132 | var relative = vec2.fromValues(distance * Math.cos(theta), distance * Math.sin(theta));
133 | vec2.add(end, portalPosition, relative);
134 | var buf = _generateLinkAttributes(portalPosition, end, color, resonatorPercent);
135 | var ind = _generateFaces(0);
136 | var attributes = [];
137 | attributes.push(new VertexAttribute('a_position', 4));
138 | attributes.push(new VertexAttribute('a_texCoord0', 4));
139 | attributes.push(new VertexAttribute('a_color', 4));
140 | var attribute = new GLAttribute(gl, attributes, buf, gl.DYNAMIC_DRAW);
141 | var faces = new GLIndex(gl, ind, gl.TRIANGLES);
142 | super(gl, attribute, faces);
143 | }
144 | }
145 |
146 | export default ResonatorLinkMesh;
147 |
--------------------------------------------------------------------------------
/src/mesh/sphere.js:
--------------------------------------------------------------------------------
1 | import Mesh from '../mesh';
2 | import VertexAttribute from '../vertex-attribute';
3 | import GLIndex from '../gl/gl-index';
4 | import GLAttribute from '../gl/gl-attribute';
5 | import { vec3 } from 'gl-matrix';
6 |
7 | // part of doing away with the THREE.js dependency
8 | // means giving up a lot of helper code for doing things
9 | // like this.
10 | //
11 | // Needless to say, this borrows heavily from THREE.SphereGeometry
12 | // https://github.com/mrdoob/three.js/blob/master/src/extras/geometries/SphereGeometry.js
13 | function createSphere(radius, phiSlices, thetaSlices) {
14 | var i, j, u, v, vec, v1, v2, v3, v4,
15 | verticesRow, faces,
16 | phi = Math.PI * 2,
17 | theta = Math.PI,
18 | // size is 8 for vec3 a_position + vec2 a_texCoord + vec3 a_normal
19 | values = new Float32Array((phiSlices + 1) * (thetaSlices + 1) * 8),
20 | faceArray = [],
21 | vertices = [],
22 | aIdx = 0,
23 | attributes = [];
24 | phiSlices = Math.max(3, phiSlices || 8);
25 | thetaSlices = Math.max(2, thetaSlices || 6);
26 |
27 | for(i = 0; i <= phiSlices; i++) {
28 | verticesRow = [];
29 | for(j = 0; j <= thetaSlices; j++)
30 | {
31 | u = j / phiSlices;
32 | v = i / thetaSlices;
33 | vec = vec3.fromValues(
34 | -radius * Math.cos(u * phi) * Math.sin(v * theta),
35 | radius * Math.cos(v * theta),
36 | radius * Math.sin(u * phi) * Math.sin(v * theta)
37 | );
38 |
39 | values[aIdx * 8 + 0] = vec[0];
40 | values[aIdx * 8 + 1] = vec[1];
41 | values[aIdx * 8 + 2] = vec[2];
42 | values[aIdx * 8 + 3] = u;
43 | values[aIdx * 8 + 4] = v;
44 | // normalized:
45 | vec3.normalize(vec, vec);
46 | values[aIdx * 8 + 5] = vec[0];
47 | values[aIdx * 8 + 6] = vec[1];
48 | values[aIdx * 8 + 7] = vec[2];
49 |
50 | verticesRow.push(aIdx++);
51 | }
52 | vertices.push(verticesRow);
53 | }
54 |
55 | for(i = 0; i < phiSlices; i++) {
56 | for(j = 0; j < thetaSlices; j++) {
57 | v1 = vertices[i][j + 1];
58 | v2 = vertices[i][j];
59 | v3 = vertices[i + 1][j];
60 | v4 = vertices[i + 1][j + 1];
61 |
62 | if(Math.abs(values[v1 * 8 + 1]) === radius) {
63 | faceArray.push.apply(faceArray, [v1, v3, v4]);
64 | values[v1 * 8 + 3] = (values[v1 * 8 + 3] + values[v2 * 8 + 3]) / 2;
65 | }
66 | else if(Math.abs(values[v3 * 8 + 1]) === radius) {
67 | faceArray.push.apply(faceArray, [v1, v2, v3]);
68 | values[v3 * 8 + 3] = (values[v3 * 8 + 3] + values[v4 * 8 + 3]) / 2;
69 | }
70 | else {
71 | faceArray.push.apply(faceArray, [v1, v2, v4]);
72 | faceArray.push.apply(faceArray, [v2, v3, v4]);
73 | }
74 | }
75 | }
76 |
77 | faces = new Uint16Array(faceArray.length);
78 | faceArray.forEach(function(v, i) {
79 | faces[i] = v;
80 | });
81 | attributes.push(new VertexAttribute('a_position', 3));
82 | attributes.push(new VertexAttribute('a_texCoord0', 2));
83 | attributes.push(new VertexAttribute('a_normal', 3));
84 | return {
85 | values: values,
86 | faces: faces,
87 | attributes: attributes
88 | };
89 | }
90 |
91 | /**
92 | * A SphereMesh is a Mesh that is a sphere, made of a number of quads determined
93 | * by the number of horizontal and vertical slices involved in its construction
94 | *
95 | * @extends {Mesh}
96 | */
97 | class SphereMesh extends Mesh {
98 |
99 | /**
100 | * Construct a sphere
101 | * @param {context} gl WebGL context
102 | * @param {Number} radius Radius of the sphere
103 | * @param {Number} vSlices Number of vertical slices
104 | * @param {Number} hSlices Number of horizontal slices
105 | */
106 | constructor(gl, radius, vSlices, hSlices) {
107 | var parsed = createSphere(radius, vSlices, hSlices);
108 | var attributes = new GLAttribute(gl, parsed.attributes, parsed.values);
109 | var faces = new GLIndex(gl, parsed.faces, gl.TRIANGLES);
110 | super(gl, attributes, faces);
111 | }
112 | }
113 |
114 | export default SphereMesh;
115 |
--------------------------------------------------------------------------------
/src/polyfill.js:
--------------------------------------------------------------------------------
1 | // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
2 | // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
3 |
4 | // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
5 |
6 | // MIT license
7 |
8 | function polyfillRequestAnimationFrame() {
9 | var lastTime = 0;
10 | var vendors = ['ms', 'moz', 'webkit', 'o'];
11 | for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
12 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
13 | window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
14 | }
15 |
16 | if (!window.requestAnimationFrame) {
17 | window.requestAnimationFrame = function(callback) {
18 | var currTime = new Date().getTime();
19 | var timeToCall = Math.max(0, 16 - (currTime - lastTime));
20 | var id = window.setTimeout(function() { callback(currTime + timeToCall); },
21 | timeToCall);
22 | lastTime = currTime + timeToCall;
23 | return id;
24 | };
25 | }
26 |
27 | if (!window.cancelAnimationFrame){
28 | window.cancelAnimationFrame = function(id) {
29 | clearTimeout(id);
30 | };
31 | }
32 | }
33 |
34 | export default polyfillRequestAnimationFrame;
35 |
--------------------------------------------------------------------------------
/src/program.js:
--------------------------------------------------------------------------------
1 | import GLBound from './gl-bound';
2 |
3 | // Taken from PhiloGL's program class:
4 | //Returns a Magic Uniform Setter
5 | function getUniformSetter(gl, program, info, isArray) {
6 | var name = info.name,
7 | loc = gl.getUniformLocation(program, name),
8 | type = info.type,
9 | matrix = false,
10 | vector = true,
11 | glFunction, typedArray;
12 |
13 | if (info.size > 1 && isArray) {
14 | switch(type) {
15 | case gl.FLOAT:
16 | glFunction = gl.uniform1fv;
17 | typedArray = Float32Array;
18 | vector = false;
19 | break;
20 | case gl.INT: case gl.BOOL: case gl.SAMPLER_2D: case gl.SAMPLER_CUBE:
21 | glFunction = gl.uniform1iv;
22 | typedArray = Uint16Array;
23 | vector = false;
24 | break;
25 | }
26 | }
27 |
28 | if (vector) {
29 | switch (type) {
30 | case gl.FLOAT:
31 | glFunction = gl.uniform1f;
32 | break;
33 | case gl.FLOAT_VEC2:
34 | glFunction = gl.uniform2fv;
35 | typedArray = isArray ? Float32Array : new Float32Array(2);
36 | break;
37 | case gl.FLOAT_VEC3:
38 | glFunction = gl.uniform3fv;
39 | typedArray = isArray ? Float32Array : new Float32Array(3);
40 | break;
41 | case gl.FLOAT_VEC4:
42 | glFunction = gl.uniform4fv;
43 | typedArray = isArray ? Float32Array : new Float32Array(4);
44 | break;
45 | case gl.INT: case gl.BOOL: case gl.SAMPLER_2D: case gl.SAMPLER_CUBE:
46 | glFunction = gl.uniform1i;
47 | break;
48 | case gl.INT_VEC2: case gl.BOOL_VEC2:
49 | glFunction = gl.uniform2iv;
50 | typedArray = isArray ? Uint16Array : new Uint16Array(2);
51 | break;
52 | case gl.INT_VEC3: case gl.BOOL_VEC3:
53 | glFunction = gl.uniform3iv;
54 | typedArray = isArray ? Uint16Array : new Uint16Array(3);
55 | break;
56 | case gl.INT_VEC4: case gl.BOOL_VEC4:
57 | glFunction = gl.uniform4iv;
58 | typedArray = isArray ? Uint16Array : new Uint16Array(4);
59 | break;
60 | case gl.FLOAT_MAT2:
61 | matrix = true;
62 | glFunction = gl.uniformMatrix2fv;
63 | break;
64 | case gl.FLOAT_MAT3:
65 | matrix = true;
66 | glFunction = gl.uniformMatrix3fv;
67 | break;
68 | case gl.FLOAT_MAT4:
69 | matrix = true;
70 | glFunction = gl.uniformMatrix4fv;
71 | break;
72 | }
73 | }
74 |
75 | //TODO(nico): Safari 5.1 doesn't have Function.prototype.bind.
76 | //remove this check when they implement it.
77 | if (glFunction.bind) {
78 | glFunction = glFunction.bind(gl);
79 | } else {
80 | var target = glFunction;
81 | glFunction = function() { target.apply(gl, arguments); };
82 | }
83 |
84 | //Set a uniform array
85 | if (isArray && typedArray) {
86 | return function(val) {
87 | glFunction(loc, new typedArray(val)); // jshint ignore:line
88 | };
89 |
90 | //Set a matrix uniform
91 | } else if (matrix) {
92 | return function(val) {
93 | glFunction(loc, false, val);
94 | };
95 |
96 | //Set a vector/typed array uniform
97 | } else if (typedArray) {
98 | return function(val) {
99 | typedArray.set(val.toFloat32Array ? val.toFloat32Array() : val);
100 | glFunction(loc, typedArray);
101 | };
102 |
103 | //Set a primitive-valued uniform
104 | } else {
105 | return function(val) {
106 | glFunction(loc, val);
107 | };
108 | }
109 | }
110 |
111 | /**
112 | * Represents a shader program consisting of a vertex shader and a fragment
113 | * shader.
114 | *
115 | * Manages the shader's attributes and uniforms.
116 | *
117 | * @class
118 | * @extends {GLBound}
119 | * @param {context} gl Webgl context
120 | * @param {String} vertex Vertex shader
121 | * @param {String} fragment Fragment shader
122 | */
123 | class Program extends GLBound {
124 |
125 | constructor(gl, vertex, fragment) {
126 | super(gl);
127 | this.program = null;
128 | this.vertexSource = Program.fixPrecision(vertex);
129 | this.fragmentSource = fragment;
130 | this.attributes = {};
131 | this.uniforms = {};
132 | }
133 |
134 | /**
135 | * Initialize the shader
136 | *
137 | * Parses out shader parameters, compiles the shader, and binds it to
138 | * the context.
139 | *
140 | * @return {void}
141 | */
142 | init() {
143 | var gl = this._gl, vertex, fragment;
144 | vertex = gl.createShader(gl.VERTEX_SHADER);
145 | gl.shaderSource(vertex, this.vertexSource);
146 | gl.compileShader(vertex);
147 | if(!gl.getShaderParameter(vertex, gl.COMPILE_STATUS))
148 | {
149 | console.warn(gl.getShaderInfoLog(vertex)); // eslint-disable-line no-console
150 | console.error('could not compile vertex shader: ' + this.vertexSource); // eslint-disable-line no-console
151 | throw 'Vertex shader compile error!';
152 | }
153 | fragment = gl.createShader(gl.FRAGMENT_SHADER);
154 | gl.shaderSource(fragment, this.fragmentSource);
155 | gl.compileShader(fragment);
156 | if(!gl.getShaderParameter(fragment, gl.COMPILE_STATUS))
157 | {
158 | console.warn(gl.getShaderInfoLog(fragment)); // eslint-disable-line no-console
159 | console.error('could not compile fragment shader: ' + this.fragmentSource); // eslint-disable-line no-console
160 | throw 'Fragment shader compile error!';
161 | }
162 |
163 | this.program = gl.createProgram();
164 | gl.attachShader(this.program, vertex);
165 | gl.attachShader(this.program, fragment);
166 |
167 | gl.linkProgram(this.program);
168 |
169 | if(!gl.getProgramParameter(this.program, gl.LINK_STATUS))
170 | {
171 | // TODO: verbose like above
172 | throw 'Could not link program';
173 | }
174 | gl.useProgram(this.program);
175 |
176 | this._setupLocations();
177 | }
178 |
179 | /**
180 | * Use the program with the given draw function
181 | * @param {Function} fn Function to handle the actual drawing.
182 | * The programs attributes and uniforms will
183 | * be passed to the draw function for use.
184 | * @return {void}
185 | */
186 | use(fn) {
187 | var gl = this._gl;
188 | if(!this.program)
189 | {
190 | this.init();
191 | }
192 | else
193 | {
194 | gl.useProgram(this.program);
195 | }
196 | fn(this.attributes, this.uniforms);
197 | //gl.useProgram(0);
198 | }
199 |
200 | _setupLocations() {
201 | var gl = this._gl, program = this.program;
202 | // this is taken partly from PhiloGL's Program class.
203 | //fill attribute locations
204 | var len = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES), info, name;
205 | for (var i = 0; i < len; i++) {
206 | info = gl.getActiveAttrib(program, i);
207 | this.attributes[info.name] = gl.getAttribLocation(program, info.name);
208 | }
209 |
210 | //create uniform setters
211 | len = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
212 | for (i = 0; i < len; i++) {
213 | info = gl.getActiveUniform(program, i);
214 | name = info.name;
215 | //if array name then clean the array brackets
216 | name = name[name.length -1] == ']' ? name.substr(0, name.length -3) : name;
217 | this.uniforms[name] = getUniformSetter(gl, program, info, info.name != name);
218 | }
219 | }
220 |
221 | /**
222 | * Fixes an issue with shaders where the shader doesn't set a precision,
223 | * leading it to have a mismatch with its counterpart
224 | *
225 | * I.e. the vertex shader might set a precision, but the fragment shader
226 | * does not, leading to precision mismatch errors.
227 | * @static
228 | * @param {String} shader The shader to check/fix
229 | * @return {String} The fixed shader, or the original if it needed
230 | * no patching.
231 | */
232 | static fixPrecision(shader)
233 | {
234 | if(/precision mediump float/g.test(shader))
235 | {
236 | return shader;
237 | }
238 | else
239 | {
240 | var lines = shader.split("\n");
241 | lines.splice(1, 0, "#ifdef GL_ES", "precision mediump float;", "#endif");
242 | return lines.join("\n");
243 | }
244 | }
245 | }
246 |
247 | export default Program;
248 |
--------------------------------------------------------------------------------
/src/program/glowramp.js:
--------------------------------------------------------------------------------
1 | import Program from '../program';
2 | import { resetGL } from '../utils';
3 |
4 | /**
5 | * A GlowrampProgram is a program meant for drawing
6 | * transparent glowramp drawables
7 | *
8 | * @extends {Program}
9 | * @param {context} gl WebGL context
10 | * @param {String} vertex Vertex shader source
11 | * @param {String} fragment Fragment shader source
12 | */
13 | class GlowrampProgram extends Program {
14 |
15 | constructor(gl, vertex, fragment) {
16 | super(gl, vertex, fragment);
17 | }
18 |
19 | /**
20 | * Use this program to draw
21 | *
22 | * Sets up the proper blending modes, etc
23 | * @param {Function} fn The draw function
24 | * @return {void}
25 | */
26 | use(fn) {
27 | if(!this.program)
28 | {
29 | this.init();
30 | }
31 | var gl = this._gl;
32 | gl.useProgram(this.program);
33 | // init stuffs.
34 | gl.disable(gl.CULL_FACE);
35 | gl.enable(gl.BLEND);
36 | gl.depthMask(false);
37 | gl.blendEquation(gl.FUNC_ADD);
38 | //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
39 | gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
40 |
41 | fn(this.attributes, this.uniforms);
42 |
43 | resetGL(gl);
44 | //gl.useProgram(0);
45 | }
46 | }
47 |
48 | export default GlowrampProgram;
49 |
--------------------------------------------------------------------------------
/src/program/opaque.js:
--------------------------------------------------------------------------------
1 | import Program from '../program';
2 | import { resetGL } from '../utils';
3 |
4 | /**
5 | * And OpaqueProgram is a Program used to draw opaque drawables
6 | *
7 | * @extends {Program}
8 | * @param {context} gl WebGL context
9 | * @param {String} vertex Vertex shader source
10 | * @param {String} fragment Fragment shader source
11 | */
12 | class OpaqueProgram extends Program {
13 |
14 | constructor(gl, vertex, fragment) {
15 | super(gl, vertex, fragment);
16 | }
17 |
18 | /**
19 | * Use this program to draw.
20 | *
21 | * Sets up the proper culling for drawing opaque objects
22 | *
23 | * @param {Function} fn The draw function
24 | * @return {void}
25 | */
26 | use(fn) {
27 | if(!this.program)
28 | {
29 | this.init();
30 | }
31 | var gl = this._gl;
32 | gl.useProgram(this.program);
33 | // init stuffs.
34 | gl.enable(gl.DEPTH_TEST);
35 | gl.enable(gl.CULL_FACE);
36 | gl.frontFace(gl.CCW);
37 | gl.cullFace(gl.BACK);
38 | gl.depthMask(true);
39 |
40 | fn(this.attributes, this.uniforms);
41 |
42 | resetGL(gl);
43 | //gl.useProgram(0);
44 | }
45 | }
46 |
47 | export default OpaqueProgram;
48 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | import GLBound from './gl-bound';
2 | import { mat4 } from 'gl-matrix';
3 |
4 | /**
5 | * ... In retrospect, I'm not sure exactly the purpose this class serves
6 | * It seems that ObjectRenderer inherits from this class, but it's also
7 | * the only renderer that's currently used.
8 | * TODO: Revisit this
9 | *
10 | * @class
11 | * @extends {GLBound}
12 | * @param {context} gl A WebGL context
13 | * @param {AssetManager} manager An AssetManager to manage GL-bound
14 | */
15 | class Renderer extends GLBound {
16 |
17 | constructor(gl, manager) {
18 | super(gl);
19 | this.manager = manager;
20 | this.viewProject = mat4.create();
21 | this.view = mat4.create();
22 | this.project = mat4.create();
23 | this.elapsed = 0;
24 | }
25 |
26 | /**
27 | * Update the internal view and projection matrices
28 | *
29 | * @param {Camera} camera The camera
30 | * @return {void}
31 | */
32 | updateView(camera) {
33 | this.view = camera.view;
34 | this.project = camera.project;
35 | mat4.multiply(this.viewProject, this.project, this.view);
36 | }
37 |
38 | /**
39 | * Actually controls the render loop?
40 | *
41 | * @abstract
42 | * @return {void}
43 | */
44 | render() {
45 | throw new Error('render() must be implemented');
46 | }
47 |
48 | /**
49 | * Updates the internal counter of elapsed time.
50 | *
51 | * @param {Number} delta Time elapsed since last render call
52 | * @return {void}
53 | */
54 | updateTime(delta) {
55 | this.elapsed += delta;
56 | }
57 | }
58 |
59 | export default Renderer;
60 |
--------------------------------------------------------------------------------
/src/renderer/object.js:
--------------------------------------------------------------------------------
1 | import Renderer from '../renderer';
2 | import Drawable from '../drawable';
3 |
4 | // TODO rework this.
5 | class ObjectRenderer extends Renderer {
6 | constructor(gl, manager) {
7 | super(gl, manager);
8 | this.drawables = [];
9 | }
10 |
11 | addDrawable(drawable, excludeChildren) {
12 | if(!(drawable instanceof Drawable))
13 | {
14 | return Promise.reject(new Error('Drawables must always inherit from the base Drawable'));
15 | }
16 | var promise = drawable.init(this.manager).catch((err) => {
17 | console.warn('could not initialize drawable: ', drawable); // eslint-disable-line no-console
18 | return Promise.reject(err);
19 | });
20 | if(drawable.updateView)
21 | {
22 | drawable.updateView(this.viewProject, null);
23 | }
24 | this.drawables.push(drawable);
25 | if(!excludeChildren) {
26 | drawable.children.forEach((c) => {
27 | this.addDrawable(c);
28 | });
29 | }
30 | return promise;
31 | }
32 |
33 | removeDrawable(drawable, destroy) {
34 | for(var i = 0; i < this.drawables.length; i++)
35 | {
36 | if(this.drawables[i] === drawable)
37 | {
38 | this.drawables.splice(i, 1);
39 | if(destroy) {
40 | drawable.dispose();
41 | return true;
42 | } else {
43 | return drawable;
44 | }
45 | }
46 | }
47 | return false;
48 | }
49 |
50 | addEntity(entity) {
51 | for(var i in entity.drawables) {
52 | this.addDrawable(entity.drawables[i]);
53 | }
54 | }
55 |
56 | updateView(camera) {
57 | super.updateView(camera);
58 | var i, len = this.drawables.length;
59 | for(i = 0; i < len; i++)
60 | {
61 | if(this.drawables[i].updateView) {
62 | this.drawables[i].updateView(this.viewProject, camera);
63 | }
64 | }
65 | }
66 |
67 | render() {
68 | var i, len = this.drawables.length;
69 | for(i = 0; i < len; i++)
70 | {
71 | this.drawables[i].draw();
72 | }
73 | }
74 |
75 | updateTime(delta) {
76 | super.updateTime(delta);
77 | var i, len = this.drawables.length;
78 | for(i = 0; i < len; i++)
79 | {
80 | // if these return false, remove them from the render loop:
81 | if(!this.drawables[i].updateTime(delta))
82 | {
83 | this.drawables.splice(i, 1);
84 | i--;
85 | len--;
86 | }
87 | }
88 | }
89 | }
90 |
91 | export default ObjectRenderer;
92 |
--------------------------------------------------------------------------------
/src/renderer/portal.js:
--------------------------------------------------------------------------------
1 | import Renderer from '../renderer';
2 |
3 | // TODO: rework this.
4 | class PortalRenderer extends Renderer {
5 | constructor(gl, manager) {
6 | super(gl, manager);
7 | this.portals = [];
8 | this.links = null;
9 | this.particles = null;
10 | }
11 |
12 | updateView(camera) {
13 | super.updateView(camera);
14 | var i, len = this.portals.length;
15 | for(i = 0; i < len; i++)
16 | {
17 | this.portals[i].updateView(this.viewProject, camera);
18 | }
19 | }
20 |
21 | render() {
22 | var i, len = this.portals.length;
23 | for(i = 0; i < len; i++)
24 | {
25 | this.portals[i].draw();
26 | }
27 | }
28 |
29 | updateTime(delta) {
30 | super.updateTime(delta);
31 | var i, len = this.portals.length;
32 | for(i = 0; i < len; i++)
33 | {
34 | // if these return false, remove them from the render loop:
35 | if(!this.portals[i].updateTime(delta))
36 | {
37 | this.portals.splice(i, 1);
38 | i--;
39 | len--;
40 | }
41 | }
42 | }
43 | }
44 |
45 | export default PortalRenderer;
46 |
--------------------------------------------------------------------------------
/src/texture.js:
--------------------------------------------------------------------------------
1 | import GLBound from './gl-bound';
2 |
3 | /**
4 | * A gl-bound texture
5 | * Supports most (all?) of the texture binding options.
6 | * Also generates mipmaps if the texture requires it.
7 | *
8 | * @class
9 | * @param {context} gl A WebGL context
10 | * @param {Object} info Texture parameters
11 | * @param {Images} image An image to use as the texture
12 | */
13 | class Texture extends GLBound {
14 |
15 | constructor(gl, info, image) {
16 | super(gl);
17 | this.info = info;
18 | var map = {
19 | 'MipMapLinearLinear': gl.LINEAR_MIPMAP_LINEAR,
20 | 'Linear': gl.LINEAR,
21 | 'MipMapLinearNearest': gl.LINEAR_MIPMAP_NEAREST,
22 | 'MipMapNearestLinear': gl.NEAREST_MIPMAP_LINEAR,
23 | 'Repeat': gl.REPEAT,
24 | 'ClampToEdge': gl.CLAMP_TO_EDGE
25 | };
26 | var texture = gl.createTexture();
27 | gl.bindTexture(gl.TEXTURE_2D, texture);
28 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, map[info.minFilter]);
29 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, map[info.magFilter]);
30 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, map[info.wrapS]);
31 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, map[info.wrapT]);
32 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
33 | if(/MipMap/.test(info.minFilter))
34 | {
35 | gl.generateMipmap(gl.TEXTURE_2D);
36 | }
37 |
38 | gl.bindTexture(gl.TEXTURE_2D, null);
39 |
40 | this.texture = texture;
41 | }
42 |
43 | /**
44 | * Bind the texture to a particular texture index
45 | *
46 | * @param {Number} index Texture index to bind to
47 | * @return {void}
48 | */
49 | use(index) {
50 | var gl = this._gl;
51 | index = index || 0;
52 | gl.bindTexture(gl.TEXTURE_2D, this.texture);
53 | gl.activeTexture(gl.TEXTURE0 + index);
54 | }
55 |
56 | /**
57 | * NYI: TODO
58 | *
59 | * @return {void}
60 | */
61 | dispose() {
62 | // TODO: Figure out when this should be called.
63 | // noop;
64 | }
65 | }
66 |
67 | export default Texture;
68 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import Constants from './constants';
2 | import TexturedDrawable from './drawable/textured';
3 |
4 | /**
5 | * Reset the GL state to some base state
6 | * @param {context} gl A WebGL context
7 | * @return {void}
8 | */
9 | export function resetGL(gl) {
10 | gl.lineWidth(1.0);
11 | gl.enable(gl.CULL_FACE);
12 | gl.frontFace(gl.CCW);
13 | gl.cullFace(gl.BACK);
14 | gl.enable(gl.DEPTH_TEST);
15 | gl.blendEquation(gl.FUNC_ADD);
16 | //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
17 | gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
18 | gl.disable(gl.BLEND);
19 | gl.depthMask(true);
20 | }
21 |
22 | /**
23 | * Set parameters base on some base set of defaults
24 | * @param {Object} base Parameter definition with defaults
25 | * @param {Object} opts Options (overrides)
26 | * @param {Boolean} deep Do deep copying on objects.
27 | * @return {Object} The base object
28 | */
29 | export function setParams(base, opts, deep) {
30 | for(var i in base)
31 | {
32 | if(base.hasOwnProperty(i) && opts.hasOwnProperty(i))
33 | {
34 | if(deep && typeof(base[i]) == 'object' && typeof(opts[i]) == 'object')
35 | {
36 | base[i] = setParams(base[i], opts[i], deep);
37 | }
38 | else
39 | {
40 | base[i] = opts[i];
41 | }
42 | }
43 | }
44 | return base;
45 | }
46 |
47 | /**
48 | * Disco portal animation
49 | * @param {Number} delta Time since last frame
50 | * @param {Number} elapsed Total time elapsed
51 | * @return {Boolean} Returns true to continue animation
52 | */
53 | export function disco(delta, elapsed) {
54 | var inc = elapsed / 1000;
55 | this.uniforms.u_baseColor[0] = Math.sin(inc);
56 | this.uniforms.u_baseColor[1] = Math.sin(inc + (2 * Math.PI / 3));
57 | this.uniforms.u_baseColor[2] = Math.sin(inc + (4 * Math.PI / 3));
58 | return true;
59 | }
60 |
61 | /**
62 | * Makes an artifact drawable class
63 | * @param {String} meshName Name of the mesh to use
64 | * @param {String} textureName Name of the texture to use
65 | * @return {ArtifactDrawable} A new drawable class for this artifact
66 | */
67 | export function makeArtifact(meshName, textureName) {
68 | class artifact extends TexturedDrawable {
69 | constructor() {
70 | super(Constants.Program.Textured, meshName, textureName);
71 | }
72 | }
73 |
74 | return artifact;
75 | }
76 |
77 | /**
78 | * Generate a set of artifacts
79 | *
80 | * @private
81 | * @param {String} series Series name
82 | * Should match the internal name of the resources
83 | * @param {Number} num Number of artifacts in the series
84 | * @param {Boolean} hasFrozen Whether or not the series also includes frozen
85 | * variants
86 | * @return {Object} Object containing artifact drawable classes
87 | * for each artifact.
88 | */
89 | export function generateArtifacts(series, num, hasFrozen) {
90 | var i, meshName, textureName = 'Artifact' + series + 'Texture';
91 |
92 | var artifacts = {};
93 |
94 | for(i = 1; i <= num; i++) {
95 | meshName = series + i;
96 | artifacts['' + i] = makeArtifact(meshName, textureName);
97 | }
98 | if(hasFrozen) {
99 | for(i = 1; i <= num; i++) {
100 | meshName = series + 'Frozen' + i;
101 | artifacts['Frozen' + i] = makeArtifact(meshName, textureName);
102 | }
103 | }
104 |
105 | return artifacts;
106 | }
107 |
--------------------------------------------------------------------------------
/src/vertex-attribute.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A vertex attribute
3 | *
4 | * @param {String} name Name of the attribute
5 | * @param {Number} size Size of the attribute (in bytes)
6 | * @param {Number} type The type of vertex attribute
7 | */
8 | class VertexAttribute {
9 | constructor(name, size, type) {
10 | this.name = name;
11 | this.size = size;
12 | this.type = type;
13 | }
14 | }
15 |
16 | export default VertexAttribute;
17 |
--------------------------------------------------------------------------------
/static/atmosphere.glsl.frag:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision mediump float;
3 | #endif
4 | varying vec3 vNormal;
5 | void main() {
6 | float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 1.0, 0 ) ), 12.0 );
7 | gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;
8 | }
--------------------------------------------------------------------------------
/static/atmosphere.glsl.vert:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision mediump float;
3 | #endif
4 | attribute vec3 a_position;
5 | attribute vec3 a_normal;
6 | uniform mat3 u_normalMatrix;
7 | uniform mat4 u_modelViewProject;
8 | varying vec3 vNormal;
9 | void main() {
10 | vNormal = normalize( u_normalMatrix * a_normal );
11 | gl_Position = u_modelViewProject * vec4( a_position, 1.0 );
12 | }
13 |
--------------------------------------------------------------------------------
/static/link3d.glsl.frag:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision mediump float;
3 | #endif
4 | uniform sampler2D u_texture;
5 | varying vec4 v_color;
6 | varying vec4 v_texCoord0And1;
7 | // this is unchanged from the original
8 | void main() {
9 | vec4 base = texture2D(u_texture, v_texCoord0And1.xy);
10 | vec4 scrolled = texture2D(u_texture, v_texCoord0And1.zw);
11 | float dots = scrolled.g;
12 | float brighten = mix(1.0, 2.0, base.b + (dots / 4.0));
13 | gl_FragColor.rgb = v_color.rgb * mix(0.35, brighten, dots);
14 | gl_FragColor.a = v_color.a * base.r;
15 | }
16 |
--------------------------------------------------------------------------------
/static/link3d.glsl.vert:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision mediump float;
3 | #endif
4 | uniform mat4 u_modelViewProject;
5 | uniform vec3 u_cameraFwd;
6 | uniform float u_elapsedTime;
7 | uniform mat4 u_model;
8 | attribute vec4 a_position;
9 | attribute vec2 a_texCoord0;
10 | attribute vec3 a_normal;
11 | attribute vec4 a_color;
12 | varying vec4 v_texCoord0And1;
13 | varying vec4 v_color;
14 | void main() {
15 | v_texCoord0And1.xy = (a_texCoord0 + vec2(0, u_elapsedTime * a_position.w * 0.6));
16 | v_texCoord0And1.zw = a_texCoord0 + vec2(0, u_elapsedTime * a_position.w);
17 | v_color = a_color;
18 | vec4 normal = u_model * vec4(a_normal.xyz, 0.0);
19 | float alpha = abs(dot(normalize(normal.xyz), u_cameraFwd));
20 | v_color.a *= (3.0 * alpha * alpha) - (2.0 * alpha * alpha * alpha);
21 | gl_Position = u_modelViewProject * vec4(a_position.xyz, 1.0);
22 | }
23 |
--------------------------------------------------------------------------------
/static/world.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeviateFish/ingress-model-viewer/acd83dca9ad9b152fb62a9b04efb5a068775cabc/static/world.jpg
--------------------------------------------------------------------------------
/test/all.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/all.js:
--------------------------------------------------------------------------------
1 | test("the base function exists", function() {
2 | ok(IMV);
3 | });
4 |
--------------------------------------------------------------------------------
/test/lib/qunit.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.12.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | overflow: hidden;
71 | }
72 |
73 | #qunit-userAgent {
74 | padding: 0.5em 0 0.5em 2.5em;
75 | background-color: #2b81af;
76 | color: #fff;
77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78 | }
79 |
80 | #qunit-modulefilter-container {
81 | float: right;
82 | }
83 |
84 | /** Tests: Pass/Fail */
85 |
86 | #qunit-tests {
87 | list-style-position: inside;
88 | }
89 |
90 | #qunit-tests li {
91 | padding: 0.4em 0.5em 0.4em 2.5em;
92 | border-bottom: 1px solid #fff;
93 | list-style-position: inside;
94 | }
95 |
96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97 | display: none;
98 | }
99 |
100 | #qunit-tests li strong {
101 | cursor: pointer;
102 | }
103 |
104 | #qunit-tests li a {
105 | padding: 0.5em;
106 | color: #c2ccd1;
107 | text-decoration: none;
108 | }
109 | #qunit-tests li a:hover,
110 | #qunit-tests li a:focus {
111 | color: #000;
112 | }
113 |
114 | #qunit-tests li .runtime {
115 | float: right;
116 | font-size: smaller;
117 | }
118 |
119 | .qunit-assert-list {
120 | margin-top: 0.5em;
121 | padding: 0.5em;
122 |
123 | background-color: #fff;
124 |
125 | border-radius: 5px;
126 | -moz-border-radius: 5px;
127 | -webkit-border-radius: 5px;
128 | }
129 |
130 | .qunit-collapsed {
131 | display: none;
132 | }
133 |
134 | #qunit-tests table {
135 | border-collapse: collapse;
136 | margin-top: .2em;
137 | }
138 |
139 | #qunit-tests th {
140 | text-align: right;
141 | vertical-align: top;
142 | padding: 0 .5em 0 0;
143 | }
144 |
145 | #qunit-tests td {
146 | vertical-align: top;
147 | }
148 |
149 | #qunit-tests pre {
150 | margin: 0;
151 | white-space: pre-wrap;
152 | word-wrap: break-word;
153 | }
154 |
155 | #qunit-tests del {
156 | background-color: #e0f2be;
157 | color: #374e0c;
158 | text-decoration: none;
159 | }
160 |
161 | #qunit-tests ins {
162 | background-color: #ffcaca;
163 | color: #500;
164 | text-decoration: none;
165 | }
166 |
167 | /*** Test Counts */
168 |
169 | #qunit-tests b.counts { color: black; }
170 | #qunit-tests b.passed { color: #5E740B; }
171 | #qunit-tests b.failed { color: #710909; }
172 |
173 | #qunit-tests li li {
174 | padding: 5px;
175 | background-color: #fff;
176 | border-bottom: none;
177 | list-style-position: inside;
178 | }
179 |
180 | /*** Passing Styles */
181 |
182 | #qunit-tests li li.pass {
183 | color: #3c510c;
184 | background-color: #fff;
185 | border-left: 10px solid #C6E746;
186 | }
187 |
188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
189 | #qunit-tests .pass .test-name { color: #366097; }
190 |
191 | #qunit-tests .pass .test-actual,
192 | #qunit-tests .pass .test-expected { color: #999999; }
193 |
194 | #qunit-banner.qunit-pass { background-color: #C6E746; }
195 |
196 | /*** Failing Styles */
197 |
198 | #qunit-tests li li.fail {
199 | color: #710909;
200 | background-color: #fff;
201 | border-left: 10px solid #EE5757;
202 | white-space: pre;
203 | }
204 |
205 | #qunit-tests > li:last-child {
206 | border-radius: 0 0 5px 5px;
207 | -moz-border-radius: 0 0 5px 5px;
208 | -webkit-border-bottom-right-radius: 5px;
209 | -webkit-border-bottom-left-radius: 5px;
210 | }
211 |
212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
213 | #qunit-tests .fail .test-name,
214 | #qunit-tests .fail .module-name { color: #000000; }
215 |
216 | #qunit-tests .fail .test-actual { color: #EE5757; }
217 | #qunit-tests .fail .test-expected { color: green; }
218 |
219 | #qunit-banner.qunit-fail { background-color: #EE5757; }
220 |
221 |
222 | /** Result */
223 |
224 | #qunit-testresult {
225 | padding: 0.5em 0.5em 0.5em 2.5em;
226 |
227 | color: #2b81af;
228 | background-color: #D2E0E6;
229 |
230 | border-bottom: 1px solid white;
231 | }
232 | #qunit-testresult .module-name {
233 | font-weight: bold;
234 | }
235 |
236 | /** Fixture */
237 |
238 | #qunit-fixture {
239 | position: absolute;
240 | top: -10000px;
241 | left: -10000px;
242 | width: 1000px;
243 | height: 1000px;
244 | }
245 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | var path = require('path');
3 |
4 | module.exports = {
5 | entry: path.resolve(__dirname, 'src', 'ingress-model-viewer.js'),
6 | output: {
7 | path: path.resolve(__dirname, 'dist'),
8 | filename: 'ingress-model-viewer.js',
9 | libraryTarget: 'var',
10 | library: "IMV"
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.js$/,
16 | exclude: /(node_modules|bower_components)/,
17 | loader: 'babel-loader',
18 | query: {
19 | presets: ['es2015']
20 | }
21 | },
22 | {
23 | test: /\.js$/,
24 | exclude: /(node_modules|bower_components)/,
25 | loader: "eslint-loader",
26 | options: {}
27 | },
28 | ]
29 | },
30 | devServer: {
31 | contentBase: path.join(__dirname),
32 | port: 8080,
33 | publicPath: '/dist/'
34 | }
35 | };
36 |
--------------------------------------------------------------------------------