Ignore:
Timestamp:
08/03/17 15:17:23 (8 years ago)
Author:
dlcheng
Message:

CHG: Updating js scripts.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • issm/trunk-jpl/src/m/plot/webgl.js

    r21768 r21911  
    99                if (!isEmptyOrUndefined(canvas.animation) && canvas.animation.handler !== 0) { clearInterval(canvas.animation.handler); }
    1010                initWebGL(canvas, options);
    11                 initializeMarker(canvas);
    1211                draw(canvas);
    1312                canvas.initialized = true;
     13               
     14                //The onStart event triggers once per plotmodel call load after WebGL and canvas initialization are complete
     15                canvas.selector.trigger('onStart', [canvas]);
    1416        }
    1517        return canvas;
     
    3436                mc.add(new Hammer.Pan({threshold: 0, pointers: 0}));
    3537                mc.add(new Hammer.Pinch({threshold: 0})).recognizeWith(mc.get('pan'));
    36                 mc.on('tap press', function (ev) {onTap(ev, canvas);});
     38                mc.on('tap', function (ev) {onTap(ev, canvas);});
    3739                mc.on('panstart panmove', function (ev) {onPan(ev, canvas, displayview);});
    38                 mc.on('pinchstart pinchmove', function (ev) {onPinch(ev, canvas, displayview);});               
     40                mc.on('pinchstart pinchmove', function (ev) {onPinch(ev, canvas, displayview);});
    3941                canvas.addEventListener('mousewheel', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
    4042                canvas.addEventListener('DOMMouseScroll', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
     
    4648                canvas.unitData = {};
    4749                canvas.unitMovieData = {};
     50               
    4851                canvas.gl = gl;
    4952                canvas.rootPath = options.getfieldvalue('rootpath', '../../../js/');
     
    5760       
    5861        //Add context state variables
     62        canvas.render = options.getfieldvalue('render', {});
    5963        canvas.controlSensitivity = options.getfieldvalue('controlsensitivity', 1);
     64        canvas.overlayHandlers = {};
    6065        var backgroundcolor = new RGBColor(options.getfieldvalue('backgroundcolor', 'lightcyan'));
    6166        if (backgroundcolor.ok) { canvas.backgroundcolor = [backgroundcolor.r/255.0, backgroundcolor.g/255.0, backgroundcolor.b/255.0, 1.0]; }
     
    6368       
    6469        //Property intiialization, using values from options first, then from default values.
     70        var atmosphere = options.getfieldvalue('atmosphere', {});
     71        canvas.atmosphere = {                                                                                           //Default Values
     72                wavelength_r: defaultFor(atmosphere.wavelength_r,                       0.65),          //0.65          Red wavelength (micrometers)
     73                wavelength_g: defaultFor(atmosphere.wavelength_g,                       0.57),          //0.57          Green wavelength (micrometers)
     74                wavelength_b: defaultFor(atmosphere.wavelength_b,                       0.475),         //0.475         Green wavelength (micrometers)
     75                eSun:   defaultFor(atmosphere.eSun,                                             100.0),         //20.0          Sun intensity   
     76                kRayleigh: defaultFor(atmosphere.kRayleigh,                             0.0025),        //0.0025        Rayleigh scattering amount
     77                kMie: defaultFor(atmosphere.kMie,                                                       0.000),         //0.01          Mie scattering amount
     78                g: defaultFor(atmosphere.g,                                                             -0.99),         //-0.99         Mie phase asymmetry/direction factor
     79                hdr_exposure: defaultFor(atmosphere.hdr_exposure,                       0.8),           //0.8           High Dynamic Range Exposure
     80                scaleHeight: defaultFor(atmosphere.scaleHeight,                         1.25),          //1.025         Scale of height of atmosphere to earth radius.
     81                scaleDepth: defaultFor(atmosphere.scaleDepth,                           0.25),          //0.25          Percentage altitude at which the atmosphere's average density is found
     82                a: defaultFor(atmosphere.a,                                                             -0.00287),      //-0.00287      Scaling constant a
     83                b: defaultFor(atmosphere.b,                                                             0.459),         //0.459         Scaling constant b
     84                c: defaultFor(atmosphere.c,                                                             3.83),          //3.83          Scaling constant c
     85                d: defaultFor(atmosphere.d,                                                             -6.80),         //-6.80         Scaling constant d
     86                e: defaultFor(atmosphere.e,                                                             3.6),           //5.25          Scaling constant e. Lower when increasing atmosphere scale.
     87                attenuation: defaultFor(atmosphere.attenuation,                         0.5)            //0.5           Strength of atmospheric scattering on ground shading.
     88        };
     89        updateAtmosphereParameters(canvas);
    6590        var animation = options.getfieldvalue('movies', {});
    6691        canvas.animation = {
    67                 frame: defaultFor(animation.frame, 0),
    68                 play: defaultFor(animation.play, true),
    69                 increment: defaultFor(animation.increment, true),
    70                 fps: defaultFor(animation.fps, 4),
    71                 interval: defaultFor(animation.interval, 1000 / animation.fps),
    72                 loop: defaultFor(animation.loop, true),
    73                 handler: defaultFor(animation.handler, 0)
     92                frame: defaultFor(animation.frame,                                                      0),
     93                play: defaultFor(animation.play,                                                        true),
     94                increment: defaultFor(animation.increment,                                      true),
     95                fps: defaultFor(animation.fps,                                                          4),
     96                interval: defaultFor(animation.interval,                                        1000 / defaultFor(animation.fps, 4)),
     97                loop: defaultFor(animation.loop,                                                        true),
     98                handler: defaultFor(animation.handler,                                          0)
    7499        }
    75100        var brush = options.getfieldvalue('brush', {});
    76101        canvas.brush = {
    77                 enabled: defaultFor(brush.enabled, false),
    78                 strength: defaultFor(brush.strength, 0.075),
    79                 falloff: defaultFor(brush.falloff, 0.5),
    80                 hit: defaultFor(brush.hit, {})
     102                enabled: defaultFor(brush.enabled,                                                      false),
     103                strength: defaultFor(brush.strength,                                            0.075),
     104                falloff: defaultFor(brush.falloff,                                                      0.5),
     105                hit: defaultFor(brush.hit,                                                                      {})
    81106        };
    82107        var camera = options.getfieldvalue('camera', {});
    83108        canvas.camera = {
    84                 position: defaultFor(camera.position, vec3.create()),
    85                 rotation: defaultFor(camera.rotation, vec3.create()),
    86                 near: defaultFor(camera.near, 1e3),
    87                 far: defaultFor(camera.far, 1e10),
    88                 fov: defaultFor(camera.fov, 45),
    89                 vMatrix: defaultFor(camera.vMatrix, mat4.create()),
    90                 pMatrix: defaultFor(camera.pMatrix, mat4.create()),
    91                 vpMatrix: defaultFor(camera.vpMatrix, mat4.create()),
    92                 vInverseMatrix: defaultFor(camera.vInverseMatrix, mat4.create()),
    93                 pInverseMatrix: defaultFor(camera.pInverseMatrix, mat4.create()),
    94                 vpInverseMatrix: defaultFor(camera.vpInverseMatrix, mat4.create()),
    95                 ready: defaultFor(camera.ready, false)
    96         };
    97         var clouds = options.getfieldvalue('clouds', {});
    98         canvas.clouds = {
    99                 enabled: defaultFor(clouds.enabled, false),
    100                 height: defaultFor(clouds.height, 7500),
    101                 quantity: defaultFor(clouds.quantity, 10),
    102                 hit: defaultFor(clouds.hit, {})
     109                position: defaultFor(camera.position,                                           vec3.create()),
     110                rotation: defaultFor(camera.rotation,                                           quat.create()),
     111                relativePosition: defaultFor(camera.relativePosition,           vec3.create()),
     112                direction: defaultFor(camera.direction,                                         vec3.create()),
     113                near: defaultFor(camera.near,                                                           1e3),
     114                far: defaultFor(camera.far,                                                             1e10),
     115                fov: defaultFor(camera.fov,                                                             45),
     116                vMatrix: defaultFor(camera.vMatrix,                                             mat4.create()),
     117                pMatrix: defaultFor(camera.pMatrix,                                             mat4.create()),
     118                vpMatrix: defaultFor(camera.vpMatrix,                                           mat4.create()),
     119                vInverseMatrix: defaultFor(camera.vInverseMatrix,                       mat4.create()),
     120                pInverseMatrix: defaultFor(camera.pInverseMatrix,                       mat4.create()),
     121                vpInverseMatrix: defaultFor(camera.vpInverseMatrix,             mat4.create()),
     122                ready: defaultFor(camera.ready,                                                         false)
    103123        };
    104124        var dataMarkers = options.getfieldvalue('datamarkers', {});
    105125        canvas.dataMarkers = {
    106                 enabled: defaultFor(dataMarkers.enabled, true),
    107                 values: defaultFor(dataMarkers.values, []),
    108                 image: defaultFor(dataMarkers.image, canvas.rootPath+'textures/data_marker.svg'),
    109                 size: defaultFor(dataMarkers.size, [32, 32]),
    110                 format: defaultFor(dataMarkers.format, ['X: %.2e<br>Y: %.2e<br>Z: %.2e<br>Value: %0.1f', 'x', 'y', 'z', 'value']),
    111                 animated: defaultFor(dataMarkers.animated, false),
    112                 labels: defaultFor(dataMarkers.labels, []),
    113                 font: defaultFor(dataMarkers.font, ''),
    114                 marker: defaultFor(dataMarkers.marker, document.getElementById('sim-data-marker-' + canvas.id)),
    115                 reposition: defaultFor(dataMarkers.reposition, true)
     126                enabled: defaultFor(dataMarkers.enabled,                                        true),
     127                values: defaultFor(dataMarkers.values,                                          []),
     128                image: defaultFor(dataMarkers.image,                                            canvas.rootPath + 'textures/data_marker.svg'),
     129                size: defaultFor(dataMarkers.size,                                                      [32, 32]),
     130                format: defaultFor(dataMarkers.format,                                          ['X: %.2e<br>Y: %.2e<br>Z: %.2e<br>Value: %0.1f', 'x', 'y', 'z', 'value']),
     131                animated: defaultFor(dataMarkers.animated,                                      false),
     132                labels: defaultFor(dataMarkers.labels,                                          []),
     133                font: defaultFor(dataMarkers.font,                                                      ''),
     134                marker: defaultFor(dataMarkers.marker,                                          document.getElementById('sim-data-marker-' + canvas.id)),
     135                reposition: defaultFor(dataMarkers.reposition,                          true)
    116136        };
    117137        var draw = options.getfieldvalue('draw', {});
    118138        canvas.draw = {
    119                 ready: defaultFor(draw.ready, false),
    120                 handler: defaultFor(draw.handler, null)
     139                ready: defaultFor(draw.ready,                                                           false),
     140                handler: defaultFor(draw.handler,                                                       null)
    121141        };
    122142        var view = options.getfieldvalue('view', {});
    123143        canvas.view = {
    124                 position: defaultFor(view.position, [0.0, 0.0, 0.0]),
    125                 rotation: defaultFor(view.rotation, [0, 90]),
    126                 zoom: defaultFor(view.zoom, 1.0),
    127                 zoomLimits: defaultFor(view.zoomLimits, [0.001, 100.0]),
    128                 lastZoom: defaultFor(view.lastZoom, 1.0),
    129                 azimuthLimits: defaultFor(view.azimuthLimits, [0, 360]),
    130                 elevationLimits: defaultFor(view.elevationLimits, [-180, 180]),
    131                 panningEnabled: defaultFor(view.panningEnabled, false),
    132                 twod: defaultFor(view.twod, false)
     144                position: defaultFor(view.position,                                             [0.0, 0.0, 0.0]),
     145                rotation: defaultFor(view.rotation,                                             [0, 90]),
     146                zoom: defaultFor(view.zoom,                                                             1.0),
     147                zoomLimits: defaultFor(view.zoomLimits,                                         [0.001, 100.0]),
     148                lastZoom: defaultFor(view.lastZoom,                                             1.0),
     149                azimuthLimits: defaultFor(view.azimuthLimits,                           [0, 360]),
     150                elevationLimits: defaultFor(view.elevationLimits,                       [-180, 180]),
     151                panningEnabled: defaultFor(view.panningEnabled,                         false),
     152                preventDefaultOnPan: defaultFor(view.preventDefaultOnPan,       false),
     153                twod: defaultFor(view.twod,                                                             false)
    133154        };
    134155
     
    171192        var shaders = {};
    172193        shaders.Colored = new GL.Shader.fromURL(rootPath+'shaders/Colored.vsh', rootPath+'shaders/Colored.fsh', null, gl);
     194        shaders.ColoredDiffuse = new GL.Shader.fromURL(rootPath+'shaders/ColoredDiffuse.vsh', rootPath+'shaders/ColoredDiffuse.fsh', null, gl);
    173195        shaders.Textured = new GL.Shader.fromURL(rootPath+'shaders/Textured.vsh', rootPath+'shaders/Textured.fsh', null, gl);
     196        shaders.TexturedDiffuse = new GL.Shader.fromURL(rootPath+'shaders/TexturedDiffuse.vsh', rootPath+'shaders/TexturedDiffuse.fsh', null, gl);
    174197        shaders.SkyFromSpace = new GL.Shader.fromURL(rootPath+'shaders/SkyFromSpace.vert', rootPath+'shaders/SkyFromSpace.frag', null, gl);
    175198        shaders.GroundFromSpace = new GL.Shader.fromURL(rootPath+'shaders/GroundFromSpace.vert', rootPath+'shaders/GroundFromSpace.frag', null, gl);
    176         shaders.Clouds = new GL.Shader.fromURL(rootPath+'shaders/Clouds.vert', rootPath+'shaders/Clouds.frag', null, gl);
    177199        return shaders;
    178200} //}}}
     
    184206        return gl.textures[imageSource];
    185207} //}}}
     208function updateAtmosphereParameters(canvas) {
     209        //Precalculate derived atmosphere shader parameters
     210        //TODO: Find a better way to structure this
     211        var atm = canvas.atmosphere;
     212        atm.inv_wavelength4 = [1.0 / Math.pow(atm.wavelength_r, 4), 1.0 / Math.pow(atm.wavelength_g, 4), 1.0 / Math.pow(atm.wavelength_b, 4)];
     213        atm.innerRadius = 6.371e6;
     214        atm.innerRadius2 = atm.innerRadius * atm.innerRadius;
     215        atm.outerRadius = atm.innerRadius * atm.scaleHeight;
     216        atm.outerRadius2 = atm.outerRadius * atm.outerRadius;
     217        atm.krESun = atm.kRayleigh * atm.eSun;
     218        atm.kmESun = atm.kMie * atm.eSun;
     219        atm.kr4PI = atm.kRayleigh * 4 * Math.PI;
     220        atm.km4PI = atm.kMie * 4 * Math.PI;
     221        atm.scale = 1.0 / (atm.outerRadius - atm.innerRadius);
     222        atm.scaleOverScaleDepth = atm.scale / atm.scaleDepth;
     223        atm.g2 = atm.g * atm.g;
     224        canvas.atmosphere = atm;
     225}
    186226function clamp(value, min, max) { //{{{
    187227        return Math.max(min, Math.min(value, max));
     
    253293} //}}}
    254294//}}}
    255 //{{{ Interface Functions
     295//{{{ Interaction Functions
    256296function onTap(ev, canvas) { //{{{
    257         //Sets up a marker on a canvas that will track a point on the mesh. Can be dismissed by closing the display or clicking the marker.
    258297        ev.preventDefault();
    259298       
    260         if (canvas.clouds.enabled) {
    261                 for (var i = 0; i < canvas.clouds.quantity; i++) {
    262                         raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes["clouds" + i]);
    263                 }
    264         }
    265         var hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.unitNode);
    266         if ('cities' in canvas.nodes) {
    267                 var hitCities = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes.cities);
    268                 canvas.nodes.cities.hit = hitCities;
    269                 updateCities(canvas);
    270         }
    271 
    272         canvas.brush.hit = hit;
    273        
    274         if (canvas.dataMarkers.enabled) {
    275                 canvas.dataMarkers.marker.selector.closed = false;
    276                 canvas.dataMarkers.marker.hit = hit;
    277                 updateMarker(canvas, true);
    278         }
    279        
    280         brushModify(canvas);
     299        var hit = raycastXY(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.unitNode);
     300
     301        //Trigger any handlers attatched to this canvas event.
     302        canvas.selector.trigger('onTap', [ev, canvas, hit]);
    281303} //}}}
    282304function onPan(ev, canvas, displaylog) { //{{{
    283305        ev.preventDefault();
    284        
    285         if (canvas.dataMarkers.enabled) {
    286                 if (!isEmptyOrUndefined(canvas.unitNode)) {
    287                         canvas.brush.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.unitNode);
    288                         brushModify(canvas);
    289                 }
    290         }
    291        
    292         if (canvas.clouds.enabled) {
    293                 if (!isEmptyOrUndefined(canvas.nodes['overlay'])) {
    294                         canvas.clouds.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes['overlay']);
    295                         updateClouds(canvas);
    296                 }
    297         }
    298306       
    299307        if (ev.type === 'panstart') {
     
    301309                canvas.lastDeltaY = 0;
    302310        }
    303         if (ev.srcEvent.shiftKey || ev.pointers.length === 2) {
    304                 if (!canvas.view.panningEnabled) return;
    305                 var deltaX = (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
    306                 var deltaY = (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
    307                
    308                 if (canvas.view.twod) {
    309                         canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * 0) * deltaY;
    310                         canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * 0) * deltaY;
     311       
     312        //Trigger any handlers attatched to this canvas event.
     313        canvas.selector.trigger('onPan', [ev, canvas]);
     314       
     315        //If any onPan handler sets preventDefaultOnPan to true, skips default onPan camera behavior
     316        if (!canvas.view.preventDefaultOnPan) {
     317                //If panning with two fingers or shift key, translate camera center
     318                if (ev.srcEvent.shiftKey || ev.pointers.length === 2) {
     319                        if (canvas.view.panningEnabled) {
     320                                var deltaX = (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
     321                                var deltaY = (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
     322                               
     323                                //TODO: convert canvas.view.rotation from az/el euler to quaternion
     324                                if (canvas.view.twod) {
     325                                        canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * 0) * deltaY;
     326                                        canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * 0) * deltaY;
     327                                }
     328                                else {
     329                                        canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaY;
     330                                        canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaY;
     331                                }
     332                        }
    311333                }
     334                //Else, rotate around camera center
    312335                else {
    313                         canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaY;
    314                         canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaY;
     336                        canvas.view.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * 2 * canvas.controlSensitivity * RAD2DEG;
     337                        canvas.view.rotation[1] += (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight * -2 * canvas.controlSensitivity * RAD2DEG;
     338                       
     339                        if (canvas.view.rotation[0] > 360) { canvas.view.rotation[0] -= 360; };
     340                        if (canvas.view.rotation[0] < -360) { canvas.view.rotation[0] += 360; };
     341                        if (canvas.view.rotation[1] > 180) { canvas.view.rotation[1] -= 360; };
     342                        if (canvas.view.rotation[1] < -180) { canvas.view.rotation[1] += 360; };
     343                       
     344                        canvas.view.rotation[0] = clamp(canvas.view.rotation[0], canvas.view.azimuthLimits[0], canvas.view.azimuthLimits[1]);
     345                        canvas.view.rotation[1] = clamp(canvas.view.rotation[1], canvas.view.elevationLimits[0], canvas.view.elevationLimits[1]);
     346                       
     347                        if (displaylog) { console.log(canvas.view.rotation); }
    315348                }
    316         }
    317        
    318         else {
    319                 canvas.view.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * 2 * canvas.controlSensitivity * RAD2DEG;
    320                 canvas.view.rotation[1] += (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight * -2 * canvas.controlSensitivity * RAD2DEG;
    321                
    322                 if (canvas.view.rotation[0] > 360) { canvas.view.rotation[0] -= 360; };
    323                 if (canvas.view.rotation[0] < -360) { canvas.view.rotation[0] += 360; };
    324                 if (canvas.view.rotation[1] > 180) { canvas.view.rotation[1] -= 360; };
    325                 if (canvas.view.rotation[1] < -180) { canvas.view.rotation[1] += 360; };
    326                
    327                 canvas.view.rotation[0] = clamp(canvas.view.rotation[0], canvas.view.azimuthLimits[0], canvas.view.azimuthLimits[1]);
    328                 canvas.view.rotation[1] = clamp(canvas.view.rotation[1], canvas.view.elevationLimits[0], canvas.view.elevationLimits[1])
    329         }
     349        }       
     350       
     351        canvas.view.preventDefaultOnPan = false;
    330352        canvas.lastDeltaX = ev.deltaX;
    331353        canvas.lastDeltaY = ev.deltaY;
    332        
    333         canvas.dataMarkers.reposition = true;
    334        
    335         if (displaylog) { console.log(canvas.view.rotation); }
    336354} //}}}
    337355function onPinch(ev, canvas, displaylog) { //{{{
    338356        ev.preventDefault();
    339357        if (ev.type === 'pinchstart') { canvas.view.lastZoom = canvas.view.zoom; }
    340         else { modifyZoom(ev.scale * canvas.view.lastZoom, canvas, displaylog); }
     358        else { modifyZoom(ev.scale * canvas.view.lastZoom, canvas, displaylog, ev, 0); }
    341359} //}}}
    342360function onZoom(ev, canvas, displaylog) { //{{{
    343361        ev.preventDefault();
    344         var delta = clamp(ev.scale || ev.wheelDelta || -ev.detail, -1, 1) * canvas.controlSensitivity * canvas.view.zoom / 20;
    345         modifyZoom(canvas.view.zoom + delta, canvas, displaylog);
    346 } //}}}
    347 function modifyZoom(value, canvas, displaylog) { //{{{
    348         canvas.view.zoom = clamp(value, canvas.view.zoomLimits[0], canvas.view.zoomLimits[1]);
    349         canvas.dataMarkers.reposition = true;
    350         if (displaylog) { console.log(canvas.view.zoom); }
    351 } //}}}
    352 function modifyDataMarkersEnabled(value, canvas) { //{{{
    353         canvas.dataMarkers.enabled = value;
     362        var delta = clamp(ev.scale || ev.wheelDelta || -ev.detail, -1, 1) * canvas.controlSensitivity * canvas.view.zoom / 2;
     363        modifyZoom(canvas.view.zoom + delta, canvas, displaylog, ev, 0);
     364} //}}}
     365function modifyZoom(value, canvas, displaylog, ev, duration) { //{{{
     366        if (isEmptyOrUndefined(duration)) duration = 200;
     367        var targetZoom = clamp(value, canvas.view.zoomLimits[0], canvas.view.zoomLimits[1]);
     368        var currentZoom = canvas.view.zoom;
     369        animateValue(
     370                0,
     371                1.0,
     372                duration,
     373                function(value, info) {
     374                        canvas.view.zoom = currentZoom * (1 - value) + targetZoom * value;
     375                        if (displaylog) { console.log(canvas.view.zoom); }
     376                       
     377                        //Trigger any handlers attatched to this canvas event.
     378                        canvas.selector.trigger('onZoom', [ev, canvas]);
     379                }
     380        );
    354381} //}}}
    355382function toggleMoviePlay(canvas) { //{{{
     
    364391        }
    365392} //}}}
    366 //}}}
    367 //{{{ Interaction Functions
    368 function raycast(canvas, x, y, node) { //{{{
    369         //Performs raycast on canvas.unitNode.mesh using x/y screen coordinates. Returns hit objects with hit position, coords, and indicies of ray-triangle intersection.
     393function screenToWorldPoint(canvas, x, y) { //{{{
     394        var viewportX = (x - canvas.width / 2) / (canvas.width / 2);
     395        var viewportY = (canvas.height / 2 - y) / (canvas.height / 2);
     396        var origin = vec3.transformMat4(vec3.create(), [viewportX, viewportY, 0], canvas.camera.vpInverseMatrix);
     397        return origin;
     398} //}}}
     399function screenToModelRay(canvas, x, y, node) { //{{{
     400        var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.camera.vpMatrix, node.modelMatrix));
     401        var viewportX = (x - canvas.width / 2) / (canvas.width / 2);
     402        var viewportY = (canvas.height / 2 - y) / (canvas.height / 2);
     403        var origin = vec3.transformMat4(vec3.create(), [viewportX, viewportY, 0], inverseMVPMatrix);
     404        var far = vec3.transformMat4(vec3.create(), [viewportX, viewportY, 1.0], inverseMVPMatrix);
     405        var direction = vec3.subtract(vec3.create(), far, origin);
     406        return {'origin':origin, 'direction':direction};
     407} //}}}
     408function raycast(canvas, origin, direction, node) { //{{{
     409        //Performs raycast on given node using ray origin and direction vectors.
     410        //Returns hit objects with hit position, normals, barycentric coordinates, element number, and indices of ray-triangle intersection.
    370411        //TODO: Diagnose marker issues with orthographic views and slr-eustatic updates when switching between basins.
    371         var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.camera.vpMatrix, node.modelMatrix));
    372         var origin = vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 0], inverseMVPMatrix);
    373         var far = far || vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 1.0], inverseMVPMatrix);
    374         var ray = vec3.subtract(vec3.create(), far, origin);
    375 
    376         var mesh = node.mesh;
    377 
    378         if (!mesh) { return; }
    379         if (!node.octree) { node.octree = new GL.Octree(mesh); }
    380        
    381         var hit = node.octree.testRay(origin, ray, 1e3, 1e10);
    382        
    383         if(!hit) { return; }
    384 
    385         if (node.name.startsWith("clouds")) { canvas.clouds.selected = node.name; alert("New selected cloud: " + canvas.clouds.selected); }
     412        if (!node.octree) { node.octree = new GL.Octree(node.mesh); }
     413       
     414        var hit = node.octree.testRay(origin, direction, 1e3, 1e10);
     415        if (!hit) { return; }
    386416
    387417        hit.modelPos = vec3.copy(vec3.create(), hit.pos);
    388418        vec3.transformMat4(hit.pos, hit.pos, node.modelMatrix);
    389         vec3.transformMat4(hit.normal, hit.normal, node.modelMatrix);
    390 
     419       
    391420        return hit;
    392421} //}}}
    393 function updateCities(canvas) {
    394         //Update selected city
    395         var hit = canvas.nodes.cities.hit;
    396         if (hit) {
    397                 citiesIndex = Math.floor(hit.indices[0] / ((ArrayMax(canvas.nodes.cities.mesh.getIndexBuffer('triangles').data) + 1) / cities.length));
    398                 cityName = cities[citiesIndex];
    399                 if (cityName !== $('#gfm-sim-controls-select-city').val()) {
    400                         $('#gfm-sim-controls-select-city').val(cityName).selectmenu("refresh");
    401                         changeCity(canvas);
    402                 }
    403         }
    404 }
    405 function brushModify(canvas) { //{{{
    406         //This function takes in the canvas and x/y coordinates, performing a raycast against the mesh, and modifies the mesh using a the canvas.brush.strength and canvas.brush.falloff properties.
    407         //Currently the brush extends to the raycasted element and its immediate neighbors.
    408         //TODO: Allow variable brush size/additional neighbors. Allow the function to work on multiple models (currently hardcoded to md model).
    409         if (!canvas.unitNode || canvas.brush.enabled != 'on') { return; }
    410        
    411         var hit = canvas.brush.hit;
    412 
    413         if (hit) {
    414                 var bufferVertices = canvas.unitNode.mesh.getBuffer('vertices');
    415                 var vertices = bufferVertices.data;
    416                 var bufferCoords = canvas.unitNode.mesh.getBuffer('coords');
    417                 var coords = bufferCoords.data;
    418 
    419                 //Query nearby elements and store indicies of affected vertices using pregenerated vertexconnectivity list (from NodeConnectivity)
    420                 var baseIndices = new Set(hit.indices);
    421                 var connectedIndices = new Set(hit.indices);
    422                 var connectedElement;
    423                 var indices;
    424                 var lengthIndex = md.mesh.vertexconnectivity[0].length - 1;
    425                 var length;
    426                 for (var i = 0; i < 3; i++) {
    427                         length = md.mesh.vertexconnectivity[hit.indices[i]][lengthIndex];
    428                         for (var j = 0; j < length; j++) {
    429                                 //Shift elements down by one (matlab 1-based index to 0-based index)
    430                                 connectedElement = md.mesh.vertexconnectivity[hit.indices[i]][j] - 1;
    431                                 indices = md.mesh.elements[connectedElement];
    432                                 connectedIndices.add(indices[0] - 1);
    433                                 connectedIndices.add(indices[1] - 1);
    434                                 connectedIndices.add(indices[2] - 1);
    435                         }
    436                 }
    437 
    438                 //Apply modifications to included vertices in mesh using brush strength and falloff.
    439                 var strength;
    440                 for (var index of connectedIndices) {
    441                         if (!baseIndices.has(index)) {
    442                                 strength = canvas.brush.strength * canvas.brush.falloff;
    443                         }
    444                         else {
    445                                 strength = canvas.brush.strength;
    446                         }
    447                         vertices[index*3+2] += strength * 100;
    448                         md.geometry.surface[index] += strength;
    449                         md.geometry.thickness[index] += strength;
    450                         coords[index*2+1] += strength;
    451                         canvas.unitData[index] += strength;
    452                 }
    453                
    454                 //Update mesh on GPU
    455                 bufferVertices.upload(canvas.gl.DYNAMIC_DRAW);
    456                 bufferCoords.upload(canvas.gl.DYNAMIC_DRAW);
    457                 canvas.unitNode.octree = new GL.Octree(canvas.unitNode.mesh);   
    458         }
    459 } //}}}
    460 function updateClouds(canvas) {
    461         //Update clouds if rendered
    462         //TODO: Steven, the hit now queries the radaroverlay.
    463         if (canvas.nodes[canvas.clouds.selected]) {
    464                 var v1 = vec3.fromValues(vertices[hit.indices[0] * 3], vertices[hit.indices[0] * 3 + 1], vertices[hit.indices[0] * 3 + 2]);
    465                 var v2 = vec3.fromValues(vertices[hit.indices[1] * 3], vertices[hit.indices[1] * 3 + 1], vertices[hit.indices[1] * 3 + 2]);
    466                 var v3 = vec3.fromValues(vertices[hit.indices[2] * 3], vertices[hit.indices[2] * 3 + 1], vertices[hit.indices[2] * 3 + 2]);
    467                 vec3.transformMat4(v1, v1, canvas.unitNode.modelMatrix);//move out of brushModify, perhaps onto onPan
    468                 vec3.transformMat4(v2, v2, canvas.unitNode.modelMatrix);
    469                 vec3.transformMat4(v3, v3, canvas.unitNode.modelMatrix);
    470                 var x  = (v1[0] + v2[0] + v3[0]) / 3;// + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
    471                 var y  = (v1[1] + v2[1] + v3[1]) / 3;// + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
    472                 var z  = (v1[2] + v2[2] + v3[2]) / 3;
    473                 canvas.nodes[canvas.clouds.selected].translation = [x, y + canvas.clouds.height, z];
    474                 updateModelMatrix(canvas.nodes[canvas.clouds.selected]);
    475         }
    476 }
    477 function initializeMarker(canvas) { //{{{
    478         //Initialize data marker and tooltip display once per page load
    479         var marker = $('#' + canvas.dataMarkers.marker.id);
    480         var size = canvas.dataMarkers.size;
    481         if (!marker.hasClass('tooltipstered')) {
    482                 marker.css({
    483                         'position': 'absolute',
    484                         'left': -size[0] + 'px',
    485                         'top': -size[1] + '0px',
    486                         'width': size[0] + 'px',
    487                         'height': size[1] + 'px',
    488                         'pointer-events': 'all',
    489                         'cursor': 'pointer',
    490                         'display': 'none'
    491                 });
    492                 marker.tooltipster({
    493                         contentAsHTML: true,
    494                         maxWidth: 320,
    495                         maxHeight: 320,
    496                         zIndex: 1000,
    497                         trigger: 'custom',
    498                         triggerOpen: {
    499                                 mouseenter: false,
    500                                 originClick: true,
    501                                 touchstart: false
    502                         },
    503                         triggerClose: {
    504                                 mouseleave: false,
    505                                 originClick: true,
    506                                 touchleave: false
    507                         },
    508                 });
    509                 marker.on('click touch', function () {
    510                         marker.fadeOut(175);
    511                         marker.tooltipster('close');
    512                         marker.closed = true;
    513                 });
    514                 marker.closed = false;
    515                 canvas.dataMarkers.marker.selector = marker;
    516         }
    517         updateMarker(canvas, true);
    518 } //}}}
    519 function repositionMarker(canvas) { //{{{
    520         //Mover marker to point to mouse position, offset in y by 1 to enable immediate clicking.
    521         //Return if no marker hit exists, the camera is not rendering, or if no reposition has been scheduled.
    522         if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit) || !canvas.camera.ready || !canvas.dataMarkers.reposition) { return; }
    523         var size = canvas.dataMarkers.size;
    524         var screenPoint = vec3.transformMat4(vec3.create(), canvas.dataMarkers.marker.hit.pos, canvas.camera.vpMatrix);
    525         var x = (screenPoint[0] + 1.0) * (canvas.width / 2) + canvas.selector.offset().left;
    526         var y = (-screenPoint[1] + 1.0) * (canvas.height / 2) + canvas.selector.offset().top;
    527         canvas.dataMarkers.marker.selector.css({
    528                 'left': (Math.round(x) - size[0] / 2) + 'px',
    529                 'top': (Math.round(y) - size[1] + 1) + 'px'
    530         });
    531         if (canvas.dataMarkers.marker.selector.tooltipster('status').state != 'closed') { canvas.dataMarkers.marker.selector.tooltipster('reposition'); }
    532         canvas.dataMarkers.reposition = false;
    533 } //}}}
    534 function updateMarker(canvas, reset) { //{{{
    535         //Retrieve data value fields and plots them on data marker popup if a hit has been registered.
    536         //TODO: Automatically pick up any field of size md.mesh.numberofelements
    537         //If no marker has been placed, no update is needed. If canvas is resimulating and unitNode has not been set yet, wait and try again.
    538         if (!canvas.dataMarkers.enabled || isEmptyOrUndefined(canvas.dataMarkers.marker.hit)) { return; }
    539         if (isEmptyOrUndefined(canvas.unitNode) || isEmptyOrUndefined(canvas.unitNode.mesh)) { setTimeout( function(){ updateMarker(canvas, reset); }, 750); return; }
    540        
    541         var hit = canvas.dataMarkers.marker.hit;
    542        
    543         var coords = canvas.unitNode.mesh.vertexBuffers.coords.data;
    544         var latitude = md.mesh.lat;
    545         var longitude = md.mesh.long;
    546         var thickness;
    547         var velocity;
    548         if (md.results[0]) {
    549                 thickness = md.results[canvas.animation.frame].Thickness;
    550                 velocity = md.results[canvas.animation.frame].Vel;
    551         }
    552         else {
    553                 thickness = md.geometry.thickness;
    554                 velocity = md.initialization.vel;
    555         }
    556        
    557         //Construct new argument array of the data display format for sprintf using first first argument as the formatSpecifier string and the rest as the additional arguments.
    558         var labels = canvas.dataMarkers.labels.slice();
    559         for (var i = 0; i < labels.length; i++) {
    560                 if (labels[i].toLowerCase() === 'x') { labels[i] = hit.modelPos[0]; }
    561                 else if (labels[i].toLowerCase() === 'y') { labels[i] = hit.modelPos[1]; }
    562                 else if (labels[i].toLowerCase() === 'z') { labels[i] = hit.modelPos[2]; }
    563                 else if (labels[i].toLowerCase() === 'latitude') {
    564                         var hitLatitude = [latitude[hit.indices[0]], latitude[hit.indices[1]], latitude[hit.indices[2]]];
    565                         var valueLatitude = Math.abs(hitLatitude[0] * hit.uvw[0] + hitLatitude[1] * hit.uvw[1] + hitLatitude[2] * hit.uvw[2]);
    566                         labels[i] = valueLatitude;
    567                 }
    568                 else if (labels[i].toLowerCase() === 'longitude') {
    569                         var hitLongitude = [longitude[hit.indices[0]], longitude[hit.indices[1]], longitude[hit.indices[2]]];
    570                         var valueLongitude = Math.abs(hitLongitude[0] * hit.uvw[0] + hitLongitude[1] * hit.uvw[1] + hitLongitude[2] * hit.uvw[2]);
    571                         labels[i] = valueLongitude;
    572                 }
    573                 else if (labels[i].toLowerCase() === 'thickness') {
    574                         var hitThickness = [thickness[hit.indices[0]], thickness[hit.indices[1]], thickness[hit.indices[2]]];
    575                         var valueThickness = hitThickness[0] * hit.uvw[0] + hitThickness[1] * hit.uvw[1] + hitThickness[2] * hit.uvw[2];
    576                         labels[i] = valueThickness;
    577                 }
    578                 else if (labels[i].toLowerCase() === 'velocity') {
    579                         var hitVelocity = [velocity[hit.indices[0]], velocity[hit.indices[1]], velocity[hit.indices[2]]];
    580                         var valueVelocity = hitVelocity[0] * hit.uvw[0] + hitVelocity[1] * hit.uvw[1] + hitVelocity[2] * hit.uvw[2];   
    581                         labels[i] = valueVelocity;
    582                 }
    583                 else if (labels[i].toLowerCase() === 'value') {
    584                         var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1], coords[hit.indices[1]*2], coords[hit.indices[1]*2+1], coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
    585                         var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
    586                         var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
    587                         var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
    588                         labels[i] = value;
    589                 }
    590         }
    591        
    592         //Apply changes to tooltip
    593         $('#tooltip-content-data-marker-' + canvas.id).html(sprintf.apply(null, canvas.dataMarkers.format.concat(labels)));
    594         $('#tooltip-content-data-marker-' + canvas.id).css({'font': canvas.dataMarkers.font});                         
    595        
    596         //If animated, setup animation loop to update plot as movie plays.
    597         if (canvas.dataMarkers.animated) {
    598                 var isEmpty = (canvas.dataMarkers.values.length === 0);
    599                 var lastUpdatedIndex = (canvas.dataMarkers.values.length - 1);
    600                 var newMovieFrame = (!isEmpty && canvas.dataMarkers.values[lastUpdatedIndex][0] != canvas.animation.frame);
    601                 //If new data marker has been placed, reinitialize plot. If not, push new value into plot value array.
    602                 if (reset) {
    603                         canvas.dataMarkers.values = [];
    604                         newMovieFrame = true;
    605                         for (var currentFrame = 0; currentFrame < (canvas.unitNode.movieLength); currentFrame++) {
    606                                 coords = canvas.unitMovieData[currentFrame];
    607                                 var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1], coords[hit.indices[1]*2], coords[hit.indices[1]*2+1], coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
    608                                 var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
    609                                 var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
    610                                 var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
    611                                 canvas.dataMarkers.values.push([currentFrame, value]);
    612                         }
    613                 }
    614                 else {
    615                         if (canvas.animation.lastFrame > canvas.animation.frame) {
    616                                 canvas.dataMarkers.values = [];
    617                         }
    618                         if (isEmpty || newMovieFrame) {
    619                                 coords = canvas.unitMovieData[canvas.animation.frame];
    620                                 var hitValues = [coords[hit.indices[0]], coords[hit.indices[1]], coords[hit.indices[2]]];
    621                                 var value = hitValues[0] * hit.uvw[0] + hitValues[1] * hit.uvw[1] + hitValues[2] * hit.uvw[2]; 
    622                                 canvas.dataMarkers.values.push([canvas.animation.frame, value]);
    623                         }
    624                 }
    625                
    626                 //Replot data marker popup using update data value array.
    627                 if (isEmpty || newMovieFrame) {
    628                         var dataLabels = {'latitude': valueLatitude, 'longitude': valueLongitude, 'thickness': valueThickness, 'velocity': valueVelocity, 'value': value};
    629                         var dataDisplay = canvas.dataMarkers.values.slice(0, canvas.animation.frame + 1);               
    630                         plot(
    631                                 'id', '#sim-plot',
    632                                 'type', 'bar',
    633                                 'width', 400,
    634                                 'height', 300,
    635                                 'nticks', 25,
    636                                 'xlabel', 'Time',
    637                                 'ylabel', 'Value',
    638                                 'title', 'Changes Over Time',
    639                                 'datalabels', canvas.dataMarkers.labels,
    640                                 'labelvalues', dataLabels,
    641                                 'data', dataDisplay
    642                         );
    643                 }
    644         }
    645         canvas.dataMarkers.reposition = true;
    646         if (reset && !canvas.dataMarkers.marker.selector.closed) {
    647                 canvas.dataMarkers.marker.selector.fadeIn(175);
    648                 canvas.dataMarkers.marker.selector.tooltipster('open');
    649         }
     422function raycastXY(canvas, x, y, node) { //{{{
     423        //Performs raycast on given node using x and y screenspace coordinates.
     424        //Returns hit objects with hit position, normals, barycentric coordinates, element number, and indices of ray-triangle intersection.
     425        //TODO: Diagnose marker issues with orthographic views and slr-eustatic updates when switching between basins.
     426        var ray = screenToModelRay(canvas, x, y, node);
     427        return raycast(canvas, ray.origin, ray.direction, node);
    650428} //}}}
    651429function animateValue(current, target, duration, stepCallback, doneCallback) { //{{{
     
    669447        var elevationRotationMatrix = mat4.create();
    670448        var aspectRatio = canvas.clientWidth / canvas.clientHeight;
    671         var cameraPosition = vec3.create();
    672 
    673         if (canvas.view.twod) { mat4.ortho(pMatrix, -aspectRatio*6.371e6/canvas.view.zoom, aspectRatio*6.371e6/canvas.view.zoom, -6.371e6/canvas.view.zoom, 6.371e6/canvas.view.zoom, canvas.camera.near, canvas.camera.far); }
    674         else { mat4.perspective(pMatrix, canvas.camera.fov * DEG2RAD, aspectRatio, canvas.camera.near, canvas.camera.far); }
     449        var camera = canvas.camera;
     450        var view = canvas.view;
     451
     452        if (view.twod) { mat4.ortho(pMatrix, -aspectRatio*6.371e6/view.zoom, aspectRatio*6.371e6/view.zoom, -6.371e6/view.zoom, 6.371e6/view.zoom, camera.near, camera.far); }
     453        else { mat4.perspective(pMatrix, camera.fov * DEG2RAD, aspectRatio, camera.near, camera.far); }
    675454       
    676455        //Apply worldspace translation
    677         mat4.translate(vMatrix, translateMatrix, vec3.negate(vec3.create(), canvas.view.position));
     456        mat4.translate(vMatrix, translateMatrix, vec3.negate(vec3.create(), view.position));
    678457       
    679458        //Calculate rotation around camera focal point about worldspace origin
    680         if (canvas.view.twod) {
     459        if (view.twod) {
    681460                mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, -DEG2RAD * 0, [0, 1, 0]);
    682461                mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * 90, [1, 0, 0]);
     
    684463        }
    685464        else {
    686                 mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, -DEG2RAD * (canvas.view.rotation[0] + 90), [0, 1, 0]);
    687                 mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * canvas.view.rotation[1], [1, 0, 0]);
     465                mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, -DEG2RAD * (view.rotation[0] + 90), [0, 1, 0]);
     466                mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * view.rotation[1], [1, 0, 0]);
    688467                mat4.multiply(rotationMatrix, elevationRotationMatrix, azimuthRotationMatrix);
     468                //var quaternionWorldX = Node.prototype.eulerToQuaternion(0, 0, DEG2RAD * (view.rotation[0]));
     469                //var quaternionWorldY = Node.prototype.eulerToQuaternion(0, DEG2RAD * (view.rotation[1]), 0);
     470                //var quaternionWorldZ = Node.prototype.eulerToQuaternion(DEG2RAD * (view.rotation[2]), 0, 0);
     471                //var quaternionTemp = quat.multiply(quat.create(), quaternionWorldY, quaternionWorldX);
     472                //quat.multiply(camera.rotation, quaternionWorldZ, quaternionTemp);
     473                //mat4.fromQuat(rotationMatrix, camera.rotation);       
    689474        }
    690475
     
    694479        //Apply screenspace translation to emulate rotation around point
    695480        mat4.identity(translateMatrix);
    696         mat4.translate(translateMatrix, translateMatrix, [0.0, 0.0, -6.371e6/canvas.view.zoom]);
     481        mat4.translate(translateMatrix, translateMatrix, [0.0, 0.0, -6.371e6/view.zoom]);
    697482        mat4.multiply(vMatrix, translateMatrix, vMatrix);
    698483       
    699484        //Apply projection matrix to get camera matrix
    700         mat4.copy(canvas.camera.vMatrix, vMatrix);
    701         mat4.multiply(canvas.camera.vpMatrix, pMatrix, vMatrix);
     485        mat4.copy(camera.vMatrix, vMatrix);
     486        mat4.multiply(camera.vpMatrix, pMatrix, vMatrix);
    702487       
    703488        //Calculate inverse view matrix fields for lighting and raycasts
    704         mat4.invert(canvas.camera.vInverseMatrix, canvas.camera.vMatrix);
    705         mat4.invert(canvas.camera.vpInverseMatrix, canvas.camera.vpMatrix);
    706        
    707         vec3.transformMat4(canvas.camera.position, cameraPosition, canvas.camera.vpInverseMatrix);
    708         canvas.camera.ready = true;
    709         repositionMarker(canvas);
     489        mat4.invert(camera.vInverseMatrix, camera.vMatrix);
     490        mat4.invert(camera.vpInverseMatrix, camera.vpMatrix);
     491       
     492        vec3.transformMat4(camera.position, vec3.create(), camera.vInverseMatrix);
     493        vec3.sub(camera.relativePosition, camera.position, view.position);
     494        vec3.normalize(camera.direction, camera.relativePosition);
     495       
     496        camera.ready = true;
    710497}//}}}
    711498function drawSceneGraphNode(canvas, node) { //{{{
     
    718505        mat4.multiply(mvpMatrix, canvas.camera.vpMatrix, node.modelMatrix);
    719506       
    720         var mvMatrix = mat4.create();
    721         mat4.multiply(mvMatrix, canvas.camera.vMatrix, node.modelMatrix);
    722        
    723         var normalMatrix = mat4.create();
    724         mat4.invert(normalMatrix, node.modelMatrix);
    725         mat4.transpose(normalMatrix, normalMatrix);
     507        var normalMatrix = mat3.create();
     508        var tempMatrix = mat4.create();
     509        mat4.invert(tempMatrix, node.modelMatrix);
     510        mat4.transpose(tempMatrix, tempMatrix);
     511        mat3.fromMat4(normalMatrix, tempMatrix);
    726512       
    727513        if (node.texture) { node.texture.bind(0); }
     
    732518        gl.lineWidth(node.lineWidth);
    733519        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    734 
     520       
    735521        //Setup for light that originates from camera
    736         var origin = vec3.fromValues(0, 0, 0);
    737         var lightOrigin = vec3.fromValues(0, 0, 0);
    738         var cameraPositionRelative = vec3.create();
    739         vec3.transformMat4(origin, origin, canvas.camera.vInverseMatrix);
    740         vec3.normalize(lightOrigin, lightOrigin);
    741         vec3.sub(cameraPositionRelative, origin, node.translation);
    742         cameraHeight = vec3.length(cameraPositionRelative);
    743        
    744         var atm = {                                     //Default Values
    745                 wavelength_r: 0.65,             //0.65          Red wavelength (micrometers)
    746                 wavelength_g: 0.57,                     //0.57          Green wavelength (micrometers)
    747                 wavelength_b: 0.475,            //0.475         Green wavelength (micrometers)
    748                 eSun: 100.0,                            //20.0          Sun intensity   
    749                 kRayleigh: 0.0025,                      //0.0025        Rayleigh scattering amount
    750                 kMie: 0.000,                            //0.01          Mie scattering amount
    751                 g: -0.99,                                       //-0.99         Mie phase asymmetry/direction factor
    752                 hdr_exposure: 0.8,                      //0.8           High Dynamic Range Exposure
    753                 scale: 1.25,                            //1.025         Scale of atmosphere. WARNING: Change atmosphereScale in applyoptions.js, and scaling constants.
    754                 scaleDepth: 0.25,                       //0.25          Percentage altitude at which the atmosphere's average density is found
    755                 a: -0.00287,                            //-0.00287      Scaling constant a
    756                 b: 0.459,                                       //0.459         Scaling constant b
    757                 c: 3.83,                                        //3.83          Scaling constant c
    758                 d: -6.80,                                       //-6.80         Scaling constant d
    759                 e: 3.6,                                         //5.25          Scaling constant e. Lower when increasing atmosphere scale.
    760                 attenuation: 0.5                        //0.5           Strength of atmospheric scattering on ground shading.
    761         };
    762                        
    763         var inv_wavelength4 = [1.0 / Math.pow(atm.wavelength_r, 4), 1.0 / Math.pow(atm.wavelength_g, 4), 1.0 / Math.pow(atm.wavelength_b, 4)];
    764         var innerRadius = 6.371e6;
    765         var outerRadius = innerRadius*atm.scale;
    766         var scale = 1.0 / (outerRadius - innerRadius);
    767         var scaleDepth = atm.scaleDepth;
     522        var atm = canvas.atmosphere;
     523        var lightOrigin = vec3.create();
    768524       
    769525        node.shader.uniforms({
    770                 m4MVP: mvpMatrix,
    771                 m4Normal: normalMatrix,
    772                 m4Model: node.modelMatrix,
    773                 u_lightPosition: lightOrigin,
    774                 //u_lightPosition: [1.0, 1.0, 1.0],
    775                 u_diffuseColor: node.diffuseColor,
    776                 u_texture: 0,
    777                 u_alpha: node.alpha,
    778                 u_maskZerosColor: node.maskZerosColor,
    779                 u_maskZerosEnabled: node.maskZerosEnabled,
    780                 u_maskZerosTolerance: node.maskZerosTolerance,
    781                 u_maskZerosZeroValue: node.maskZerosZeroValue,
    782                 u_maskEnabled: node.maskEnabled,
    783                 u_maskHeight: node.maskHeight,
    784                 u_maskColor: node.maskColor,
    785                 u_pointSize: node.pointSize,
    786                 v3CameraPosition: origin,
    787                 v3Translate: node.translation,
    788                 v3LightPos: lightOrigin,
    789                 v3InvWavelength: inv_wavelength4,
    790                 fOuterRadius: outerRadius,
    791                 fOuterRadius2: outerRadius * outerRadius,
    792                 fInnerRadius: innerRadius,
    793                 fInnerRadius2: innerRadius * innerRadius,
    794                 fKrESun: atm.kRayleigh * atm.eSun,
    795                 fKmESun: atm.kMie * atm.eSun,
    796                 fKr4PI: atm.kRayleigh * 4 * Math.PI,
    797                 fKm4PI: atm.kMie * 4 * Math.PI,
    798                 fScale: scale,
    799                 fScaleDepth: scaleDepth,
    800                 fScaleOverScaleDepth: scale/scaleDepth,
    801                 v3LightPosFrag: lightOrigin,
    802                 fHdrExposure: atm.hdr_exposure,
    803                 g: atm.g,                       
    804                 g2: atm.g * atm.g,
    805                 a: atm.a,
    806                 b: atm.b,
    807                 c: atm.c,
    808                 d: atm.d,               
    809                 e: atm.e,
    810                 attenuation: atm.attenuation
     526                m4MVP:                                  mvpMatrix,
     527                m3Normal:                               normalMatrix,
     528                m4Model:                                node.modelMatrix,
     529                u_alpha:                                node.alpha,
     530                u_ambientColor:                 node.ambientColor,
     531                u_cameraPosition:               canvas.camera.position,
     532                u_diffuseColor:                 node.diffuseColor,
     533                u_lightDirection:               canvas.camera.direction,
     534                u_lightingBias:                 node.lightingBias,
     535                u_maskZerosColor:               node.maskZerosColor,
     536                u_maskZerosEnabled:     node.maskZerosEnabled,
     537                u_maskZerosTolerance:   node.maskZerosTolerance,
     538                u_maskZerosZeroValue:   node.maskZerosZeroValue,
     539                u_maskEnabled:                  node.maskEnabled,
     540                u_maskHeight:                   node.maskHeight,
     541                u_maskColor:                    node.maskColor,
     542                u_pointSize:                    node.pointSize,
     543                u_specularColor:                node.specularColor,
     544                u_specularPower:                node.specularPower,
     545                u_specularStrength:     node.specularStrength,
     546                u_texture:                              0,
     547                v3CameraPosition:               canvas.camera.position,
     548                v3Translate:                    node.translation,
     549                v3LightPos:                     lightOrigin,
     550                v3InvWavelength:                atm.inv_wavelength4,
     551                fOuterRadius:                   atm.outerRadius,
     552                fOuterRadius2:                  atm.outerRadius2,
     553                fInnerRadius:                   atm.innerRadius,
     554                fInnerRadius2:                  atm.innerRadius2,
     555                fKrESun:                                atm.krESun,
     556                fKmESun:                                atm.kmESun,
     557                fKr4PI:                                 atm.kr4PI,
     558                fKm4PI:                                 atm.km4PI,
     559                fScale:                                 atm.scale,
     560                fScaleDepth:                    atm.scaleDepth,
     561                fScaleOverScaleDepth:   atm.scaleOverScaleDepth,
     562                v3LightPosFrag:                 lightOrigin,
     563                fHdrExposure:                   atm.hdr_exposure,       
     564                g:                                              atm.g,                 
     565                g2:                                     atm.g2,
     566                a:                                              atm.a,
     567                b:                                              atm.b,
     568                c:                                              atm.c,
     569                d:                                              atm.d,         
     570                e:                                              atm.e,
     571                attenuation:                    atm.attenuation
    811572        }).draw(node.mesh, node.drawMode, 'triangles');
    812573       
     
    814575        gl.disable(gl.CULL_FACE);
    815576} //}}}
     577function canvasResize(canvas) {
     578        var rect = canvas.getBoundingClientRect();
     579        canvas.width  = rect.width;
     580        canvas.height = rect.height;
     581        canvas.gl.viewport(0, 0, canvas.width, canvas.height);
     582       
     583        if (!isEmptyOrUndefined(canvas.overlaycanvas)) {
     584                rect = canvas.overlaycanvas.getBoundingClientRect();
     585                canvas.overlaycanvas.width  = rect.width;
     586                canvas.overlaycanvas.height = rect.height;
     587        }
     588}
    816589function draw(canvas) { //{{{
    817590        //Ensure all nodes are ready to render
     
    833606        //Begin rendering nodes
    834607        if (canvas.draw.ready) {
    835                 var rect = canvas.getBoundingClientRect();
    836                 canvas.width  = rect.width;
    837                 canvas.height = rect.height;
     608                //Handle canvas resizing and viewport/screenspace coordinate synchronization
     609                canvasResize(canvas);   
    838610               
    839611                var gl = canvas.gl;
    840612                gl.makeCurrent(); //litegl function to handle switching between multiple canvases
    841                 gl.viewport(0, 0, canvas.width, canvas.height);
    842613                gl.clearColor(canvas.backgroundcolor[0], canvas.backgroundcolor[1], canvas.backgroundcolor[2], canvas.backgroundcolor[3]);
    843614                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
     
    845616                updateCameraMatrix(canvas);
    846617               
    847                 if (canvas.textcanvas) { canvas.textcanvas.draw(canvas); }
    848                 if (canvas.overlaycanvas) { canvas.overlaycanvas.draw(canvas); }
     618                //Trigger any handlers attatched to this canvas event.
     619                canvas.selector.trigger('onPreRender', [canvas]);
     620               
     621                for (var handler in canvas.overlayHandlers) { canvas.overlayHandlers[handler](canvas); }
    849622               
    850623                var drawPassNumber = 3;
Note: See TracChangeset for help on using the changeset viewer.