├── LICENSE ├── p5rovercam.min.js ├── README.md └── p5.rovercam.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 freshfork 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /p5rovercam.min.js: -------------------------------------------------------------------------------- 1 | class RoverCam{constructor(t){this.sensitivity=.02,this.friction=.8,this.speed=.1,this.reset(),this.active=!0,this.enableControl=!0,this.p5=void 0!==t?t:p5.instance,null!==this.p5&&this.p5.registerMethod("post",()=>{this.active&&this.draw()}),this.keyMap={mx1:[87,38],mx2:[83,40],my1:[65,37],my2:[68,39],mz1:[69,69],mz2:[81,81],y1:[65,37],y2:[68,39],p1:[82,82],p2:[70,70],r1:[90,90],r2:[67,67],f1:[107,187],f2:[109,189],e1:[82,82],e2:[70,70]}}controller(){if(this.enableControl){var t=this.keyMap,i=this.p5;RoverCam.pointerLock?(this.yaw(i.movedX*this.sensitivity/10),this.pitch(i.movedY*this.sensitivity/10),(i.keyIsDown(t.my1[0])||i.keyIsDown(t.my1[1]))&&this.moveY(this.speed),(i.keyIsDown(t.my2[0])||i.keyIsDown(t.my2[1]))&&this.moveY(-this.speed),(i.keyIsDown(t.e1[0])||i.keyIsDown(t.e1[1]))&&this.elevate(-this.speed),(i.keyIsDown(t.e2[0])||i.keyIsDown(t.e2[1]))&&this.elevate(this.speed)):((i.keyIsDown(t.y1[0])||i.keyIsDown(t.y1[1]))&&this.yaw(-this.sensitivity),(i.keyIsDown(t.y2[0])||i.keyIsDown(t.y2[1]))&&this.yaw(this.sensitivity),(i.keyIsDown(t.p1[0])||i.keyIsDown(t.p1[1]))&&this.pitch(-this.sensitivity),(i.keyIsDown(t.p2[0])||i.keyIsDown(t.p2[1]))&&this.pitch(this.sensitivity)),(i.keyIsDown(t.mx1[0])||i.keyIsDown(t.mx1[1]))&&this.moveX(this.speed),(i.keyIsDown(t.mx2[0])||i.keyIsDown(t.mx2[1]))&&this.moveX(-this.speed),(i.keyIsDown(t.mz1[0])||i.keyIsDown(t.mz1[1]))&&this.moveZ(this.speed),(i.keyIsDown(t.mz2[0])||i.keyIsDown(t.mz2[1]))&&this.moveZ(-this.speed),(i.keyIsDown(t.f1[0])||i.keyIsDown(t.f1[1]))&&this.fov(-this.sensitivity/10),(i.keyIsDown(t.f2[0])||i.keyIsDown(t.f2[1]))&&this.fov(this.sensitivity/10)}}moveX(t){this.velocity.add(p5.Vector.mult(this.forward,t))}moveY(t){this.velocity.add(p5.Vector.mult(this.right,t))}moveZ(t){this.velocity.add(p5.Vector.mult(this.up,-t))}yaw(t){this.pan+=t}pitch(t){this.tilt+=t,this.tilt=this.clamp(this.tilt,-Math.PI/2.01,Math.PI/2.01),this.tilt==Math.PI/2&&(this.tilt+=.001)}roll(t){this.rot+=t}fov(t){this.fovy+=t,this.width=0}elevate(t){this.offset[0]+=t}usePointerLock(t){void 0===t&&(t=p5.instance),null!==t&&(RoverCam.canvas=t._renderer.elt,document.addEventListener("click",()=>{RoverCam.pointerLock?(t.exitPointerLock(),RoverCam.pointerLock=!1):(RoverCam.pointerLock=!0,t.requestPointerLock())},!1),document.addEventListener("pointerlockchange",RoverCam.onPointerlockChange,!1))}reset(){this.pan=0,this.tilt=0,this.rot=0,this.fovy=1,this.width=0,this.height=0,this.position=new p5.Vector(0,0,0),this.velocity=new p5.Vector(0,0,0),this.up=new p5.Vector(0,1,0),this.right=new p5.Vector(1,0,0),this.forward=new p5.Vector(0,0,1),this.offset=[0,0]}setActive(t){this.active=t,t&&(this.width=0)}setState(t){void 0!==t.fov&&(this.fovy=t.fov,this.width=0),void 0!==t.active&&(this.active=t.active),void 0!==t.rotation&&(this.pan=t.rotation[0],this.tilt=t.rotation[1],this.rot=t.rotation[2]),void 0!==t.position&&(this.position=new p5.Vector(t.position[0],t.position[1],t.position[2])),void 0!==t.offset&&(this.offset=t.offset),void 0!==t.enableControl&&(this.enableControl=t.enableControl),void 0!==t.speed&&(this.speed=t.speed),void 0!==t.sensitivity&&(this.sensitivity=t.sensitivity)}draw(){this.p5.width===this.width&&this.p5.height===this.height||(this.p5.perspective(this.fovy,this.p5.width/this.p5.height,.01,1e4),this.width=this.p5.width,this.height=this.p5.height),this.controller(),this.forward=new p5.Vector(Math.cos(this.pan),Math.tan(this.tilt),Math.sin(this.pan)),this.forward.normalize(),this.right=new p5.Vector(Math.cos(this.pan-Math.PI/2),0,Math.sin(this.pan-Math.PI/2)),this.velocity.mult(this.friction),this.position.add(this.velocity);let t=p5.Vector.sub(this.position,p5.Vector.mult(this.right,this.offset[1])),i=p5.Vector.add(t,this.forward);this.p5.camera(t.x,t.y+this.offset[0],t.z,i.x,i.y+this.offset[0],i.z,this.up.x,this.up.y,this.up.z)}clamp(t,i,e){return t>e?e:t{document.pointerLockElement!==RoverCam.canvas&&document.mozPointerLockElement!==RoverCam.canvas&&(RoverCam.pointerLock=!1)}),p5.prototype.createRoverCam=function(){return new RoverCam(this)}; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## p5.RoverCam 2 | A super-simple first-person perspective camera library for p5.js in WEBGL mode. 3 | 4 | RoverCam supports p5.js global and multi-instance modes. 5 | 6 | The position and orientation of an instance of RoverCam can be controlled using the mouse and WASD / Arrow keys (as well as E and Q for upward and downward motion). 7 | 8 | ### Release path 9 | 10 | - [CDN: p5.rovercam.min.js](https://cdn.jsdelivr.net/gh/freshfork/p5.RoverCam@1.1.1/p5.rovercam.min.js) 11 | 12 | ### Usage 13 | 14 | ```javascript 15 | var rover; 16 | 17 | function setup() { 18 | createCanvas(800, 800, WEBGL); 19 | rover = createRoverCam(); 20 | rover.usePointerLock(); // optional; default is keyboard control only 21 | rover.setState({ // optional 22 | position: [-400,-200,-200], 23 | rotation: [0.4,0.3,0], 24 | sensitivity: 0.1, 25 | speed: 0.5 26 | }); 27 | } 28 | 29 | function draw() { 30 | background(0); 31 | box(200); 32 | } 33 | ``` 34 | 35 | After creating the camera, it can be controlled using the mouse or the keyboard with the following default controls: 36 | 37 | ``` 38 | Mouse: click : toggle pointer lock 39 | left/right : yaw 40 | up/down : pitch 41 | 42 | Keys: a/d : yaw | left/right* 43 | w/s : forward/backward 44 | e/q : up/down 45 | r/f : pitch | elevation* 46 | ←/→ : left/right 47 | ↑/↓ : forward/backward 48 | +/- : field of view 49 | 50 | * when pointerLock is enabled 51 | ``` 52 | 53 | 54 | ### Examples 55 | 56 | - [MazeRunner on openprocessing](https://www.openprocessing.org/sketch/755273) 57 | - [MazeRunner on editor.p5js.org](https://editor.p5js.org/jwdunn1/sketches/iI-2XX0Hw) 58 | - [Multi-camera](https://editor.p5js.org/jwdunn1/sketches/L6xSzdKM8) 59 | - [Multi-instance](https://editor.p5js.org/jwdunn1/sketches/QzBAQZPVT) 60 | - [Stereo vision](https://editor.p5js.org/jwdunn1/sketches/nxfEMXn-s) 61 | - [Controller](https://editor.p5js.org/jwdunn1/sketches/3IaJbTnkd) 62 | - [Stereo MazeRunner](https://editor.p5js.org/jwdunn1/present/q24UkWcZe) 63 | 64 | ### Utility methods 65 | 66 | Mouse pointer lock can be enabled by invoking the `usePointerLock()` method. Pointer lock is global for all instances, thus only one instance needs to set this. See the _Multi-instance_ and _Stereo vision_ examples above. 67 | 68 | ```javascript 69 | rover.usePointerLock(); 70 | ``` 71 | 72 | Note: If using p5.js instance mode, pass the instance variable (see the _Stereo MazeRunner_ example above). 73 | 74 | ```javascript 75 | rover.usePointerLock(p); 76 | ``` 77 | 78 | The camera can be reset to initialization values with the `reset()` method. Note, this will override any state set by the `setState()` method. 79 | 80 | ```javascript 81 | rover.reset(); 82 | ``` 83 | 84 | Camera state can be set with the `setState()` method. An object argument may contain any of the following optional property/value pairs: 85 | 86 | ```javascript 87 | rover.setState = { 88 | active: true, 89 | enableControl: false, 90 | position: [-400,50,0], 91 | rotation: [0,0.1,0], 92 | offset: [0,20], 93 | fov: 1, 94 | speed: 0.1, 95 | sensitivity: 0.02 96 | }; 97 | ``` 98 | 99 | Camera controls can be enabled or disabled by setting the boolean `enableControl` property. This is useful to switch camera controls in a multi-instance scenario. The camera view remains visible. See the _Multi-instance_ example above. By default, a camera's controls are enabled. In other scenarios like the _Stereo vision_ example, the two camera controls remain enabled for simultaneous control of the view. 100 | 101 | ```javascript 102 | rover.enableControl = true; 103 | ``` 104 | 105 | A camera view can be enabled or disabled with the `setActive()` method. It requires one boolean argument. When disabled, the camera is no longer updated or visible. Another view can take its place. See the _Multi-camera_ example above. By default, a camera's view is enabled. 106 | 107 | ```javascript 108 | rover.setActive(true); 109 | ``` 110 | 111 | ### Customization 112 | 113 | The RoverCam class can be extended. See the _MazeRunner_ and _Stereo MazeRunner_ examples above. 114 | 115 | ```javascript 116 | class Player extends RoverCam { 117 | constructor(){ 118 | super(); 119 | this.speed = 0.04; 120 | this.dimensions = createVector(1, 3, 1); 121 | this.velocity = createVector(0, 0, 0); 122 | this.gravity = createVector(0, 0.03, 0); 123 | this.grounded = false; 124 | } 125 | 126 | update(){ 127 | this.velocity.add(this.gravity); 128 | this.position.add(this.velocity); 129 | 130 | // extend the keyboard controls by adding a hop behavior 131 | if (this.grounded && keyIsDown(32)){ // space 132 | this.grounded = false; 133 | this.velocity.y = -1.5; 134 | this.position.y -= 0.2; 135 | } 136 | } 137 | } 138 | ``` 139 | Note: In multi-instance uses, pass the instance variable into the extended constructor and then on to the `super()`. 140 | 141 | ```javascript 142 | class Player extends RoverCam { 143 | constructor(p){ 144 | super(p); 145 | } 146 | } 147 | ``` 148 | 149 | --- 150 | 151 | The keyboard can be remapped. The following example replaces the `w,a,s,d` primary movement keys to `k,j,l,h` 152 | 153 | ```javascript 154 | var rover; 155 | 156 | function setup() { 157 | createCanvas(800, 800, WEBGL); 158 | rover = createRoverCam(); 159 | rover.keyMap.mx1 = [75, 38]; // k, UP_ARROW 160 | rover.keyMap.mx2 = [74, 40]; // j, DOWN_ARROW 161 | rover.keyMap.my1 = [72, 37]; // h, LEFT_ARROW 162 | rover.keyMap.my2 = [76, 39]; // l, RIGHT_ARROW 163 | } 164 | ``` 165 | 166 | --- 167 | 168 | The controller method can be customized. It must call on the primitive translation and rotation methods to move the camera. See the _Controller_ example above. 169 | 170 | ```javascript 171 | 172 | rover.controller = function() { // override 173 | if (RoverCam.pointerLock) { 174 | this.yaw(movedX * this.sensitivity / 10); // mouse left/right 175 | this.pitch(movedY * this.sensitivity / 10); // mouse up/down 176 | if(keyIsDown(72) || keyIsDown(LEFT_ARROW)) this.moveY(this.speed); // h 177 | if(keyIsDown(76) || keyIsDown(RIGHT_ARROW)) this.moveY(-this.speed);// l 178 | } 179 | else { // otherwise yaw/pitch with keys 180 | if (keyIsDown(72) || keyIsDown(LEFT_ARROW)) this.yaw(-0.02); // h 181 | if (keyIsDown(76) || keyIsDown(RIGHT_ARROW)) this.yaw(0.02); // l 182 | if (keyIsDown(82)) this.pitch(-0.02); // r 183 | if (keyIsDown(70)) this.pitch(0.02); // f 184 | } 185 | if (keyIsDown(75) || keyIsDown(UP_ARROW)) this.moveX(this.speed); // k 186 | if (keyIsDown(74) || keyIsDown(DOWN_ARROW)) this.moveX(-this.speed); // j 187 | if (keyIsDown(69)) this.moveZ(this.speed); // e 188 | }; 189 | ``` 190 | ### Primitive internal camera control methods 191 | 192 | The following methods control the camera translation and rotation. The camera local axes are X in front/behind, Y to the right/left, and Z above/below. 193 | 194 | #### Translation 195 | 196 | - `moveX()` translates the camera forward or backward 197 | - `moveY()` translates the camera left or right 198 | - `moveZ()` translates the camera up or down 199 | - `elevate()` adjusts the camera's up/down position by an offset 200 | 201 | #### Rotation 202 | 203 | - `yaw()` rotates the camera left or right (pan) 204 | - `pitch()` rotates the camera up or right (tilt) 205 | - `roll()` rotates the camera along its X axis (TBD) 206 | 207 | #### Field of view 208 | 209 | - `fov()` adjusts the camera's fovy perspective value 210 | 211 | ### History 212 | 213 | Source forked from github.com/jrc03c/queasycam and ported to JavaScript. 214 | -------------------------------------------------------------------------------- /p5.rovercam.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * The p5.RoverCam library - First-Person 3D CameraControl for p5.js and WEBGL. 4 | * 5 | * Copyright © 2020 by p5.RoverCam authors 6 | * 7 | * Source: https://github.com/freshfork/p5.RoverCam 8 | * 9 | * MIT License: https://opensource.org/licenses/MIT 10 | * 11 | * 12 | * explanatory note: 13 | * 14 | * p5.RoverCam is a derivative of the QueasyCam Library by Josh Castle, 15 | * ported to JavaScript for p5.js from github.com/jrc03c/queasycam 16 | * 17 | * updates 18 | * 20200628 incorporate pointerLock and overridable controller method 19 | * 20200629 add support for switching between multiple cameras 20 | * 20200701 v1.1.0 fix registerMethod and allow for p5js instance mode 21 | * 20200702 v1.1.1 moved pointerLock; added keymap and ocular offsetting 22 | */ 23 | 24 | // First-person camera control 25 | // Mouse: 26 | // left/right : yaw 27 | // up/down : pitch 28 | // click : enter/leave pointerLock 29 | 30 | // Keys: a/d : yaw or left/right if pointerLock 31 | // w/s : forward/backward 32 | // e/q : up/down 33 | 34 | class RoverCam { 35 | constructor(instance) { 36 | this.sensitivity = 0.02; 37 | this.friction = 0.8; 38 | this.speed = 0.1; 39 | this.reset(); 40 | this.active = true; // use the setActive method 41 | this.enableControl = true; // used to enable/disable controls 42 | if(instance !== undefined) this.p5 = instance; 43 | else this.p5 = p5.instance; 44 | if(this.p5 !== null) 45 | this.p5.registerMethod('post', () => {if (this.active) this.draw();}); 46 | this.keyMap = { // maps each control command to a pair of keys 47 | mx1: [87, 38], // w, UP_ARROW 48 | mx2: [83, 40], // s, DOWN_ARROW 49 | my1: [65, 37], // a, LEFT_ARROW 50 | my2: [68, 39], // d, RIGHT_ARROW 51 | mz1: [69, 69], // e 52 | mz2: [81, 81], // q 53 | y1: [65, 37], // a, LEFT_ARROW 54 | y2: [68, 39], // d, RIGHT_ARROW 55 | p1: [82, 82], // r 56 | p2: [70, 70], // f 57 | r1: [90, 90], // z 58 | r2: [67, 67], // c 59 | f1: [107,187], // + 60 | f2: [109,189], // - 61 | e1: [82, 82], // r 62 | e2: [70, 70] // f 63 | }; 64 | } 65 | 66 | // Application can override the following method 67 | controller() { // default behavior 68 | if (!this.enableControl) return; 69 | var k = this.keyMap, p = this.p5; 70 | if (RoverCam.pointerLock) { 71 | this.yaw(p.movedX * this.sensitivity / 10); // mouse left/right 72 | this.pitch(p.movedY * this.sensitivity / 10); // mouse up/down 73 | if (p.keyIsDown(k.my1[0]) || p.keyIsDown(k.my1[1])) this.moveY( this.speed); // a 74 | if (p.keyIsDown(k.my2[0]) || p.keyIsDown(k.my2[1])) this.moveY(-this.speed); // d 75 | if (p.keyIsDown(k.e1[0]) || p.keyIsDown(k.e1[1])) this.elevate(-this.speed); // r 76 | if (p.keyIsDown(k.e2[0]) || p.keyIsDown(k.e2[1])) this.elevate(this.speed); // f 77 | } else { // otherwise yaw/pitch with keys 78 | if (p.keyIsDown(k.y1[0]) || p.keyIsDown(k.y1[1])) this.yaw(-this.sensitivity); // a 79 | if (p.keyIsDown(k.y2[0]) || p.keyIsDown(k.y2[1])) this.yaw(this.sensitivity); // d 80 | if (p.keyIsDown(k.p1[0]) || p.keyIsDown(k.p1[1])) this.pitch(-this.sensitivity); // r 81 | if (p.keyIsDown(k.p2[0]) || p.keyIsDown(k.p2[1])) this.pitch(this.sensitivity); // f 82 | } 83 | if (p.keyIsDown(k.mx1[0]) || p.keyIsDown(k.mx1[1])) this.moveX(this.speed); // w 84 | if (p.keyIsDown(k.mx2[0]) || p.keyIsDown(k.mx2[1])) this.moveX(-this.speed); // s 85 | if (p.keyIsDown(k.mz1[0]) || p.keyIsDown(k.mz1[1])) this.moveZ(this.speed); // e 86 | if (p.keyIsDown(k.mz2[0]) || p.keyIsDown(k.mz2[1])) this.moveZ(-this.speed); // q 87 | 88 | if (p.keyIsDown(k.f1[0]) || p.keyIsDown(k.f1[1])) this.fov(-this.sensitivity / 10); // + 89 | if (p.keyIsDown(k.f2[0]) || p.keyIsDown(k.f2[1])) this.fov(this.sensitivity / 10); // - 90 | 91 | // test roll TBD 92 | //if(p.keyIsDown(k.r1[0]) || p.keyIsDown(k.r1[1])) this.roll(this.sensitivity); // z 93 | //if(p.keyIsDown(k.r2[0]) || p.keyIsDown(k.r2[1])) this.roll(-this.sensitivity); // c 94 | } 95 | 96 | // Primitive internal camera control methods 97 | moveX(speed) { 98 | this.velocity.add(p5.Vector.mult(this.forward, speed)); 99 | } 100 | moveY(speed) { 101 | this.velocity.add(p5.Vector.mult(this.right, speed)); 102 | } 103 | moveZ(speed) { 104 | this.velocity.add(p5.Vector.mult(this.up, -speed)); 105 | } 106 | yaw(angle) { 107 | this.pan += angle; 108 | } 109 | pitch(angle) { 110 | this.tilt += angle; 111 | this.tilt = this.clamp(this.tilt, -Math.PI / 2.01, Math.PI / 2.01); 112 | if (this.tilt == Math.PI / 2.0) this.tilt += 0.001; 113 | } 114 | roll(angle) { // TBD: useful for flight sim or sloped racetracks 115 | this.rot += angle; 116 | } 117 | fov(angle) { 118 | this.fovy += angle; 119 | this.width = 0; // trigger a perspective call in the draw loop 120 | } 121 | elevate(delta) { 122 | this.offset[0] += delta; 123 | } 124 | 125 | // Utility methods 126 | usePointerLock(instance) { 127 | if(instance === undefined) instance = p5.instance; 128 | if(instance === null) return; 129 | RoverCam.canvas = instance._renderer.elt; 130 | // ffd8 - click into pointerlock example based on: 131 | // https://p5js.org/reference/#/p5/exitPointerLock 132 | document.addEventListener('click', () => { 133 | if (!RoverCam.pointerLock) { 134 | RoverCam.pointerLock = true; 135 | instance.requestPointerLock(); 136 | } else { 137 | instance.exitPointerLock(); 138 | RoverCam.pointerLock = false; 139 | } 140 | }, false); 141 | document.addEventListener('pointerlockchange', RoverCam.onPointerlockChange, false); 142 | } 143 | reset() { 144 | this.pan = 0.0; 145 | this.tilt = 0.0; 146 | this.rot = 0.0; 147 | this.fovy = 1.0; 148 | this.width = 0; // trigger a perspective call in the draw loop 149 | this.height = 0; 150 | this.position = new p5.Vector(0, 0, 0); 151 | this.velocity = new p5.Vector(0, 0, 0); 152 | this.up = new p5.Vector(0, 1, 0); 153 | this.right = new p5.Vector(1, 0, 0); 154 | this.forward = new p5.Vector(0, 0, 1); 155 | this.offset = [0,0]; // ffd8 - adjust height of cam 156 | } 157 | setActive(active){ // method to switch between multiple cameras 158 | this.active = active; 159 | if(active) this.width=0; // trigger a perspective call in the draw loop 160 | } 161 | setState(state){ // state object can have fov,active,rotation,position 162 | if(state.fov !== undefined) { 163 | this.fovy = state.fov; 164 | this.width = 0; // trigger a perspective call in the draw loop; 165 | } 166 | if(state.active !== undefined) this.active = state.active; 167 | if(state.rotation !== undefined) { 168 | this.pan = state.rotation[0]; 169 | this.tilt = state.rotation[1]; 170 | this.rot = state.rotation[2]; 171 | } 172 | if(state.position !== undefined) this.position = new p5.Vector(state.position[0],state.position[1],state.position[2]); 173 | if(state.offset !== undefined) this.offset = state.offset; 174 | if(state.enableControl !== undefined) this.enableControl = state.enableControl; 175 | if(state.speed !== undefined) this.speed = state.speed; 176 | if(state.sensitivity !== undefined) this.sensitivity = state.sensitivity; 177 | } 178 | 179 | // This method is called after the main p5.js draw loop 180 | draw() { 181 | if (this.p5.width !== this.width || this.p5.height !== this.height) { 182 | this.p5.perspective(this.fovy, this.p5.width / this.p5.height, 0.01, 10000.0); 183 | this.width = this.p5.width; 184 | this.height = this.p5.height; 185 | } 186 | 187 | // Call the potentially overridden controller method 188 | this.controller(); 189 | 190 | this.forward = new p5.Vector(Math.cos(this.pan), Math.tan(this.tilt), Math.sin(this.pan)); 191 | this.forward.normalize(); 192 | this.right = new p5.Vector(Math.cos(this.pan - Math.PI / 2.0), 0, Math.sin(this.pan - Math.PI / 2.0)); 193 | // TBD: handle roll command (using this.rot) 194 | 195 | this.velocity.mult(this.friction); 196 | this.position.add(this.velocity); 197 | let position = p5.Vector.sub(this.position, p5.Vector.mult(this.right,this.offset[1])); 198 | let center = p5.Vector.add(position, this.forward); 199 | this.p5.camera(position.x, position.y+this.offset[0], position.z, center.x, center.y+this.offset[0], center.z, this.up.x, this.up.y, this.up.z); 200 | } 201 | 202 | clamp(aNumber, aMin, aMax) { 203 | return (aNumber > aMax ? aMax : 204 | aNumber < aMin ? aMin : 205 | aNumber); 206 | } 207 | } 208 | RoverCam.version = "1.1.1"; 209 | // Optional pointerLock applies to all RoverCam instances 210 | RoverCam.pointerLock = false; 211 | // handle exit from pointerLock when user presses ESCAPE 212 | RoverCam.onPointerlockChange = () => { 213 | if (document.pointerLockElement !== RoverCam.canvas && 214 | document.mozPointerLockElement !== RoverCam.canvas) RoverCam.pointerLock = false; 215 | } 216 | p5.prototype.createRoverCam = function(){ 217 | return new RoverCam(this); 218 | } --------------------------------------------------------------------------------