advancedDynamicTexture.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /// <reference path="../../dist/preview release/babylon.d.ts"/>
  2. module BABYLON.GUI {
  3. export interface IFocusableControl {
  4. onFocus(): void;
  5. onBlur(): void;
  6. processKeyboard(evt: KeyboardEvent): void;
  7. }
  8. export class AdvancedDynamicTexture extends DynamicTexture {
  9. private _isDirty = false;
  10. private _renderObserver: Observer<Camera>;
  11. private _resizeObserver: Observer<Engine>;
  12. private _preKeyboardObserver: Observer<KeyboardInfoPre>;
  13. private _pointerMoveObserver: Observer<PointerInfoPre>;
  14. private _pointerObserver: Observer<PointerInfo>;
  15. private _canvasPointerOutObserver: Observer<Engine>;
  16. private _background: string;
  17. public _rootContainer = new Container("root");
  18. public _lastPickedControl: Control;
  19. public _lastControlOver: Control;
  20. public _lastControlDown: Control;
  21. public _capturingControl: Control;
  22. public _shouldBlockPointer: boolean;
  23. public _layerToDispose: Layer;
  24. public _linkedControls = new Array<Control>();
  25. private _isFullscreen = false;
  26. private _fullscreenViewport = new Viewport(0, 0, 1, 1);
  27. private _idealWidth = 0;
  28. private _idealHeight = 0;
  29. private _renderAtIdealSize = false;
  30. private _focusedControl: IFocusableControl;
  31. public get background(): string {
  32. return this._background;
  33. }
  34. public set background(value: string) {
  35. if (this._background === value) {
  36. return;
  37. }
  38. this._background = value;
  39. this.markAsDirty();
  40. }
  41. public get idealWidth(): number {
  42. return this._idealWidth;
  43. }
  44. public set idealWidth(value: number) {
  45. if (this._idealWidth === value) {
  46. return;
  47. }
  48. this._idealWidth = value;
  49. this.markAsDirty();
  50. this._rootContainer._markAllAsDirty();
  51. }
  52. public get idealHeight(): number {
  53. return this._idealHeight;
  54. }
  55. public set idealHeight(value: number) {
  56. if (this._idealHeight === value) {
  57. return;
  58. }
  59. this._idealHeight = value;
  60. this.markAsDirty();
  61. this._rootContainer._markAllAsDirty();
  62. }
  63. public get renderAtIdealSize(): boolean {
  64. return this._renderAtIdealSize;
  65. }
  66. public set renderAtIdealSize(value: boolean) {
  67. if (this._renderAtIdealSize === value) {
  68. return;
  69. }
  70. this._renderAtIdealSize = value;
  71. this._onResize();
  72. }
  73. public get layer(): Layer {
  74. return this._layerToDispose;
  75. }
  76. public get rootContainer(): Container {
  77. return this._rootContainer;
  78. }
  79. public get focusedControl(): IFocusableControl {
  80. return this._focusedControl;
  81. }
  82. public set focusedControl(control: IFocusableControl) {
  83. if (this._focusedControl == control) {
  84. return;
  85. }
  86. if (!this._focusedControl) {
  87. control.onFocus();
  88. } else {
  89. this._focusedControl.onBlur();
  90. }
  91. this._focusedControl = control;
  92. }
  93. constructor(name: string, width = 0, height = 0, scene: Scene, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
  94. super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
  95. this._renderObserver = this.getScene().onBeforeCameraRenderObservable.add((camera: Camera) => this._checkUpdate(camera));
  96. this._preKeyboardObserver = this.getScene().onPreKeyboardObservable.add(info => {
  97. if (!this._focusedControl) {
  98. return;
  99. }
  100. if (info.type === KeyboardEventTypes.KEYDOWN) {
  101. this._focusedControl.processKeyboard(info.event);
  102. }
  103. info.skipOnPointerObservable = true;
  104. });
  105. this._rootContainer._link(null, this);
  106. this.hasAlpha = true;
  107. if (!width || !height) {
  108. this._resizeObserver = this.getScene().getEngine().onResizeObservable.add(() => this._onResize());
  109. this._onResize();
  110. }
  111. this._texture.isReady = true;
  112. }
  113. public executeOnAllControls(func: (control: Control) => void, container?: Container) {
  114. if (!container) {
  115. container = this._rootContainer;
  116. }
  117. for (var child of container.children) {
  118. if ((<any>child).children) {
  119. this.executeOnAllControls(func, (<Container>child));
  120. continue;
  121. }
  122. func(child);
  123. }
  124. }
  125. public markAsDirty() {
  126. this._isDirty = true;
  127. }
  128. public addControl(control: Control): AdvancedDynamicTexture {
  129. this._rootContainer.addControl(control);
  130. return this;
  131. }
  132. public removeControl(control: Control): AdvancedDynamicTexture {
  133. this._rootContainer.removeControl(control);
  134. return this;
  135. }
  136. public dispose() {
  137. this.getScene().onBeforeCameraRenderObservable.remove(this._renderObserver);
  138. if (this._resizeObserver) {
  139. this.getScene().getEngine().onResizeObservable.remove(this._resizeObserver);
  140. }
  141. if (this._pointerMoveObserver) {
  142. this.getScene().onPrePointerObservable.remove(this._pointerMoveObserver);
  143. }
  144. if (this._pointerObserver) {
  145. this.getScene().onPointerObservable.remove(this._pointerObserver);
  146. }
  147. if (this._canvasPointerOutObserver) {
  148. this.getScene().getEngine().onCanvasPointerOutObservable.remove(this._canvasPointerOutObserver);
  149. }
  150. if (this._layerToDispose) {
  151. this._layerToDispose.texture = null;
  152. this._layerToDispose.dispose();
  153. this._layerToDispose = null;
  154. }
  155. this._rootContainer.dispose();
  156. super.dispose();
  157. }
  158. private _onResize(): void {
  159. // Check size
  160. var engine = this.getScene().getEngine();
  161. var textureSize = this.getSize();
  162. var renderWidth = engine.getRenderWidth();
  163. var renderHeight = engine.getRenderHeight();
  164. if (this._renderAtIdealSize) {
  165. if (this._idealWidth) {
  166. renderHeight = (renderHeight * this._idealWidth) / renderWidth;
  167. renderWidth = this._idealWidth;
  168. } else if (this._idealHeight) {
  169. renderWidth = (renderWidth * this._idealHeight) / renderHeight;
  170. renderHeight = this._idealHeight;
  171. }
  172. }
  173. if (textureSize.width !== renderWidth || textureSize.height !== renderHeight) {
  174. this.scaleTo(renderWidth, renderHeight);
  175. this.markAsDirty();
  176. if (this._idealWidth || this._idealHeight) {
  177. this._rootContainer._markAllAsDirty();
  178. }
  179. }
  180. }
  181. public _getGlobalViewport(scene: Scene): Viewport {
  182. var engine = scene.getEngine();
  183. return this._fullscreenViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
  184. }
  185. private _checkUpdate(camera: Camera): void {
  186. if (this._layerToDispose) {
  187. if ((camera.layerMask & this._layerToDispose.layerMask) === 0) {
  188. return;
  189. }
  190. }
  191. if (this._isFullscreen && this._linkedControls.length) {
  192. var scene = this.getScene();
  193. var globalViewport = this._getGlobalViewport(scene);
  194. for (var control of this._linkedControls) {
  195. if (!control.isVisible) {
  196. continue;
  197. }
  198. var mesh = control._linkedMesh;
  199. if (!mesh || mesh.isDisposed()) {
  200. Tools.SetImmediate(()=>{
  201. control.linkWithMesh(null);
  202. });
  203. continue;
  204. }
  205. var position = mesh.getBoundingInfo().boundingSphere.center;
  206. var projectedPosition = Vector3.Project(position, mesh.getWorldMatrix(), scene.getTransformMatrix(), globalViewport);
  207. if (projectedPosition.z < 0 || projectedPosition.z > 1) {
  208. control.notRenderable = true;
  209. continue;
  210. }
  211. control.notRenderable = false;
  212. control._moveToProjectedPosition(projectedPosition);
  213. }
  214. }
  215. if (!this._isDirty && !this._rootContainer.isDirty) {
  216. return;
  217. }
  218. this._isDirty = false;
  219. this._render();
  220. this.update();
  221. }
  222. private _render(): void {
  223. var engine = this.getScene().getEngine();
  224. var textureSize = this.getSize();
  225. var renderWidth = textureSize.width;
  226. var renderHeight = textureSize.height;
  227. // Clear
  228. var context = this.getContext();
  229. context.clearRect(0, 0, renderWidth, renderHeight);
  230. if (this._background) {
  231. context.save();
  232. context.fillStyle = this._background;
  233. context.fillRect(0, 0, renderWidth, renderHeight);
  234. context.restore();
  235. }
  236. // Render
  237. context.font = "18px Arial";
  238. context.strokeStyle = "white";
  239. var measure = new Measure(0, 0, renderWidth, renderHeight);
  240. this._rootContainer._draw(measure, context);
  241. }
  242. private _doPicking(x: number, y: number, type: number): void {
  243. var scene = this.getScene();
  244. var engine = scene.getEngine();
  245. var textureSize = this.getSize();
  246. if (this._isFullscreen) {
  247. x = x * (textureSize.width / engine.getRenderWidth());
  248. y = y * (textureSize.height / engine.getRenderHeight());
  249. }
  250. if (this._capturingControl) {
  251. this._capturingControl._processObservables(type, x, y);
  252. return;
  253. }
  254. if (!this._rootContainer._processPicking(x, y, type)) {
  255. if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
  256. if (this._lastControlOver) {
  257. this._lastControlOver._onPointerOut();
  258. }
  259. this._lastControlOver = null;
  260. }
  261. }
  262. this._manageFocus();
  263. }
  264. public attach(): void {
  265. var scene = this.getScene();
  266. this._pointerMoveObserver = scene.onPrePointerObservable.add((pi, state) => {
  267. if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
  268. && pi.type !== BABYLON.PointerEventTypes.POINTERUP
  269. && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
  270. return;
  271. }
  272. let camera = scene.cameraToUseForPointers || scene.activeCamera;
  273. let engine = scene.getEngine();
  274. let viewport = camera.viewport;
  275. let x = (scene.pointerX / engine.getHardwareScalingLevel() - viewport.x * engine.getRenderWidth()) / viewport.width;
  276. let y = (scene.pointerY / engine.getHardwareScalingLevel() - viewport.y * engine.getRenderHeight()) / viewport.height;
  277. this._shouldBlockPointer = false;
  278. this._doPicking(x, y, pi.type);
  279. pi.skipOnPointerObservable = this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
  280. });
  281. this._attachToOnPointerOut(scene);
  282. }
  283. public attachToMesh(mesh: AbstractMesh, supportPointerMove = true): void {
  284. var scene = this.getScene();
  285. this._pointerObserver = scene.onPointerObservable.add((pi, state) => {
  286. if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
  287. && pi.type !== BABYLON.PointerEventTypes.POINTERUP
  288. && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
  289. return;
  290. }
  291. if (pi.pickInfo.hit && pi.pickInfo.pickedMesh === mesh) {
  292. var uv = pi.pickInfo.getTextureCoordinates();
  293. var size = this.getSize();
  294. this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type);
  295. } else if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
  296. if (this._lastControlDown) {
  297. this._lastControlDown.forcePointerUp();
  298. }
  299. this._lastControlDown = null;
  300. this.focusedControl = null;
  301. } else if (pi.type === BABYLON.PointerEventTypes.POINTERMOVE) {
  302. if (this._lastControlOver) {
  303. this._lastControlOver._onPointerOut();
  304. }
  305. this._lastControlOver = null;
  306. }
  307. });
  308. mesh.enablePointerMoveEvents = supportPointerMove;
  309. this._attachToOnPointerOut(scene);
  310. }
  311. private _manageFocus(): void {
  312. // Focus management
  313. if (this._focusedControl) {
  314. if (this._focusedControl !== (<any>this._lastPickedControl)) {
  315. if (this._lastPickedControl.isFocusInvisible) {
  316. return;
  317. }
  318. this.focusedControl = null;
  319. }
  320. }
  321. }
  322. private _attachToOnPointerOut(scene: Scene): void {
  323. this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add(() => {
  324. if (this._lastControlOver) {
  325. this._lastControlOver._onPointerOut();
  326. }
  327. this._lastControlOver = null;
  328. if (this._lastControlDown) {
  329. this._lastControlDown.forcePointerUp();
  330. }
  331. this._lastControlDown = null;
  332. });
  333. }
  334. // Statics
  335. public static CreateForMesh(mesh: AbstractMesh, width = 1024, height = 1024, supportPointerMove = true): AdvancedDynamicTexture {
  336. var result = new AdvancedDynamicTexture(mesh.name + " AdvancedDynamicTexture", width, height, mesh.getScene(), true, Texture.TRILINEAR_SAMPLINGMODE);
  337. var material = new BABYLON.StandardMaterial("AdvancedDynamicTextureMaterial", mesh.getScene());
  338. material.backFaceCulling = false;
  339. material.diffuseColor = BABYLON.Color3.Black();
  340. material.specularColor = BABYLON.Color3.Black();
  341. material.emissiveTexture = result;
  342. material.opacityTexture = result;
  343. mesh.material = material;
  344. result.attachToMesh(mesh, supportPointerMove);
  345. return result;
  346. }
  347. public static CreateFullscreenUI(name: string, foreground: boolean = true, scene: Scene = null): AdvancedDynamicTexture {
  348. var result = new AdvancedDynamicTexture(name, 0, 0, scene);
  349. // Display
  350. var layer = new BABYLON.Layer(name + "_layer", null, scene, !foreground);
  351. layer.texture = result;
  352. result._layerToDispose = layer;
  353. result._isFullscreen = true;
  354. // Attach
  355. result.attach();
  356. return result;
  357. }
  358. }
  359. }