babylon.freeCamera.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. module BABYLON {
  2. export class FreeCamera extends Camera {
  3. public cameraDirection = new BABYLON.Vector3(0, 0, 0);
  4. public cameraRotation = new BABYLON.Vector2(0, 0);
  5. public rotation = new BABYLON.Vector3(0, 0, 0);
  6. public ellipsoid = new BABYLON.Vector3(0.5, 1, 0.5);
  7. public keysUp = [38];
  8. public keysDown = [40];
  9. public keysLeft = [37];
  10. public keysRight = [39];
  11. public speed = 2.0;
  12. public checkCollisions = false;
  13. public applyGravity = false;
  14. public noRotationConstraint = false;
  15. public angularSensibility = 2000.0;
  16. public lockedTarget = null;
  17. public onCollide = null;
  18. private _keys = [];
  19. private _collider = new Collider();
  20. private _needMoveForGravity = true;
  21. private _currentTarget = BABYLON.Vector3.Zero();
  22. private _viewMatrix = BABYLON.Matrix.Zero();
  23. private _camMatrix = BABYLON.Matrix.Zero();
  24. private _cameraTransformMatrix = BABYLON.Matrix.Zero();
  25. private _cameraRotationMatrix = BABYLON.Matrix.Zero();
  26. private _referencePoint = BABYLON.Vector3.Zero();
  27. private _transformedReferencePoint = BABYLON.Vector3.Zero();
  28. private _oldPosition = BABYLON.Vector3.Zero();
  29. private _diffPosition = BABYLON.Vector3.Zero();
  30. private _newPosition = BABYLON.Vector3.Zero();
  31. private _lookAtTemp = BABYLON.Matrix.Zero();
  32. private _tempMatrix = BABYLON.Matrix.Zero();
  33. private _attachedElement: HTMLElement;
  34. private _localDirection: Vector3;
  35. private _transformedDirection: Vector3;
  36. private _onMouseDown: (e: MouseEvent) => any;
  37. private _onMouseUp: (e: MouseEvent) => any;
  38. private _onMouseOut: (e: MouseEvent) => any;
  39. private _onMouseMove: (e: MouseEvent) => any;
  40. private _onKeyDown: (e: KeyboardEvent) => any;
  41. private _onKeyUp: (e: KeyboardEvent) => any;
  42. private _onLostFocus: (e: FocusEvent) => any;
  43. private _reset: () => void;
  44. constructor(name: string, position: Vector3, scene: Scene) {
  45. super(name, position, scene);
  46. }
  47. public _getLockedTargetPosition(): Vector3 {
  48. if (!this.lockedTarget) {
  49. return null;
  50. }
  51. return this.lockedTarget.position || this.lockedTarget;
  52. }
  53. // Cache
  54. public _initCache() {
  55. super._initCache();
  56. this._cache.lockedTarget = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  57. this._cache.rotation = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  58. }
  59. public _updateCache(ignoreParentClass?: boolean): void {
  60. if (!ignoreParentClass) {
  61. super._updateCache();
  62. }
  63. var lockedTargetPosition = this._getLockedTargetPosition();
  64. if (!lockedTargetPosition) {
  65. this._cache.lockedTarget = null;
  66. }
  67. else {
  68. if (!this._cache.lockedTarget) {
  69. this._cache.lockedTarget = lockedTargetPosition.clone();
  70. }
  71. else {
  72. this._cache.lockedTarget.copyFrom(lockedTargetPosition);
  73. }
  74. }
  75. this._cache.rotation.copyFrom(this.rotation);
  76. }
  77. // Synchronized
  78. public _isSynchronizedViewMatrix(): boolean {
  79. if (!super._isSynchronizedViewMatrix()) {
  80. return false;
  81. }
  82. var lockedTargetPosition = this._getLockedTargetPosition();
  83. return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
  84. && this._cache.rotation.equals(this.rotation);
  85. }
  86. // Methods
  87. private _computeLocalCameraSpeed(): number {
  88. return this.speed * ((BABYLON.Tools.GetDeltaTime() / (BABYLON.Tools.GetFps() * 10.0)));
  89. }
  90. // Target
  91. public setTarget(target: Vector3): void {
  92. this.upVector.normalize();
  93. BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
  94. this._camMatrix.invert();
  95. this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
  96. var vDir = target.subtract(this.position);
  97. if (vDir.x >= 0.0) {
  98. this.rotation.y = (-Math.atan(vDir.z / vDir.x) + Math.PI / 2.0);
  99. } else {
  100. this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
  101. }
  102. this.rotation.z = -Math.acos(BABYLON.Vector3.Dot(new BABYLON.Vector3(0, 1.0, 0), this.upVector));
  103. if (isNaN(this.rotation.x)) {
  104. this.rotation.x = 0;
  105. }
  106. if (isNaN(this.rotation.y)) {
  107. this.rotation.y = 0;
  108. }
  109. if (isNaN(this.rotation.z)) {
  110. this.rotation.z = 0;
  111. }
  112. }
  113. public getTarget(): Vector3 {
  114. return this._currentTarget;
  115. }
  116. // Controls
  117. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  118. var previousPosition;
  119. var engine = this.getEngine();
  120. if (this._attachedElement) {
  121. return;
  122. }
  123. this._attachedElement = element;
  124. if (this._onMouseDown === undefined) {
  125. this._onMouseDown = evt => {
  126. previousPosition = {
  127. x: evt.clientX,
  128. y: evt.clientY
  129. };
  130. if (!noPreventDefault) {
  131. evt.preventDefault();
  132. }
  133. };
  134. this._onMouseUp = evt => {
  135. previousPosition = null;
  136. if (!noPreventDefault) {
  137. evt.preventDefault();
  138. }
  139. };
  140. this._onMouseOut = evt => {
  141. previousPosition = null;
  142. this._keys = [];
  143. if (!noPreventDefault) {
  144. evt.preventDefault();
  145. }
  146. };
  147. this._onMouseMove = evt => {
  148. if (!previousPosition && !engine.isPointerLock) {
  149. return;
  150. }
  151. var offsetX;
  152. var offsetY;
  153. if (!engine.isPointerLock) {
  154. offsetX = evt.clientX - previousPosition.x;
  155. offsetY = evt.clientY - previousPosition.y;
  156. } else {
  157. offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
  158. offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
  159. }
  160. this.cameraRotation.y += offsetX / this.angularSensibility;
  161. this.cameraRotation.x += offsetY / this.angularSensibility;
  162. previousPosition = {
  163. x: evt.clientX,
  164. y: evt.clientY
  165. };
  166. if (!noPreventDefault) {
  167. evt.preventDefault();
  168. }
  169. };
  170. this._onKeyDown = evt => {
  171. if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
  172. this.keysDown.indexOf(evt.keyCode) !== -1 ||
  173. this.keysLeft.indexOf(evt.keyCode) !== -1 ||
  174. this.keysRight.indexOf(evt.keyCode) !== -1) {
  175. var index = this._keys.indexOf(evt.keyCode);
  176. if (index === -1) {
  177. this._keys.push(evt.keyCode);
  178. }
  179. if (!noPreventDefault) {
  180. evt.preventDefault();
  181. }
  182. }
  183. };
  184. this._onKeyUp = evt => {
  185. if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
  186. this.keysDown.indexOf(evt.keyCode) !== -1 ||
  187. this.keysLeft.indexOf(evt.keyCode) !== -1 ||
  188. this.keysRight.indexOf(evt.keyCode) !== -1) {
  189. var index = this._keys.indexOf(evt.keyCode);
  190. if (index >= 0) {
  191. this._keys.splice(index, 1);
  192. }
  193. if (!noPreventDefault) {
  194. evt.preventDefault();
  195. }
  196. }
  197. };
  198. this._onLostFocus = () => {
  199. this._keys = [];
  200. };
  201. this._reset = () => {
  202. this._keys = [];
  203. previousPosition = null;
  204. this.cameraDirection = new BABYLON.Vector3(0, 0, 0);
  205. this.cameraRotation = new BABYLON.Vector2(0, 0);
  206. };
  207. }
  208. element.addEventListener("mousedown", this._onMouseDown, false);
  209. element.addEventListener("mouseup", this._onMouseUp, false);
  210. element.addEventListener("mouseout", this._onMouseOut, false);
  211. element.addEventListener("mousemove", this._onMouseMove, false);
  212. window.addEventListener("keydown", this._onKeyDown, false);
  213. window.addEventListener("keyup", this._onKeyUp, false);
  214. window.addEventListener("blur", this._onLostFocus, false);
  215. }
  216. public detachControl(element: HTMLElement): void {
  217. if (this._attachedElement != element) {
  218. return;
  219. }
  220. element.removeEventListener("mousedown", this._onMouseDown);
  221. element.removeEventListener("mouseup", this._onMouseUp);
  222. element.removeEventListener("mouseout", this._onMouseOut);
  223. element.removeEventListener("mousemove", this._onMouseMove);
  224. window.removeEventListener("keydown", this._onKeyDown);
  225. window.removeEventListener("keyup", this._onKeyUp);
  226. window.removeEventListener("blur", this._onLostFocus);
  227. this._attachedElement = null;
  228. if (this._reset) {
  229. this._reset();
  230. }
  231. }
  232. public _collideWithWorld(velocity: Vector3): void {
  233. this.position.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
  234. this._collider.radius = this.ellipsoid;
  235. this.getScene()._getNewPosition(this._oldPosition, velocity, this._collider, 3, this._newPosition);
  236. this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
  237. if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
  238. this.position.addInPlace(this._diffPosition);
  239. if (this.onCollide) {
  240. this.onCollide(this._collider.collidedMesh);
  241. }
  242. }
  243. }
  244. public _checkInputs(): void {
  245. if (!this._localDirection) {
  246. this._localDirection = BABYLON.Vector3.Zero();
  247. this._transformedDirection = BABYLON.Vector3.Zero();
  248. }
  249. // Keyboard
  250. for (var index = 0; index < this._keys.length; index++) {
  251. var keyCode = this._keys[index];
  252. var speed = this._computeLocalCameraSpeed();
  253. if (this.keysLeft.indexOf(keyCode) !== -1) {
  254. this._localDirection.copyFromFloats(-speed, 0, 0);
  255. } else if (this.keysUp.indexOf(keyCode) !== -1) {
  256. this._localDirection.copyFromFloats(0, 0, speed);
  257. } else if (this.keysRight.indexOf(keyCode) !== -1) {
  258. this._localDirection.copyFromFloats(speed, 0, 0);
  259. } else if (this.keysDown.indexOf(keyCode) !== -1) {
  260. this._localDirection.copyFromFloats(0, 0, -speed);
  261. }
  262. this.getViewMatrix().invertToRef(this._cameraTransformMatrix);
  263. BABYLON.Vector3.TransformNormalToRef(this._localDirection, this._cameraTransformMatrix, this._transformedDirection);
  264. this.cameraDirection.addInPlace(this._transformedDirection);
  265. }
  266. }
  267. public _update(): void {
  268. this._checkInputs();
  269. var needToMove = this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  270. var needToRotate = Math.abs(this.cameraRotation.x) > 0 || Math.abs(this.cameraRotation.y) > 0;
  271. // Move
  272. if (needToMove) {
  273. if (this.checkCollisions && this.getScene().collisionsEnabled) {
  274. this._collideWithWorld(this.cameraDirection);
  275. if (this.applyGravity) {
  276. var oldPosition = this.position;
  277. this._collideWithWorld(this.getScene().gravity);
  278. this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, this.position) != 0);
  279. }
  280. } else {
  281. this.position.addInPlace(this.cameraDirection);
  282. }
  283. }
  284. // Rotate
  285. if (needToRotate) {
  286. this.rotation.x += this.cameraRotation.x;
  287. this.rotation.y += this.cameraRotation.y;
  288. if (!this.noRotationConstraint) {
  289. var limit = (Math.PI / 2) * 0.95;
  290. if (this.rotation.x > limit)
  291. this.rotation.x = limit;
  292. if (this.rotation.x < -limit)
  293. this.rotation.x = -limit;
  294. }
  295. }
  296. // Inertia
  297. if (needToMove) {
  298. if (Math.abs(this.cameraDirection.x) < BABYLON.Engine.Epsilon) {
  299. this.cameraDirection.x = 0;
  300. }
  301. if (Math.abs(this.cameraDirection.y) < BABYLON.Engine.Epsilon) {
  302. this.cameraDirection.y = 0;
  303. }
  304. if (Math.abs(this.cameraDirection.z) < BABYLON.Engine.Epsilon) {
  305. this.cameraDirection.z = 0;
  306. }
  307. this.cameraDirection.scaleInPlace(this.inertia);
  308. }
  309. if (needToRotate) {
  310. if (Math.abs(this.cameraRotation.x) < BABYLON.Engine.Epsilon) {
  311. this.cameraRotation.x = 0;
  312. }
  313. if (Math.abs(this.cameraRotation.y) < BABYLON.Engine.Epsilon) {
  314. this.cameraRotation.y = 0;
  315. }
  316. this.cameraRotation.scaleInPlace(this.inertia);
  317. }
  318. }
  319. public _getViewMatrix(): Matrix {
  320. BABYLON.Vector3.FromFloatsToRef(0, 0, 1, this._referencePoint);
  321. if (!this.lockedTarget) {
  322. // Compute
  323. if (this.upVector.x != 0 || this.upVector.y != 1.0 || this.upVector.z != 0) {
  324. BABYLON.Matrix.LookAtLHToRef(BABYLON.Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
  325. BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  326. this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
  327. this._lookAtTemp.invert();
  328. this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
  329. } else {
  330. BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  331. }
  332. BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
  333. // Computing target and final matrix
  334. this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
  335. } else {
  336. this._currentTarget.copyFrom(this._getLockedTargetPosition());
  337. }
  338. BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
  339. return this._viewMatrix;
  340. }
  341. }
  342. }