API Docs for: 0.9.16
Show:

File: phoria-view.js

/**
 * @fileoverview phoria - View Control. Helpers to control the view via mouse, provide high-level mouse events.
 * Reverse object selection (entity picking) - contribution from Ruan Moolman.
 * @author Kevin Roast
 * @date 26th Jan 2014
 * 
 * @module View
 */

/**
 * View helper class. Provides view related utilities such as high-level event handling. Reverse object selection (entity picking).
 * 
 * @class View
 */
(function() {
   "use strict";
   
   Phoria.View = {};
   
   Phoria.View.events = {};

    /**
     * @method addMouseEvents
     *
     * @param el
     * @param fnOnClick
     */
   Phoria.View.addMouseEvents = function addMouseEvents(el, fnOnClick)
   {
      if (el.id)
      {
         // mouse rotation and position tracking instance
         var mouse = {
            velocityH: 0,        // final target value from horizontal mouse movement 
            velocityLastH: 0,
            positionX: 0,
            clickPositionX: 0,   // last mouse click position
            velocityV: 0,        // final target value from vertical mouse movement 
            velocityLastV: 0,
            positionY: 0,
            clickPositionY: 0    // last mouse click position
         };
         
         // set object reference for our events
         Phoria.View.events[el.id] = mouse;
         
         mouse.onMouseMove = function onMouseMove(evt) {
         	mouse.positionX = evt.clientX;
         	mouse.velocityH = mouse.velocityLastH + (mouse.positionX - mouse.clickPositionX) * 0.5;
         	mouse.positionY = evt.clientY;
         	mouse.velocityV = mouse.velocityLastV + (mouse.positionY - mouse.clickPositionY) * 0.5;
         };
         
         mouse.onMouseUp = function onMouseUp(evt) {
         	el.removeEventListener('mousemove', mouse.onMouseMove, false);
         };
         
         mouse.onMouseOut = function onMouseOut(evt) {
         	el.removeEventListener('mousemove', mouse.onMouseMove, false);
         };
         
         mouse.onMouseDown = function onMouseDown(evt) {
         	evt.preventDefault();
         	el.addEventListener('mousemove', mouse.onMouseMove, false);
         	mouse.clickPositionX = evt.clientX;
         	mouse.velocityLastH = mouse.velocityH;
         	mouse.clickPositionY = evt.clientY;
         	mouse.velocityLastV = mouse.velocityV;
         };
         
         el.addEventListener('mousedown', mouse.onMouseDown, false);
         el.addEventListener('mouseup', mouse.onMouseUp, false);
         el.addEventListener('mouseout', mouse.onMouseOut, false);
         
         // add click handler if supplied
         if (fnOnClick) el.addEventListener('click', fnOnClick, false);
         
         return mouse;
      }
   }

    /**
     * @method removeMouseEvents
     *
     * @param el
     * @param fnOnClick
     */
   Phoria.View.removeMouseEvents = function removeMouseEvents(el, fnOnClick)
   {
      if (el.id)
      {
         var mouse = Phoria.View.events[el.id];
         if (mouse)
         {
            el.removeEventListener('mousemove', mouse.onMouseMove, false);
            el.removeEventListener('mousedown', mouse.onMouseDown, false);
            el.removeEventListener('mouseup', mouse.onMouseUp, false);
            el.removeEventListener('mouseout', mouse.onMouseOut, false);
            if (fnOnClick) el.removeEventListener('click', fnOnClick, false);
            Phoria.View.events[el.id] = null;
         }
      }
   }

    /**
     * @method getMouse
     *
     * @param el
     */
   Phoria.View.getMouse = function getMouse(el)
   {
      return Phoria.View.events[el.id];
   }

    /**
     * @method calculateClickPointAndVector
     *
     * @param scene
     * @param mousex
     * @param mousey
     */
   Phoria.View.calculateClickPointAndVector = function calculateClickPointAndVector(scene, mousex, mousey)
   {
      var camLookAt = vec3.fromValues(
         scene.camera.lookat.x,
         scene.camera.lookat.y,
         scene.camera.lookat.z);
      var camOff = vec3.subtract(vec3.create(), scene._cameraPosition, camLookAt);
      
      // get pixels per unit at click plane (plane normal to camera direction going through the camera focus point)
      var pixelsPerUnit = (scene.viewport.height / 2) / (vec3.length(camOff) * Math.tan((scene.perspective.fov / 180 * Math.PI) / 2));
      
      // calculate world units (from the centre of canvas) corresponding to the mouse click position
      var dif = vec2.fromValues(mousex - (scene.viewport.width / 2), mousey - (scene.viewport.height / 2));
      vec2.subtract(dif, dif, new vec2.fromValues(8, 8)); // calibrate
      var units = vec2.create();
      vec2.scale(units, dif, 1 / pixelsPerUnit);
      
      // move click point horizontally on click plane by the number of units calculated from the x offset of the mouse click
      var upVector = vec3.fromValues(scene.camera.up.x, scene.camera.up.y, scene.camera.up.z);
      var normalVectorSide = vec3.create();
      vec3.cross(normalVectorSide, camOff, upVector);
      vec3.normalize(normalVectorSide, normalVectorSide);
      var clickPoint = vec3.scaleAndAdd(vec3.create(), camLookAt, normalVectorSide, units[0]);
      
      // move click point vertically on click plane by the number of units calculated from the y offset of the mouse click
      var normalVectorUp = vec3.create();
      vec3.cross(normalVectorUp, normalVectorSide, camOff);
      vec3.normalize(normalVectorUp, normalVectorUp);
      vec3.scale(normalVectorUp, normalVectorUp, units[1]);
      vec3.subtract(clickPoint, clickPoint, normalVectorUp);
      
      // calculate click vector (vector from click point to the camera's position)
      var camVector = vec3.add(vec3.create(), camLookAt, camOff);
      return {
         clickPoint: clickPoint,
         clickVector: vec3.subtract(vec3.create(), clickPoint, camVector)
      };
   }
   
   Phoria.View.getIntersectedObjects = function getIntersectedObjects(scene, clickPoint, clickVector)
   {
      var intersections = [], obj, polygonNormal, polygonPoint, polygonCoords, polygonPlaneIntersection, pointVector;
      
      // Go through all the appropriate objects
      var objects = scene.renderlist;
      for (var n = 0, obj; n < objects.length; n++)
      {
         obj = objects[n];
         
         // only consider solid objects
         if (obj.style.drawmode !== "solid") continue;
         
         // Go through all the polygons of an object
         for (var m = 0; m < obj.polygons.length; m++)
         {
            polygonNormal = vec3.clone(obj.polygons[m]._worldnormal);
            polygonPoint = vec3.clone(obj._worldcoords[obj.polygons[m].vertices[0]]);
            
            // Get the point where the line intersectects the polygon's plane
            polygonPlaneIntersection = Phoria.Util.planeLineIntersection(polygonNormal, polygonPoint, clickVector, clickPoint);
            
            // if the intersection is null, it means the line does not intersect the plane
            if (polygonPlaneIntersection !== null)
            {
               // Check if the intersection is inside the polygon
               if (Phoria.Util.intersectionInsidePolygon(obj.polygons[m], obj._worldcoords, polygonPlaneIntersection))
               {
                  // add intersection to the array being returned
                  var returnObject = {
                     entity: obj,
                     polygonIndex: m,
                     intersectionPoint: polygonPlaneIntersection
                  };
                  intersections.push(returnObject);
               }
            }
         }
      }
      
      // calculate distance to each intersection from camera's position
      for (var i = 0; i < intersections.length; i++)
      {
         intersections[i].distance = vec3.distance(scene._cameraPosition, intersections[i].intersectionPoint);
      }
      
      // sort intersection points from closest to farthest
      for (var i = 0; i < intersections.length - 1; i++)
      {
         for (var j = i + 1, keepVal; j < intersections.length; j++)
         {
            if (intersections[i].distance >= intersections[j].distance)
            {
               keepVal = intersections[j];
               intersections[j] = intersections[i];
               intersections[i] = keepVal;
            }
         }
      }
      
      // return list of all intersections
      return intersections;
   }

})();