control3D.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
  2. module BABYLON.GUI {
  3. /**
  4. * Class used as base class for controls
  5. */
  6. export class Control3D implements IDisposable, IBehaviorAware<Control3D> {
  7. /** @hidden */
  8. public _host: GUI3DManager;
  9. private _node: Nullable<TransformNode>;
  10. private _downCount = 0;
  11. private _enterCount = -1;
  12. private _downPointerIds:{[id:number] : boolean} = {};
  13. private _isVisible = true;
  14. /** Gets or sets the control position in world space */
  15. public get position(): Vector3 {
  16. if (!this._node) {
  17. return Vector3.Zero();
  18. }
  19. return this._node.position;
  20. }
  21. public set position(value: Vector3) {
  22. if (!this._node) {
  23. return;
  24. }
  25. this._node.position = value;;
  26. }
  27. /** Gets or sets the control scaling in world space */
  28. public get scaling(): Vector3 {
  29. if (!this._node) {
  30. return new Vector3(1, 1, 1);
  31. }
  32. return this._node.scaling;
  33. }
  34. public set scaling(value: Vector3) {
  35. if (!this._node) {
  36. return;
  37. }
  38. this._node.scaling = value;;
  39. }
  40. /** Callback used to start pointer enter animation */
  41. public pointerEnterAnimation: () => void;
  42. /** Callback used to start pointer out animation */
  43. public pointerOutAnimation: () => void;
  44. /** Callback used to start pointer down animation */
  45. public pointerDownAnimation: () => void;
  46. /** Callback used to start pointer up animation */
  47. public pointerUpAnimation: () => void;
  48. /**
  49. * An event triggered when the pointer move over the control
  50. */
  51. public onPointerMoveObservable = new Observable<Vector3>();
  52. /**
  53. * An event triggered when the pointer move out of the control
  54. */
  55. public onPointerOutObservable = new Observable<Control3D>();
  56. /**
  57. * An event triggered when the pointer taps the control
  58. */
  59. public onPointerDownObservable = new Observable<Vector3WithInfo>();
  60. /**
  61. * An event triggered when pointer is up
  62. */
  63. public onPointerUpObservable = new Observable<Vector3WithInfo>();
  64. /**
  65. * An event triggered when a control is clicked on (with a mouse)
  66. */
  67. public onPointerClickObservable = new Observable<Vector3WithInfo>();
  68. /**
  69. * An event triggered when pointer enters the control
  70. */
  71. public onPointerEnterObservable = new Observable<Control3D>();
  72. /**
  73. * Gets or sets the parent container
  74. */
  75. public parent: Nullable<Container3D>;
  76. // Behaviors
  77. private _behaviors = new Array<Behavior<Control3D>>();
  78. /**
  79. * Gets the list of attached behaviors
  80. * @see http://doc.babylonjs.com/features/behaviour
  81. */
  82. public get behaviors(): Behavior<Control3D>[] {
  83. return this._behaviors;
  84. }
  85. /**
  86. * Attach a behavior to the control
  87. * @see http://doc.babylonjs.com/features/behaviour
  88. * @param behavior defines the behavior to attach
  89. * @returns the current control
  90. */
  91. public addBehavior(behavior: Behavior<Control3D>): Control3D {
  92. var index = this._behaviors.indexOf(behavior);
  93. if (index !== -1) {
  94. return this;
  95. }
  96. behavior.init();
  97. let scene = this._host.scene;
  98. if (scene.isLoading) {
  99. // We defer the attach when the scene will be loaded
  100. scene.onDataLoadedObservable.addOnce(() => {
  101. behavior.attach(this);
  102. });
  103. } else {
  104. behavior.attach(this);
  105. }
  106. this._behaviors.push(behavior);
  107. return this;
  108. }
  109. /**
  110. * Remove an attached behavior
  111. * @see http://doc.babylonjs.com/features/behaviour
  112. * @param behavior defines the behavior to attach
  113. * @returns the current control
  114. */
  115. public removeBehavior(behavior: Behavior<Control3D>): Control3D {
  116. var index = this._behaviors.indexOf(behavior);
  117. if (index === -1) {
  118. return this;
  119. }
  120. this._behaviors[index].detach();
  121. this._behaviors.splice(index, 1);
  122. return this;
  123. }
  124. /**
  125. * Gets an attached behavior by name
  126. * @param name defines the name of the behavior to look for
  127. * @see http://doc.babylonjs.com/features/behaviour
  128. * @returns null if behavior was not found else the requested behavior
  129. */
  130. public getBehaviorByName(name: string): Nullable<Behavior<Control3D>> {
  131. for (var behavior of this._behaviors) {
  132. if (behavior.name === name) {
  133. return behavior;
  134. }
  135. }
  136. return null;
  137. }
  138. /** Gets or sets a boolean indicating if the control is visible */
  139. public get isVisible(): boolean {
  140. return this._isVisible;
  141. }
  142. public set isVisible(value: boolean) {
  143. if (this._isVisible === value) {
  144. return;
  145. }
  146. this._isVisible = value;
  147. let mesh = this.mesh;
  148. if (mesh) {
  149. mesh.setEnabled(value);
  150. }
  151. }
  152. /**
  153. * Creates a new control
  154. * @param name defines the control name
  155. */
  156. constructor(
  157. /** Defines the control name */
  158. public name?: string) {
  159. }
  160. /**
  161. * Gets a string representing the class name
  162. */
  163. public get typeName(): string {
  164. return this._getTypeName();
  165. }
  166. protected _getTypeName(): string {
  167. return "Control3D";
  168. }
  169. /**
  170. * Gets the transform node used by this control
  171. */
  172. public get node(): Nullable<TransformNode> {
  173. return this._node;
  174. }
  175. /**
  176. * Gets the mesh used to render this control
  177. */
  178. public get mesh(): Nullable<AbstractMesh> {
  179. if (this._node instanceof AbstractMesh) {
  180. return this._node as AbstractMesh;
  181. }
  182. return null;
  183. }
  184. /**
  185. * Link the control as child of the given node
  186. * @param node defines the node to link to. Use null to unlink the control
  187. * @returns the current control
  188. */
  189. public linkToTransformNode(node: Nullable<TransformNode>): Control3D {
  190. if (this._node) {
  191. this._node.parent = node;
  192. }
  193. return this;
  194. }
  195. /** @hidden **/
  196. public _prepareNode(scene: Scene): void{
  197. if (!this._node) {
  198. this._node = this._createNode(scene);
  199. if (!this.node) {
  200. return;
  201. }
  202. this._node!.metadata = this; // Store the control on the metadata field in order to get it when picking
  203. this._node!.position = this.position;
  204. this._node!.scaling = this.scaling;
  205. let mesh = this.mesh;
  206. if (mesh) {
  207. mesh.isPickable = true;
  208. this._affectMaterial(mesh);
  209. }
  210. }
  211. }
  212. /**
  213. * Node creation.
  214. * Can be overriden by children
  215. * @param scene defines the scene where the node must be attached
  216. * @returns the attached node or null if none. Must return a Mesh or AbstractMesh if there is an atttached visible object
  217. */
  218. protected _createNode(scene: Scene): Nullable<TransformNode> {
  219. // Do nothing by default
  220. return null;
  221. }
  222. /**
  223. * Affect a material to the given mesh
  224. * @param mesh defines the mesh which will represent the control
  225. */
  226. protected _affectMaterial(mesh: AbstractMesh) {
  227. mesh.material = null;
  228. }
  229. // Pointers
  230. /** @hidden */
  231. public _onPointerMove(target: Control3D, coordinates: Vector3): void {
  232. this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this);
  233. }
  234. /** @hidden */
  235. public _onPointerEnter(target: Control3D): boolean {
  236. if (this._enterCount > 0) {
  237. return false;
  238. }
  239. if (this._enterCount === -1) { // -1 is for touch input, we are now sure we are with a mouse or pencil
  240. this._enterCount = 0;
  241. }
  242. this._enterCount++;
  243. this.onPointerEnterObservable.notifyObservers(this, -1, target, this);
  244. if (this.pointerEnterAnimation) {
  245. this.pointerEnterAnimation();
  246. }
  247. return true;
  248. }
  249. /** @hidden */
  250. public _onPointerOut(target: Control3D): void {
  251. this._enterCount = 0;
  252. this.onPointerOutObservable.notifyObservers(this, -1, target, this);
  253. if (this.pointerOutAnimation) {
  254. this.pointerOutAnimation();
  255. }
  256. }
  257. /** @hidden */
  258. public _onPointerDown(target: Control3D, coordinates: Vector3, pointerId:number, buttonIndex: number): boolean {
  259. if (this._downCount !== 0) {
  260. return false;
  261. }
  262. this._downCount++;
  263. this._downPointerIds[pointerId] = true;
  264. this.onPointerDownObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this);
  265. if (this.pointerDownAnimation) {
  266. this.pointerDownAnimation();
  267. }
  268. return true;
  269. }
  270. /** @hidden */
  271. public _onPointerUp(target: Control3D, coordinates: Vector3, pointerId:number, buttonIndex: number, notifyClick: boolean): void {
  272. this._downCount = 0;
  273. delete this._downPointerIds[pointerId];
  274. if (notifyClick && (this._enterCount > 0 || this._enterCount === -1)) {
  275. this.onPointerClickObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this);
  276. }
  277. this.onPointerUpObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this);
  278. if (this.pointerUpAnimation) {
  279. this.pointerUpAnimation();
  280. }
  281. }
  282. /** @hidden */
  283. public forcePointerUp(pointerId: Nullable<number> = null) {
  284. if(pointerId !== null){
  285. this._onPointerUp(this, Vector3.Zero(), pointerId, 0, true);
  286. }else{
  287. for(var key in this._downPointerIds){
  288. this._onPointerUp(this, Vector3.Zero(), +key as number, 0, true);
  289. }
  290. }
  291. }
  292. /** @hidden */
  293. public _processObservables(type: number, pickedPoint: Vector3, pointerId:number, buttonIndex: number): boolean {
  294. if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
  295. this._onPointerMove(this, pickedPoint);
  296. var previousControlOver = this._host._lastControlOver[pointerId];
  297. if (previousControlOver && previousControlOver !== this) {
  298. previousControlOver._onPointerOut(this);
  299. }
  300. if (previousControlOver !== this) {
  301. this._onPointerEnter(this);
  302. }
  303. this._host._lastControlOver[pointerId] = this;
  304. return true;
  305. }
  306. if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
  307. this._onPointerDown(this, pickedPoint, pointerId, buttonIndex);
  308. this._host._lastControlDown[pointerId] = this;
  309. this._host._lastPickedControl = this;
  310. return true;
  311. }
  312. if (type === BABYLON.PointerEventTypes.POINTERUP) {
  313. if (this._host._lastControlDown[pointerId]) {
  314. this._host._lastControlDown[pointerId]._onPointerUp(this, pickedPoint, pointerId, buttonIndex, true);
  315. }
  316. delete this._host._lastControlDown[pointerId];
  317. return true;
  318. }
  319. return false;
  320. }
  321. /** @hidden */
  322. public _disposeNode(): void {
  323. if (this._node) {
  324. this._node.dispose();
  325. this._node = null;
  326. }
  327. }
  328. /**
  329. * Releases all associated resources
  330. */
  331. public dispose() {
  332. this.onPointerDownObservable.clear();
  333. this.onPointerEnterObservable.clear();
  334. this.onPointerMoveObservable.clear();
  335. this.onPointerOutObservable.clear();
  336. this.onPointerUpObservable.clear();
  337. this.onPointerClickObservable.clear();
  338. this._disposeNode();
  339. // Behaviors
  340. for (var behavior of this._behaviors) {
  341. behavior.detach();
  342. }
  343. }
  344. }
  345. }