advancedDynamicTexture.ts 26 KB

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