3d-interface/js/l3d/src/utils/ObjectClicker.js

178 lines
4.5 KiB
JavaScript

L3D.ObjectClicker = (function() {
function pointerCheck(camera) {
return (camera instanceof L3D.PointerCamera && camera.pointerLocked);
}
/**
* Class to allow easy object clicking
* @constructor
* @memberof L3D
* @param renderer {THREE.Renderer} the renderer
* @param camera {THREE.Camera} the camera to use
* @param objects {Object[]} the objects on which the collision is possible
* @param onHover {function} callback called when the mouse hovers an object
* @param onClick {function} callback called when the user clicks an object
* @param [domElement=document] {Element} element to which ObjectClicker listens to
*/
var ObjectClicker = function(renderer, camera, objects, onHover, onClick, domElement) {
/**
* Renderer used
* @type {THREE.Renderer}
*/
this.renderer = renderer;
/**
* Objects on which the collision is possible
* @type {Object[]}
*/
this.objects = objects;
/**
* Function called when the mouse hovers an object
* @type {function}
*/
this.onHover = onHover;
/**
* Function called when the user clicks on an object
* @type {function}
*/
this.onClick = onClick;
/**
* Camera used for ray casting
* @type {THREE.Camera}
*/
this.camera = camera;
/**
* Position of the mouse
* @type {Object}
*/
this.mouse = {x: null, y: null};
/**
* Element on which we will listen
* @type {Element}
*/
this.domElement = domElement || document;
/**
* Element currently hovered
* @type {Object}
*/
this.hoveredElement = null;
/**
* Raycaster used for finding the objects
* @private
* @type {THREE.Raycaster}
*/
this.raycaster = new THREE.Raycaster();
/**
* Currently pointed object
* @type {Object}
*/
this.currentPointedObject = null;
/**
* Previously pointed object
* @type {Object}
*/
this.previousPointedObject = null;
// Add event listeners
var self = this;
this.domElement.addEventListener('mousemove', function(event) { self.update(event); });
if (navigator.userAgent.indexOf("Firefox") > 0) {
// If firefox
this.domElement.addEventListener('mousedown', function(event) {self.click(event); });
} else {
// If chrome
this.domElement.addEventListener('click', function(event) { self.click(event); });
this.domElement.addEventListener('contextmenu', function(event) { event.button = 2; self.click(event); });
}
};
/**
* Returns the object pointed by the mouse
*/
ObjectClicker.prototype.getPointedObject = function() {
// Compute x and y for unprojection
var x = ( this.mouse.x / this.renderer.domElement.width ) * 2 - 1;
var y = - (this.mouse.y / this.renderer.domElement.height) * 2 + 1;
if (pointerCheck(this.camera)) {
x = 0;
y = 0;
}
var vector = new THREE.Vector3(x,y,0.5);
vector.unproject(this.camera);
// Set raycaster
this.raycaster.set(this.camera.position, vector.sub(this.camera.position).normalize());
// Compute intersections
var intersects = this.raycaster.intersectObjects(this.objects, false);
// Avoid non-raycastable objects
for (var i = 0; i < intersects.length && !intersects[i].object.raycastable; i++){}
// Objects are sorted by distance in intersects, the best is the first
return intersects[i];
};
/**
* Updates the position of the mouse and the hovered object and calls onHover
* @param event {Event} the event received by 'mousemove'
*/
ObjectClicker.prototype.update = function(event) {
// Set mouse position
if (event !== undefined) {
this.mouse.x = event.offsetX || event.layerX;
this.mouse.y = event.offsetY || event.layerY;
}
// Update current pointed object
this.previousPointedObject = this.currentPointedObject;
this.currentPointedObject = this.getPointedObject();
// If those two objects are different, call onHover
if (this.previousPointedObject !== this.currentPointedObject) {
var p = pointerCheck(this.camera);
this.onHover(
this.currentPointedObject,
p ? this.renderer.domElement.width / 2 : this.mouse.x,
p ? this.renderer.domElement.height / 2 : this.mouse.y
);
}
};
/**
* Calls onClick on the current pointed element
*/
ObjectClicker.prototype.click = function(event) {
this.onClick(this.currentPointedObject, this.mouse.x, this.mouse.y, event);
};
return ObjectClicker;
})();