babylon.freeCamera.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. var globalPosition: Vector3;
  234. if (this.parent) {
  235. globalPosition = BABYLON.Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
  236. } else {
  237. globalPosition = this.position;
  238. }
  239. globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
  240. this._collider.radius = this.ellipsoid;
  241. this.getScene()._getNewPosition(this._oldPosition, velocity, this._collider, 3, this._newPosition);
  242. this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
  243. if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
  244. this.position.addInPlace(this._diffPosition);
  245. if (this.onCollide) {
  246. this.onCollide(this._collider.collidedMesh);
  247. }
  248. }
  249. }
  250. public _checkInputs(): void {
  251. if (!this._localDirection) {
  252. this._localDirection = BABYLON.Vector3.Zero();
  253. this._transformedDirection = BABYLON.Vector3.Zero();
  254. }
  255. // Keyboard
  256. for (var index = 0; index < this._keys.length; index++) {
  257. var keyCode = this._keys[index];
  258. var speed = this._computeLocalCameraSpeed();
  259. if (this.keysLeft.indexOf(keyCode) !== -1) {
  260. this._localDirection.copyFromFloats(-speed, 0, 0);
  261. } else if (this.keysUp.indexOf(keyCode) !== -1) {
  262. this._localDirection.copyFromFloats(0, 0, speed);
  263. } else if (this.keysRight.indexOf(keyCode) !== -1) {
  264. this._localDirection.copyFromFloats(speed, 0, 0);
  265. } else if (this.keysDown.indexOf(keyCode) !== -1) {
  266. this._localDirection.copyFromFloats(0, 0, -speed);
  267. }
  268. this.getViewMatrix().invertToRef(this._cameraTransformMatrix);
  269. BABYLON.Vector3.TransformNormalToRef(this._localDirection, this._cameraTransformMatrix, this._transformedDirection);
  270. this.cameraDirection.addInPlace(this._transformedDirection);
  271. }
  272. }
  273. public _update(): void {
  274. this._checkInputs();
  275. var needToMove = this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  276. var needToRotate = Math.abs(this.cameraRotation.x) > 0 || Math.abs(this.cameraRotation.y) > 0;
  277. // Move
  278. if (needToMove) {
  279. if (this.checkCollisions && this.getScene().collisionsEnabled) {
  280. this._collideWithWorld(this.cameraDirection);
  281. if (this.applyGravity) {
  282. var oldPosition = this.position;
  283. this._collideWithWorld(this.getScene().gravity);
  284. this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, this.position) != 0);
  285. }
  286. } else {
  287. this.position.addInPlace(this.cameraDirection);
  288. }
  289. }
  290. // Rotate
  291. if (needToRotate) {
  292. this.rotation.x += this.cameraRotation.x;
  293. this.rotation.y += this.cameraRotation.y;
  294. if (!this.noRotationConstraint) {
  295. var limit = (Math.PI / 2) * 0.95;
  296. if (this.rotation.x > limit)
  297. this.rotation.x = limit;
  298. if (this.rotation.x < -limit)
  299. this.rotation.x = -limit;
  300. }
  301. }
  302. // Inertia
  303. if (needToMove) {
  304. if (Math.abs(this.cameraDirection.x) < BABYLON.Engine.Epsilon) {
  305. this.cameraDirection.x = 0;
  306. }
  307. if (Math.abs(this.cameraDirection.y) < BABYLON.Engine.Epsilon) {
  308. this.cameraDirection.y = 0;
  309. }
  310. if (Math.abs(this.cameraDirection.z) < BABYLON.Engine.Epsilon) {
  311. this.cameraDirection.z = 0;
  312. }
  313. this.cameraDirection.scaleInPlace(this.inertia);
  314. }
  315. if (needToRotate) {
  316. if (Math.abs(this.cameraRotation.x) < BABYLON.Engine.Epsilon) {
  317. this.cameraRotation.x = 0;
  318. }
  319. if (Math.abs(this.cameraRotation.y) < BABYLON.Engine.Epsilon) {
  320. this.cameraRotation.y = 0;
  321. }
  322. this.cameraRotation.scaleInPlace(this.inertia);
  323. }
  324. }
  325. public _getViewMatrix(): Matrix {
  326. BABYLON.Vector3.FromFloatsToRef(0, 0, 1, this._referencePoint);
  327. if (!this.lockedTarget) {
  328. // Compute
  329. if (this.upVector.x != 0 || this.upVector.y != 1.0 || this.upVector.z != 0) {
  330. BABYLON.Matrix.LookAtLHToRef(BABYLON.Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
  331. BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  332. this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
  333. this._lookAtTemp.invert();
  334. this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
  335. } else {
  336. BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  337. }
  338. BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
  339. // Computing target and final matrix
  340. this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
  341. } else {
  342. this._currentTarget.copyFrom(this._getLockedTargetPosition());
  343. }
  344. BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
  345. return this._viewMatrix;
  346. }
  347. }
  348. }