├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Jam3 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # camera-picking-ray 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Creates a picking ray for a camera. Commonly used for mouse interaction in 2D and 3D games. 6 | 7 | The camera is assumed to have a `projection` and `view` matrix, which can be combined and inverted to form `invProjView`. 8 | 9 | ```js 10 | var origin = [0, 0, 0] 11 | var direction = [0, 0, 0] 12 | 13 | //compute ray and store it in (origin, direction) 14 | pick(origin, direction, mouse, viewport, invProjView) 15 | 16 | console.log(origin, direction) 17 | ``` 18 | 19 | Full example, hit-testing against a 3D sphere: 20 | 21 | ```js 22 | var mat4 = require('gl-mat4') 23 | var pick = require('camera-picking-ray') 24 | var intersect = require('ray-sphere-intersection') 25 | 26 | //your camera matrices 27 | var projection = ... 28 | var view = ... 29 | var projView = mat4.multiply([], projection, view) 30 | var invProjView = mat4.invert([], projView) 31 | 32 | var mouse = [ screenX, screenHeight - screenY ] 33 | var viewport = [ 0, 0, screenWidth, screenHeight ] 34 | 35 | var ray = { 36 | ro: [0, 0, 0], 37 | rd: [0, 0, 0] 38 | } 39 | 40 | //store result in ray (origin, direction) 41 | pick(ray.ro, ray.rd, mouse, viewport, invProjView) 42 | 43 | //let's see if the mouse hit a 3D sphere... 44 | var center = [0, 0, 0], 45 | radius = 1.5 46 | var hit = intersect([], ray.ro, ray.rd, center, radius) 47 | 48 | if (hit) { 49 | console.log("Mouse hit the sphere at:", hit) 50 | } 51 | ``` 52 | 53 | PRs welcome. 54 | 55 | ## Usage 56 | 57 | [![NPM](https://nodei.co/npm/camera-picking-ray.png)](https://www.npmjs.com/package/camera-picking-ray) 58 | 59 | #### `pick(origin, direction, mouse, viewport, invProjView)` 60 | 61 | Creates a picking ray for the given `mouse` screen-space position (vec2) and `viewport` screen-space bounds (x, y, width, height). `invProjView` is the inverse of the combined `projection * view` matrix for your camera. 62 | 63 | Stores the resulting ray in the first two parameters: `origin` (vec3) and `direction` (vec3). 64 | 65 | ## See Also 66 | 67 | - [ray-sphere-intersection](https://www.npmjs.com/package/ray-sphere-intersection) 68 | - [ray-triangle-intersection](https://www.npmjs.com/package/ray-triangle-intersection) 69 | - [ray-plane-intersection](https://www.npmjs.com/package/ray-plane-intersection) 70 | - [ray-aabb](https://www.npmjs.com/package/ray-aabb) 71 | - [camera-unproject](https://www.npmjs.com/package/camera-unproject) 72 | 73 | ## License 74 | 75 | MIT, see [LICENSE.md](http://github.com/Jam3/camera-picking-ray/blob/master/LICENSE.md) for details. 76 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var unproject = require('camera-unproject') 2 | var set = require('gl-vec3/set') 3 | var sub = require('gl-vec3/subtract') 4 | var normalize = require('gl-vec3/normalize') 5 | 6 | module.exports = createPickRay 7 | function createPickRay(origin, direction, point, viewport, invProjView) { 8 | set(origin, point[0], point[1], 0) 9 | set(direction, point[0], point[1], 1) 10 | unproject(origin, origin, viewport, invProjView) 11 | unproject(direction, direction, viewport, invProjView) 12 | sub(direction, direction, origin) 13 | normalize(direction, direction) 14 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camera-picking-ray", 3 | "version": "1.0.1", 4 | "description": "creates a picking ray for a 2D/3D camera", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "dependencies": { 13 | "camera-unproject": "^1.0.1", 14 | "gl-vec3": "^1.0.3" 15 | }, 16 | "devDependencies": { 17 | "gl-mat4": "^1.1.3", 18 | "gl-vec3": "^1.0.3", 19 | "tape": "^4.0.0", 20 | "test-fuzzy-array": "^1.0.1" 21 | }, 22 | "scripts": { 23 | "test": "node test.js" 24 | }, 25 | "keywords": [ 26 | "pick", 27 | "picking", 28 | "intersect", 29 | "intersection", 30 | "ray", 31 | "rays", 32 | "hit", 33 | "test", 34 | "unproject", 35 | "project", 36 | "collision", 37 | "mouse", 38 | "touch", 39 | "2d", 40 | "3d", 41 | "projection", 42 | "perspective" 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/Jam3/camera-picking-ray.git" 47 | }, 48 | "homepage": "https://github.com/Jam3/camera-picking-ray", 49 | "bugs": { 50 | "url": "https://github.com/Jam3/camera-picking-ray/issues" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var pick = require('./') 2 | var test = require('tape') 3 | 4 | var mat4 = require('gl-mat4') 5 | var vec3 = require('gl-vec3') 6 | var Fuzzy = require('test-fuzzy-array') 7 | 8 | test('creates a picking ray for a 2D/3D camera', function (t) { 9 | //could improve/simplify tests here for various cases 10 | var near = 0.001 11 | var almostEqual = Fuzzy(t, near) 12 | var viewport = [0, 0, 128, 256] 13 | 14 | // simulate a camera (projection & view matrices) 15 | var proj = mat4.create() 16 | var view = mat4.create() 17 | var position = [0, 0, -3] 18 | var direction = [0, 0, -1] 19 | var up = [0, 1, 0] 20 | var center = [0, 0, 0] 21 | 22 | mat4.perspective(proj, Math.PI / 4, viewport[2] / viewport[3], near, 1) 23 | 24 | // build view matrix 25 | vec3.add(center, position, direction) 26 | mat4.lookAt(view, position, center, up) 27 | 28 | var combined = mat4.multiply([], proj, view) 29 | var invProj = mat4.invert([], combined) 30 | 31 | var ray = { 32 | origin: [0, 0, 0], 33 | direction: [0, 0, 0] 34 | } 35 | 36 | var mouse 37 | mouse = [64, 127] //center of screen 38 | pick(ray.origin, ray.direction, mouse, viewport, invProj) 39 | almostEqual(ray.origin, [0, 0, -3]) 40 | almostEqual(ray.direction, [0, 0, -1]) 41 | t.end() 42 | }) 43 | --------------------------------------------------------------------------------