advancedDynamicTexture.ts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. import { DynamicTexture, Nullable, Observer, Camera, Engine, KeyboardInfoPre, ClipboardEventTypes, ClipboardInfo, PointerInfoPre, PointerInfo, Layer, Viewport, Scene, Texture, KeyboardEventTypes, Vector3, Matrix, Vector2, Tools, PointerEventTypes, AbstractMesh, StandardMaterial, Color3 } from 'babylonjs';
  2. import { Container } from "./controls/container";
  3. import { Control } from "./controls/control";
  4. import { Style } from "./style";
  5. import { Measure } from "./measure";
  6. /**
  7. * Interface used to define a control that can receive focus
  8. */
  9. export interface IFocusableControl {
  10. /**
  11. * Function called when the control receives the focus
  12. */
  13. onFocus(): void;
  14. /**
  15. * Function called when the control loses the focus
  16. */
  17. onBlur(): void;
  18. /**
  19. * Function called to let the control handle keyboard events
  20. * @param evt defines the current keyboard event
  21. */
  22. processKeyboard(evt: KeyboardEvent): void;
  23. /**
  24. * Defines clipboard events
  25. * @param {ClipboardEventsTypes} evt
  26. */
  27. onClipboardKeyBoardEvent(type: ClipboardEventTypes): void;
  28. onClipboardPointerEvents(showHighlightedText: boolean): void;
  29. /**
  30. * Function called to get the list of controls that should not steal the focus from this control
  31. * @returns an array of controls
  32. */
  33. keepsFocusWith(): Nullable<Control[]>;
  34. }
  35. /**
  36. * Class used to create texture to support 2D GUI elements
  37. * @see http://doc.babylonjs.com/how_to/gui
  38. */
  39. export class AdvancedDynamicTexture extends DynamicTexture {
  40. private _isDirty = false;
  41. private _ctrKeyOn = false;
  42. private _renderObserver: Nullable<Observer<Camera>>;
  43. private _resizeObserver: Nullable<Observer<Engine>>;
  44. private _preKeyboardObserver: Nullable<Observer<KeyboardInfoPre>>;
  45. private _pointerMoveObserver: Nullable<Observer<PointerInfoPre>>;
  46. private _pointerObserver: Nullable<Observer<PointerInfo>>;
  47. private _canvasPointerOutObserver: Nullable<Observer<PointerEvent>>;
  48. private _background: string;
  49. /** @hidden */
  50. public _rootContainer = new Container("root");
  51. /** @hidden */
  52. public _lastPickedControl: Control;
  53. /** @hidden */
  54. public _lastControlOver: { [pointerId: number]: Control } = {};
  55. /** @hidden */
  56. public _lastControlDown: { [pointerId: number]: Control } = {};
  57. /** @hidden */
  58. public _capturingControl: { [pointerId: number]: Control } = {};
  59. /** @hidden */
  60. public _shouldBlockPointer: boolean;
  61. /** @hidden */
  62. public _layerToDispose: Nullable<Layer>;
  63. /** @hidden */
  64. public _linkedControls = new Array<Control>();
  65. private _isFullscreen = false;
  66. private _fullscreenViewport = new Viewport(0, 0, 1, 1);
  67. private _idealWidth = 0;
  68. private _idealHeight = 0;
  69. private _useSmallestIdeal: boolean = false;
  70. private _renderAtIdealSize = false;
  71. private _focusedControl: Nullable<IFocusableControl>;
  72. private _blockNextFocusCheck = false;
  73. private _renderScale = 1;
  74. private _rootCanvas: Nullable<HTMLCanvasElement>;
  75. /**
  76. * Gets or sets a boolean defining if alpha is stored as premultiplied
  77. */
  78. public premulAlpha = false;
  79. /**
  80. * Gets or sets a number used to scale rendering size (2 means that the texture will be twice bigger).
  81. * Useful when you want more antialiasing
  82. */
  83. public get renderScale(): number {
  84. return this._renderScale;
  85. }
  86. public set renderScale(value: number) {
  87. if (value === this._renderScale) {
  88. return;
  89. }
  90. this._renderScale = value;
  91. this._onResize();
  92. }
  93. /** Gets or sets the background color */
  94. public get background(): string {
  95. return this._background;
  96. }
  97. public set background(value: string) {
  98. if (this._background === value) {
  99. return;
  100. }
  101. this._background = value;
  102. this.markAsDirty();
  103. }
  104. /**
  105. * Gets or sets the ideal width used to design controls.
  106. * The GUI will then rescale everything accordingly
  107. * @see http://doc.babylonjs.com/how_to/gui#adaptive-scaling
  108. */
  109. public get idealWidth(): number {
  110. return this._idealWidth;
  111. }
  112. public set idealWidth(value: number) {
  113. if (this._idealWidth === value) {
  114. return;
  115. }
  116. this._idealWidth = value;
  117. this.markAsDirty();
  118. this._rootContainer._markAllAsDirty();
  119. }
  120. /**
  121. * Gets or sets the ideal height used to design controls.
  122. * The GUI will then rescale everything accordingly
  123. * @see http://doc.babylonjs.com/how_to/gui#adaptive-scaling
  124. */
  125. public get idealHeight(): number {
  126. return this._idealHeight;
  127. }
  128. public set idealHeight(value: number) {
  129. if (this._idealHeight === value) {
  130. return;
  131. }
  132. this._idealHeight = value;
  133. this.markAsDirty();
  134. this._rootContainer._markAllAsDirty();
  135. }
  136. /**
  137. * Gets or sets a boolean indicating if the smallest ideal value must be used if idealWidth and idealHeight are both set
  138. * @see http://doc.babylonjs.com/how_to/gui#adaptive-scaling
  139. */
  140. public get useSmallestIdeal(): boolean {
  141. return this._useSmallestIdeal;
  142. }
  143. public set useSmallestIdeal(value: boolean) {
  144. if (this._useSmallestIdeal === value) {
  145. return;
  146. }
  147. this._useSmallestIdeal = value;
  148. this.markAsDirty();
  149. this._rootContainer._markAllAsDirty();
  150. }
  151. /**
  152. * Gets or sets a boolean indicating if adaptive scaling must be used
  153. * @see http://doc.babylonjs.com/how_to/gui#adaptive-scaling
  154. */
  155. public get renderAtIdealSize(): boolean {
  156. return this._renderAtIdealSize;
  157. }
  158. public set renderAtIdealSize(value: boolean) {
  159. if (this._renderAtIdealSize === value) {
  160. return;
  161. }
  162. this._renderAtIdealSize = value;
  163. this._onResize();
  164. }
  165. /**
  166. * Gets the underlying layer used to render the texture when in fullscreen mode
  167. */
  168. public get layer(): Nullable<Layer> {
  169. return this._layerToDispose;
  170. }
  171. /**
  172. * Gets the root container control
  173. */
  174. public get rootContainer(): Container {
  175. return this._rootContainer;
  176. }
  177. /**
  178. * Gets or sets the current focused control
  179. */
  180. public get focusedControl(): Nullable<IFocusableControl> {
  181. return this._focusedControl;
  182. }
  183. public set focusedControl(control: Nullable<IFocusableControl>) {
  184. if (this._focusedControl == control) {
  185. return;
  186. }
  187. if (this._focusedControl) {
  188. this._focusedControl.onBlur();
  189. }
  190. if (control) {
  191. control.onFocus();
  192. }
  193. this._focusedControl = control;
  194. }
  195. /**
  196. * Gets or sets a boolean indicating if the texture must be rendered in background or foreground when in fullscreen mode
  197. */
  198. public get isForeground(): boolean {
  199. if (!this.layer) {
  200. return true;
  201. }
  202. return (!this.layer.isBackground);
  203. }
  204. public set isForeground(value: boolean) {
  205. if (!this.layer) {
  206. return;
  207. }
  208. if (this.layer.isBackground === !value) {
  209. return;
  210. }
  211. this.layer.isBackground = !value;
  212. }
  213. /**
  214. * Creates a new AdvancedDynamicTexture
  215. * @param name defines the name of the texture
  216. * @param width defines the width of the texture
  217. * @param height defines the height of the texture
  218. * @param scene defines the hosting scene
  219. * @param generateMipMaps defines a boolean indicating if mipmaps must be generated (false by default)
  220. * @param samplingMode defines the texture sampling mode (Texture.NEAREST_SAMPLINGMODE by default)
  221. */
  222. constructor(name: string, width = 0, height = 0, scene: Nullable<Scene>, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
  223. super(name, { width: width, height: height }, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
  224. scene = this.getScene();
  225. if (!scene || !this._texture) {
  226. return;
  227. }
  228. this._rootCanvas = scene.getEngine()!.getRenderingCanvas()!;
  229. this._renderObserver = scene.onBeforeCameraRenderObservable.add((camera: Camera) => this._checkUpdate(camera));
  230. this._preKeyboardObserver = scene.onPreKeyboardObservable.add((info) => {
  231. if (!this._focusedControl || (!this._ctrKeyOn && (info.event.ctrlKey || info.event.metaKey))) {
  232. this._ctrKeyOn = true;
  233. return;
  234. }
  235. if (info.type === KeyboardEventTypes.KEYDOWN) {
  236. //get the event type
  237. let type = ClipboardInfo.GetTypeFromCharacter(info.event);
  238. (this._ctrKeyOn && type !== -1) ? this._focusedControl.onClipboardKeyBoardEvent(type) : this._focusedControl.processKeyboard(info.event);
  239. }
  240. info.skipOnPointerObservable = true;
  241. this._ctrKeyOn = false;
  242. });
  243. this._rootContainer._link(null, this);
  244. this.hasAlpha = true;
  245. if (!width || !height) {
  246. this._resizeObserver = scene.getEngine().onResizeObservable.add(() => this._onResize());
  247. this._onResize();
  248. }
  249. this._texture.isReady = true;
  250. }
  251. /**
  252. * Function used to execute a function on all controls
  253. * @param func defines the function to execute
  254. * @param container defines the container where controls belong. If null the root container will be used
  255. */
  256. public executeOnAllControls(func: (control: Control) => void, container?: Container) {
  257. if (!container) {
  258. container = this._rootContainer;
  259. }
  260. func(container);
  261. for (var child of container.children) {
  262. if ((<any>child).children) {
  263. this.executeOnAllControls(func, (<Container>child));
  264. continue;
  265. }
  266. func(child);
  267. }
  268. }
  269. /**
  270. * Marks the texture as dirty forcing a complete update
  271. */
  272. public markAsDirty() {
  273. this._isDirty = true;
  274. }
  275. /**
  276. * Helper function used to create a new style
  277. * @returns a new style
  278. * @see http://doc.babylonjs.com/how_to/gui#styles
  279. */
  280. public createStyle(): Style {
  281. return new Style(this);
  282. }
  283. /**
  284. * Adds a new control to the root container
  285. * @param control defines the control to add
  286. * @returns the current texture
  287. */
  288. public addControl(control: Control): AdvancedDynamicTexture {
  289. this._rootContainer.addControl(control);
  290. return this;
  291. }
  292. /**
  293. * Removes a control from the root container
  294. * @param control defines the control to remove
  295. * @returns the current texture
  296. */
  297. public removeControl(control: Control): AdvancedDynamicTexture {
  298. this._rootContainer.removeControl(control);
  299. return this;
  300. }
  301. /**
  302. * Release all resources
  303. */
  304. public dispose(): void {
  305. let scene = this.getScene();
  306. if (!scene) {
  307. return;
  308. }
  309. this._rootCanvas = null;
  310. scene.onBeforeCameraRenderObservable.remove(this._renderObserver);
  311. if (this._resizeObserver) {
  312. scene.getEngine().onResizeObservable.remove(this._resizeObserver);
  313. }
  314. if (this._pointerMoveObserver) {
  315. scene.onPrePointerObservable.remove(this._pointerMoveObserver);
  316. }
  317. if (this._pointerObserver) {
  318. scene.onPointerObservable.remove(this._pointerObserver);
  319. }
  320. if (this._preKeyboardObserver) {
  321. scene.onPreKeyboardObservable.remove(this._preKeyboardObserver);
  322. }
  323. if (this._canvasPointerOutObserver) {
  324. scene.getEngine().onCanvasPointerOutObservable.remove(this._canvasPointerOutObserver);
  325. }
  326. if (this._layerToDispose) {
  327. this._layerToDispose.texture = null;
  328. this._layerToDispose.dispose();
  329. this._layerToDispose = null;
  330. }
  331. this._rootContainer.dispose();
  332. super.dispose();
  333. }
  334. private _onResize(): void {
  335. let scene = this.getScene();
  336. if (!scene) {
  337. return;
  338. }
  339. // Check size
  340. var engine = scene.getEngine();
  341. var textureSize = this.getSize();
  342. var renderWidth = engine.getRenderWidth() * this._renderScale;
  343. var renderHeight = engine.getRenderHeight() * this._renderScale;
  344. if (this._renderAtIdealSize) {
  345. if (this._idealWidth) {
  346. renderHeight = (renderHeight * this._idealWidth) / renderWidth;
  347. renderWidth = this._idealWidth;
  348. } else if (this._idealHeight) {
  349. renderWidth = (renderWidth * this._idealHeight) / renderHeight;
  350. renderHeight = this._idealHeight;
  351. }
  352. }
  353. if (textureSize.width !== renderWidth || textureSize.height !== renderHeight) {
  354. this.scaleTo(renderWidth, renderHeight);
  355. this.markAsDirty();
  356. if (this._idealWidth || this._idealHeight) {
  357. this._rootContainer._markAllAsDirty();
  358. }
  359. }
  360. }
  361. /** @hidden */
  362. public _getGlobalViewport(scene: Scene): Viewport {
  363. var engine = scene.getEngine();
  364. return this._fullscreenViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
  365. }
  366. /**
  367. * Get screen coordinates for a vector3
  368. * @param position defines the position to project
  369. * @param worldMatrix defines the world matrix to use
  370. * @returns the projected position
  371. */
  372. public getProjectedPosition(position: Vector3, worldMatrix: Matrix): Vector2 {
  373. var scene = this.getScene();
  374. if (!scene) {
  375. return Vector2.Zero();
  376. }
  377. var globalViewport = this._getGlobalViewport(scene);
  378. var projectedPosition = Vector3.Project(position, worldMatrix, scene.getTransformMatrix(), globalViewport);
  379. projectedPosition.scaleInPlace(this.renderScale);
  380. return new Vector2(projectedPosition.x, projectedPosition.y);
  381. }
  382. private _checkUpdate(camera: Camera): void {
  383. if (this._layerToDispose) {
  384. if ((camera.layerMask & this._layerToDispose.layerMask) === 0) {
  385. return;
  386. }
  387. }
  388. if (this._isFullscreen && this._linkedControls.length) {
  389. var scene = this.getScene();
  390. if (!scene) {
  391. return;
  392. }
  393. var globalViewport = this._getGlobalViewport(scene);
  394. for (var control of this._linkedControls) {
  395. if (!control.isVisible) {
  396. continue;
  397. }
  398. var mesh = control._linkedMesh;
  399. if (!mesh || mesh.isDisposed()) {
  400. Tools.SetImmediate(() => {
  401. control.linkWithMesh(null);
  402. });
  403. continue;
  404. }
  405. var position = mesh.getBoundingInfo().boundingSphere.center;
  406. var projectedPosition = Vector3.Project(position, mesh.getWorldMatrix(), scene.getTransformMatrix(), globalViewport);
  407. if (projectedPosition.z < 0 || projectedPosition.z > 1) {
  408. control.notRenderable = true;
  409. continue;
  410. }
  411. control.notRenderable = false;
  412. // Account for RenderScale.
  413. projectedPosition.scaleInPlace(this.renderScale);
  414. control._moveToProjectedPosition(projectedPosition);
  415. }
  416. }
  417. if (!this._isDirty && !this._rootContainer.isDirty) {
  418. return;
  419. }
  420. this._isDirty = false;
  421. this._render();
  422. this.update(true, this.premulAlpha);
  423. }
  424. private _render(): void {
  425. var textureSize = this.getSize();
  426. var renderWidth = textureSize.width;
  427. var renderHeight = textureSize.height;
  428. // Clear
  429. var context = this.getContext();
  430. context.clearRect(0, 0, renderWidth, renderHeight);
  431. if (this._background) {
  432. context.save();
  433. context.fillStyle = this._background;
  434. context.fillRect(0, 0, renderWidth, renderHeight);
  435. context.restore();
  436. }
  437. // Render
  438. context.font = "18px Arial";
  439. context.strokeStyle = "white";
  440. var measure = new Measure(0, 0, renderWidth, renderHeight);
  441. this._rootContainer._draw(measure, context);
  442. }
  443. /** @hidden */
  444. public _changeCursor(cursor: string) {
  445. if (this._rootCanvas) {
  446. this._rootCanvas.style.cursor = cursor;
  447. }
  448. }
  449. private _doPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): void {
  450. var scene = this.getScene();
  451. if (!scene) {
  452. return;
  453. }
  454. var engine = scene.getEngine();
  455. var textureSize = this.getSize();
  456. if (this._isFullscreen) {
  457. x = x * (textureSize.width / engine.getRenderWidth());
  458. y = y * (textureSize.height / engine.getRenderHeight());
  459. }
  460. if (this._capturingControl[pointerId]) {
  461. this._capturingControl[pointerId]._processObservables(type, x, y, pointerId, buttonIndex);
  462. return;
  463. }
  464. if (!this._rootContainer._processPicking(x, y, type, pointerId, buttonIndex)) {
  465. this._changeCursor("");
  466. if (type === PointerEventTypes.POINTERMOVE) {
  467. if (this._lastControlOver[pointerId]) {
  468. this._lastControlOver[pointerId]._onPointerOut(this._lastControlOver[pointerId]);
  469. }
  470. delete this._lastControlOver[pointerId];
  471. }
  472. }
  473. this._manageFocus();
  474. }
  475. /** @hidden */
  476. public _cleanControlAfterRemovalFromList(list: { [pointerId: number]: Control }, control: Control) {
  477. for (var pointerId in list) {
  478. if (!list.hasOwnProperty(pointerId)) {
  479. continue;
  480. }
  481. var lastControlOver = list[pointerId];
  482. if (lastControlOver === control) {
  483. delete list[pointerId];
  484. }
  485. }
  486. }
  487. /** @hidden */
  488. public _cleanControlAfterRemoval(control: Control) {
  489. this._cleanControlAfterRemovalFromList(this._lastControlDown, control);
  490. this._cleanControlAfterRemovalFromList(this._lastControlOver, control);
  491. }
  492. /** Attach to all scene events required to support pointer events */
  493. public attach(): void {
  494. var scene = this.getScene();
  495. if (!scene) {
  496. return;
  497. }
  498. this._pointerMoveObserver = scene.onPrePointerObservable.add((pi, state) => {
  499. if (scene!.isPointerCaptured((<PointerEvent>(pi.event)).pointerId)) {
  500. return;
  501. }
  502. //check for focused control and call the onClipboardPointerEvents
  503. (this._focusedControl) ? ((pi.type === PointerEventTypes.POINTERDOUBLETAP) ? this._focusedControl.onClipboardPointerEvents(true) : null) : null;
  504. if (pi.type !== PointerEventTypes.POINTERMOVE
  505. && pi.type !== PointerEventTypes.POINTERUP
  506. && pi.type !== PointerEventTypes.POINTERDOWN) {
  507. return;
  508. }
  509. if (!scene) {
  510. return;
  511. }
  512. let camera = scene.cameraToUseForPointers || scene.activeCamera;
  513. if (!camera) {
  514. return;
  515. }
  516. let engine = scene.getEngine();
  517. let viewport = camera.viewport;
  518. let x = (scene.pointerX / engine.getHardwareScalingLevel() - viewport.x * engine.getRenderWidth()) / viewport.width;
  519. let y = (scene.pointerY / engine.getHardwareScalingLevel() - viewport.y * engine.getRenderHeight()) / viewport.height;
  520. this._shouldBlockPointer = false;
  521. // Do picking modifies _shouldBlockPointer
  522. this._doPicking(x, y, pi.type, (pi.event as PointerEvent).pointerId || 0, pi.event.button);
  523. // Avoid overwriting a true skipOnPointerObservable to false
  524. if (this._shouldBlockPointer) {
  525. pi.skipOnPointerObservable = this._shouldBlockPointer;
  526. }
  527. });
  528. this._attachToOnPointerOut(scene);
  529. }
  530. /**
  531. * Connect the texture to a hosting mesh to enable interactions
  532. * @param mesh defines the mesh to attach to
  533. * @param supportPointerMove defines a boolean indicating if pointer move events must be catched as well
  534. */
  535. public attachToMesh(mesh: AbstractMesh, supportPointerMove = true): void {
  536. var scene = this.getScene();
  537. if (!scene) {
  538. return;
  539. }
  540. this._pointerObserver = scene.onPointerObservable.add((pi, state) => {
  541. if (pi.type !== PointerEventTypes.POINTERMOVE
  542. && pi.type !== PointerEventTypes.POINTERUP
  543. && pi.type !== PointerEventTypes.POINTERDOWN) {
  544. return;
  545. }
  546. var pointerId = (pi.event as PointerEvent).pointerId || 0;
  547. if (pi.pickInfo && pi.pickInfo.hit && pi.pickInfo.pickedMesh === mesh) {
  548. var uv = pi.pickInfo.getTextureCoordinates();
  549. if (uv) {
  550. let size = this.getSize();
  551. this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type, pointerId, pi.event.button);
  552. }
  553. } else if (pi.type === PointerEventTypes.POINTERUP) {
  554. if (this._lastControlDown[pointerId]) {
  555. this._lastControlDown[pointerId]._forcePointerUp(pointerId);
  556. }
  557. delete this._lastControlDown[pointerId];
  558. if (this.focusedControl) {
  559. const friendlyControls = this.focusedControl.keepsFocusWith();
  560. let canMoveFocus = true;
  561. if (friendlyControls) {
  562. for (var control of friendlyControls) {
  563. // Same host, no need to keep the focus
  564. if (this === control._host) {
  565. continue;
  566. }
  567. // Different hosts
  568. const otherHost = control._host;
  569. if (otherHost._lastControlOver[pointerId] && otherHost._lastControlOver[pointerId].isAscendant(control)) {
  570. canMoveFocus = false;
  571. break;
  572. }
  573. }
  574. }
  575. if (canMoveFocus) {
  576. this.focusedControl = null;
  577. }
  578. }
  579. } else if (pi.type === PointerEventTypes.POINTERMOVE) {
  580. if (this._lastControlOver[pointerId]) {
  581. this._lastControlOver[pointerId]._onPointerOut(this._lastControlOver[pointerId]);
  582. }
  583. delete this._lastControlOver[pointerId];
  584. }
  585. });
  586. mesh.enablePointerMoveEvents = supportPointerMove;
  587. this._attachToOnPointerOut(scene);
  588. }
  589. /**
  590. * Move the focus to a specific control
  591. * @param control defines the control which will receive the focus
  592. */
  593. public moveFocusToControl(control: IFocusableControl): void {
  594. this.focusedControl = control;
  595. this._lastPickedControl = <any>control;
  596. this._blockNextFocusCheck = true;
  597. }
  598. private _manageFocus(): void {
  599. if (this._blockNextFocusCheck) {
  600. this._blockNextFocusCheck = false;
  601. this._lastPickedControl = <any>this._focusedControl;
  602. return;
  603. }
  604. // Focus management
  605. if (this._focusedControl) {
  606. if (this._focusedControl !== (<any>this._lastPickedControl)) {
  607. if (this._lastPickedControl.isFocusInvisible) {
  608. return;
  609. }
  610. this.focusedControl = null;
  611. }
  612. }
  613. }
  614. private _attachToOnPointerOut(scene: Scene): void {
  615. this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add((pointerEvent) => {
  616. if (this._lastControlOver[pointerEvent.pointerId]) {
  617. this._lastControlOver[pointerEvent.pointerId]._onPointerOut(this._lastControlOver[pointerEvent.pointerId]);
  618. }
  619. delete this._lastControlOver[pointerEvent.pointerId];
  620. if (this._lastControlDown[pointerEvent.pointerId]) {
  621. this._lastControlDown[pointerEvent.pointerId]._forcePointerUp();
  622. }
  623. delete this._lastControlDown[pointerEvent.pointerId];
  624. });
  625. }
  626. // Statics
  627. /**
  628. * Creates a new AdvancedDynamicTexture in projected mode (ie. attached to a mesh)
  629. * @param mesh defines the mesh which will receive the texture
  630. * @param width defines the texture width (1024 by default)
  631. * @param height defines the texture height (1024 by default)
  632. * @param supportPointerMove defines a boolean indicating if the texture must capture move events (true by default)
  633. * @param onlyAlphaTesting defines a boolean indicating that alpha blending will not be used (only alpha testing) (false by default)
  634. * @returns a new AdvancedDynamicTexture
  635. */
  636. public static CreateForMesh(mesh: AbstractMesh, width = 1024, height = 1024, supportPointerMove = true, onlyAlphaTesting = false): AdvancedDynamicTexture {
  637. var result = new AdvancedDynamicTexture(mesh.name + " AdvancedDynamicTexture", width, height, mesh.getScene(), true, Texture.TRILINEAR_SAMPLINGMODE);
  638. var material = new StandardMaterial("AdvancedDynamicTextureMaterial", mesh.getScene());
  639. material.backFaceCulling = false;
  640. material.diffuseColor = Color3.Black();
  641. material.specularColor = Color3.Black();
  642. if (onlyAlphaTesting) {
  643. material.diffuseTexture = result;
  644. material.emissiveTexture = result;
  645. result.hasAlpha = true;
  646. } else {
  647. material.emissiveTexture = result;
  648. material.opacityTexture = result;
  649. }
  650. mesh.material = material;
  651. result.attachToMesh(mesh, supportPointerMove);
  652. return result;
  653. }
  654. /**
  655. * Creates a new AdvancedDynamicTexture in fullscreen mode.
  656. * In this mode the texture will rely on a layer for its rendering.
  657. * This allows it to be treated like any other layer.
  658. * As such, if you have a multi camera setup, you can set the layerMask on the GUI as well.
  659. * LayerMask is set through advancedTexture.layer.layerMask
  660. * @param name defines name for the texture
  661. * @param foreground defines a boolean indicating if the texture must be rendered in foreground (default is true)
  662. * @param scene defines the hsoting scene
  663. * @param sampling defines the texture sampling mode (Texture.BILINEAR_SAMPLINGMODE by default)
  664. * @returns a new AdvancedDynamicTexture
  665. */
  666. public static CreateFullscreenUI(name: string, foreground: boolean = true, scene: Nullable<Scene> = null, sampling = Texture.BILINEAR_SAMPLINGMODE): AdvancedDynamicTexture {
  667. var result = new AdvancedDynamicTexture(name, 0, 0, scene, false, sampling);
  668. // Display
  669. var layer = new Layer(name + "_layer", null, scene, !foreground);
  670. layer.texture = result;
  671. result._layerToDispose = layer;
  672. result._isFullscreen = true;
  673. // Attach
  674. result.attach();
  675. return result;
  676. }
  677. }