spriteSceneComponent.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import { Nullable } from "../types";
  2. import { Observable } from "../Misc/observable";
  3. import { Scene } from "../scene";
  4. import { Sprite } from "./sprite";
  5. import { ISpriteManager } from "./spriteManager";
  6. import { Ray } from "../Culling/ray";
  7. import { Camera } from "../Cameras/camera";
  8. import { PickingInfo } from "../Collisions/pickingInfo";
  9. import { ISceneComponent, SceneComponentConstants } from "../sceneComponent";
  10. import { ActionEvent } from "../Actions/actionEvent";
  11. import { Constants } from "../Engines/constants";
  12. declare module "../scene" {
  13. export interface Scene {
  14. /** @hidden */
  15. _pointerOverSprite: Nullable<Sprite>;
  16. /** @hidden */
  17. _pickedDownSprite: Nullable<Sprite>;
  18. /** @hidden */
  19. _tempSpritePickingRay: Nullable<Ray>;
  20. /**
  21. * All of the sprite managers added to this scene
  22. * @see http://doc.babylonjs.com/babylon101/sprites
  23. */
  24. spriteManagers: Array<ISpriteManager>;
  25. /**
  26. * An event triggered when sprites rendering is about to start
  27. * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
  28. */
  29. onBeforeSpritesRenderingObservable: Observable<Scene>;
  30. /**
  31. * An event triggered when sprites rendering is done
  32. * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
  33. */
  34. onAfterSpritesRenderingObservable: Observable<Scene>;
  35. /** @hidden */
  36. _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
  37. /** Launch a ray to try to pick a sprite in the scene
  38. * @param x position on screen
  39. * @param y position on screen
  40. * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
  41. * @param fastCheck defines if the first intersection will be used (and not the closest)
  42. * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
  43. * @returns a PickingInfo
  44. */
  45. pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
  46. /** Use the given ray to pick a sprite in the scene
  47. * @param ray The ray (in world space) to use to pick meshes
  48. * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
  49. * @param fastCheck defines if the first intersection will be used (and not the closest)
  50. * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
  51. * @returns a PickingInfo
  52. */
  53. pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
  54. /** @hidden */
  55. _internalMultiPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]>;
  56. /** Launch a ray to try to pick sprites in the scene
  57. * @param x position on screen
  58. * @param y position on screen
  59. * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
  60. * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
  61. * @returns a PickingInfo array
  62. */
  63. multiPickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]>;
  64. /** Use the given ray to pick sprites in the scene
  65. * @param ray The ray (in world space) to use to pick meshes
  66. * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
  67. * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
  68. * @returns a PickingInfo array
  69. */
  70. multiPickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]>;
  71. /**
  72. * Force the sprite under the pointer
  73. * @param sprite defines the sprite to use
  74. */
  75. setPointerOverSprite(sprite: Nullable<Sprite>): void;
  76. /**
  77. * Gets the sprite under the pointer
  78. * @returns a Sprite or null if no sprite is under the pointer
  79. */
  80. getPointerOverSprite(): Nullable<Sprite>;
  81. }
  82. }
  83. Scene.prototype._internalPickSprites = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
  84. if (!PickingInfo) {
  85. return null;
  86. }
  87. var pickingInfo = null;
  88. if (!camera) {
  89. if (!this.activeCamera) {
  90. return null;
  91. }
  92. camera = this.activeCamera;
  93. }
  94. if (this.spriteManagers.length > 0) {
  95. for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
  96. var spriteManager = this.spriteManagers[spriteIndex];
  97. if (!spriteManager.isPickable) {
  98. continue;
  99. }
  100. var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
  101. if (!result || !result.hit) {
  102. continue;
  103. }
  104. if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
  105. continue;
  106. }
  107. pickingInfo = result;
  108. if (fastCheck) {
  109. break;
  110. }
  111. }
  112. }
  113. return pickingInfo || new PickingInfo();
  114. };
  115. Scene.prototype._internalMultiPickSprites = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]> {
  116. if (!PickingInfo) {
  117. return null;
  118. }
  119. var pickingInfos = new Array<PickingInfo>();
  120. if (!camera) {
  121. if (!this.activeCamera) {
  122. return null;
  123. }
  124. camera = this.activeCamera;
  125. }
  126. if (this.spriteManagers.length > 0) {
  127. for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
  128. var spriteManager = this.spriteManagers[spriteIndex];
  129. if (!spriteManager.isPickable) {
  130. continue;
  131. }
  132. var results = spriteManager.multiIntersects(ray, camera, predicate);
  133. if (results !== null) {
  134. pickingInfos = pickingInfos.concat(results);
  135. }
  136. }
  137. }
  138. return pickingInfos;
  139. };
  140. Scene.prototype.pickSprite = function(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
  141. this.createPickingRayInCameraSpaceToRef(x, y, this._tempSpritePickingRay!, camera);
  142. return this._internalPickSprites(this._tempSpritePickingRay!, predicate, fastCheck, camera);
  143. };
  144. Scene.prototype.pickSpriteWithRay = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
  145. if (!this._tempSpritePickingRay) {
  146. return null;
  147. }
  148. if (!camera) {
  149. if (!this.activeCamera) {
  150. return null;
  151. }
  152. camera = this.activeCamera;
  153. }
  154. Ray.TransformToRef(ray, camera.getViewMatrix(), this._tempSpritePickingRay);
  155. return this._internalPickSprites(this._tempSpritePickingRay, predicate, fastCheck, camera);
  156. };
  157. Scene.prototype.multiPickSprite = function(x: number, y: number, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]> {
  158. this.createPickingRayInCameraSpaceToRef(x, y, this._tempSpritePickingRay!, camera);
  159. return this._internalMultiPickSprites(this._tempSpritePickingRay!, predicate, camera);
  160. };
  161. Scene.prototype.multiPickSpriteWithRay = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, camera?: Camera): Nullable<PickingInfo[]> {
  162. if (!this._tempSpritePickingRay) {
  163. return null;
  164. }
  165. if (!camera) {
  166. if (!this.activeCamera) {
  167. return null;
  168. }
  169. camera = this.activeCamera;
  170. }
  171. Ray.TransformToRef(ray, camera.getViewMatrix(), this._tempSpritePickingRay);
  172. return this._internalMultiPickSprites(this._tempSpritePickingRay, predicate, camera);
  173. };
  174. Scene.prototype.setPointerOverSprite = function(sprite: Nullable<Sprite>): void {
  175. if (this._pointerOverSprite === sprite) {
  176. return;
  177. }
  178. if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
  179. this._pointerOverSprite.actionManager.processTrigger(Constants.ACTION_OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
  180. }
  181. this._pointerOverSprite = sprite;
  182. if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
  183. this._pointerOverSprite.actionManager.processTrigger(Constants.ACTION_OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
  184. }
  185. };
  186. Scene.prototype.getPointerOverSprite = function(): Nullable<Sprite> {
  187. return this._pointerOverSprite;
  188. };
  189. /**
  190. * Defines the sprite scene component responsible to manage sprites
  191. * in a given scene.
  192. */
  193. export class SpriteSceneComponent implements ISceneComponent {
  194. /**
  195. * The component name helpfull to identify the component in the list of scene components.
  196. */
  197. public readonly name = SceneComponentConstants.NAME_SPRITE;
  198. /**
  199. * The scene the component belongs to.
  200. */
  201. public scene: Scene;
  202. /** @hidden */
  203. private _spritePredicate: (sprite: Sprite) => boolean;
  204. /**
  205. * Creates a new instance of the component for the given scene
  206. * @param scene Defines the scene to register the component in
  207. */
  208. constructor(scene: Scene) {
  209. this.scene = scene;
  210. this.scene.spriteManagers = new Array<ISpriteManager>();
  211. this.scene._tempSpritePickingRay = Ray ? Ray.Zero() : null;
  212. this.scene.onBeforeSpritesRenderingObservable = new Observable<Scene>();
  213. this.scene.onAfterSpritesRenderingObservable = new Observable<Scene>();
  214. this._spritePredicate = (sprite: Sprite): boolean => {
  215. if (!sprite.actionManager) {
  216. return false;
  217. }
  218. return sprite.isPickable && sprite.actionManager.hasPointerTriggers;
  219. };
  220. }
  221. /**
  222. * Registers the component in a given scene
  223. */
  224. public register(): void {
  225. this.scene._pointerMoveStage.registerStep(SceneComponentConstants.STEP_POINTERMOVE_SPRITE, this, this._pointerMove);
  226. this.scene._pointerDownStage.registerStep(SceneComponentConstants.STEP_POINTERDOWN_SPRITE, this, this._pointerDown);
  227. this.scene._pointerUpStage.registerStep(SceneComponentConstants.STEP_POINTERUP_SPRITE, this, this._pointerUp);
  228. }
  229. /**
  230. * Rebuilds the elements related to this component in case of
  231. * context lost for instance.
  232. */
  233. public rebuild(): void {
  234. /** Nothing to do for sprites */
  235. }
  236. /**
  237. * Disposes the component and the associated ressources.
  238. */
  239. public dispose(): void {
  240. this.scene.onBeforeSpritesRenderingObservable.clear();
  241. this.scene.onAfterSpritesRenderingObservable.clear();
  242. let spriteManagers = this.scene.spriteManagers;
  243. while (spriteManagers.length) {
  244. spriteManagers[0].dispose();
  245. }
  246. }
  247. private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
  248. var result = this.scene.pickSprite(x, y, this._spritePredicate, fastCheck, camera);
  249. if (result) {
  250. result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
  251. }
  252. return result;
  253. }
  254. private _pointerMove(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, element: HTMLElement): Nullable<PickingInfo> {
  255. var scene = this.scene;
  256. if (isMeshPicked) {
  257. scene.setPointerOverSprite(null);
  258. } else {
  259. pickResult = this._pickSpriteButKeepRay(pickResult, unTranslatedPointerX, unTranslatedPointerY, false, scene.cameraToUseForPointers || undefined);
  260. if (pickResult && pickResult.hit && pickResult.pickedSprite) {
  261. scene.setPointerOverSprite(pickResult.pickedSprite);
  262. if (!scene.doNotHandleCursors) {
  263. if (scene._pointerOverSprite && scene._pointerOverSprite.actionManager && scene._pointerOverSprite.actionManager.hoverCursor) {
  264. element.style.cursor = scene._pointerOverSprite.actionManager.hoverCursor;
  265. } else {
  266. element.style.cursor = scene.hoverCursor;
  267. }
  268. }
  269. } else {
  270. scene.setPointerOverSprite(null);
  271. }
  272. }
  273. return pickResult;
  274. }
  275. private _pointerDown(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
  276. var scene = this.scene;
  277. scene._pickedDownSprite = null;
  278. if (scene.spriteManagers.length > 0) {
  279. pickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
  280. if (pickResult && pickResult.hit && pickResult.pickedSprite) {
  281. if (pickResult.pickedSprite.actionManager) {
  282. scene._pickedDownSprite = pickResult.pickedSprite;
  283. switch (evt.button) {
  284. case 0:
  285. pickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
  286. break;
  287. case 1:
  288. pickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
  289. break;
  290. case 2:
  291. pickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
  292. break;
  293. }
  294. if (pickResult.pickedSprite.actionManager) {
  295. pickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
  296. }
  297. }
  298. }
  299. }
  300. return pickResult;
  301. }
  302. private _pointerUp(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
  303. var scene = this.scene;
  304. if (scene.spriteManagers.length > 0) {
  305. let spritePickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
  306. if (spritePickResult) {
  307. if (spritePickResult.hit && spritePickResult.pickedSprite) {
  308. if (spritePickResult.pickedSprite.actionManager) {
  309. spritePickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
  310. if (spritePickResult.pickedSprite.actionManager) {
  311. if (!this.scene._inputManager._isPointerSwiping()) {
  312. spritePickResult.pickedSprite.actionManager.processTrigger(Constants.ACTION_OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
  313. }
  314. }
  315. }
  316. }
  317. if (scene._pickedDownSprite && scene._pickedDownSprite.actionManager && scene._pickedDownSprite !== spritePickResult.pickedSprite) {
  318. scene._pickedDownSprite.actionManager.processTrigger(Constants.ACTION_OnPickOutTrigger, ActionEvent.CreateNewFromSprite(scene._pickedDownSprite, scene, evt));
  319. }
  320. }
  321. }
  322. return pickResult;
  323. }
  324. }