4 |
5 | QUnit Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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}
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/demo/clean-slate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
15 |
16 |
17 |
18 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/snapshots.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/demo/link-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ingress Model Sandbox
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/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.