babylon.canvas2d.js 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508
  1. var __extends = (this && this.__extends) || function (d, b) {
  2. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  3. function __() { this.constructor = d; }
  4. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5. };
  6. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  7. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  8. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  9. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  10. return c > 3 && r && Object.defineProperty(target, key, r), r;
  11. };
  12. var BABYLON;
  13. (function (BABYLON) {
  14. // This class contains data that lifetime is bounding to the Babylon Engine object
  15. var Canvas2DEngineBoundData = (function () {
  16. function Canvas2DEngineBoundData() {
  17. this._modelCache = new BABYLON.StringDictionary();
  18. }
  19. Canvas2DEngineBoundData.prototype.GetOrAddModelCache = function (key, factory) {
  20. return this._modelCache.getOrAddWithFactory(key, factory);
  21. };
  22. Canvas2DEngineBoundData.prototype.DisposeModelRenderCache = function (modelRenderCache) {
  23. if (!modelRenderCache.isDisposed) {
  24. return false;
  25. }
  26. this._modelCache.remove(modelRenderCache.modelKey);
  27. return true;
  28. };
  29. return Canvas2DEngineBoundData;
  30. })();
  31. BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
  32. var Canvas2D = (function (_super) {
  33. __extends(Canvas2D, _super);
  34. function Canvas2D(scene, settings) {
  35. var _this = this;
  36. _super.call(this, settings);
  37. /**
  38. * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
  39. * Beware that you have to take under consideration the origin in your calculations! Good luck!
  40. */
  41. this.worldSpaceToNodeLocal = function (worldPos) {
  42. var node = _this._worldSpaceNode;
  43. if (!node) {
  44. return;
  45. }
  46. var mtx = node.getWorldMatrix().clone();
  47. mtx.invert();
  48. var v = BABYLON.Vector3.TransformCoordinates(worldPos, mtx);
  49. var res = new BABYLON.Vector2(v.x, v.y);
  50. var size = _this.actualSize;
  51. res.x += size.width * 0.5; // res is centered, make it relative to bottom/left
  52. res.y += size.height * 0.5;
  53. return res;
  54. };
  55. /**
  56. * If you use a custom WorldSpaceCanvasNode you have to override this property to update the UV of your object to reflect the changes due to a resizing of the cached bitmap
  57. */
  58. this.worldSpaceCacheChanged = function () {
  59. var plane = _this.worldSpaceCanvasNode;
  60. var vd = BABYLON.VertexData.ExtractFromMesh(plane); //new VertexData();
  61. vd.uvs = new Float32Array(8);
  62. var material = plane.material;
  63. var tex = _this._renderableData._cacheTexture;
  64. if (material.diffuseTexture !== tex) {
  65. material.diffuseTexture = tex;
  66. tex.hasAlpha = true;
  67. }
  68. var nodeuv = _this._renderableData._cacheNodeUVs;
  69. for (var i = 0; i < 4; i++) {
  70. vd.uvs[i * 2 + 0] = nodeuv[i].x;
  71. vd.uvs[i * 2 + 1] = nodeuv[i].y;
  72. }
  73. vd.applyToMesh(plane);
  74. };
  75. this._notifDebugMode = false;
  76. this._mapCounter = 0;
  77. this._drawCallsOpaqueCounter = new BABYLON.PerfCounter();
  78. this._drawCallsAlphaTestCounter = new BABYLON.PerfCounter();
  79. this._drawCallsTransparentCounter = new BABYLON.PerfCounter();
  80. this._groupRenderCounter = new BABYLON.PerfCounter();
  81. this._updateTransparentDataCounter = new BABYLON.PerfCounter();
  82. this._cachedGroupRenderCounter = new BABYLON.PerfCounter();
  83. this._updateCachedStateCounter = new BABYLON.PerfCounter();
  84. this._updateLayoutCounter = new BABYLON.PerfCounter();
  85. this._updatePositioningCounter = new BABYLON.PerfCounter();
  86. this._updateLocalTransformCounter = new BABYLON.PerfCounter();
  87. this._updateGlobalTransformCounter = new BABYLON.PerfCounter();
  88. this._boundingInfoRecomputeCounter = new BABYLON.PerfCounter();
  89. this._cachedCanvasGroup = null;
  90. this._profileInfoText = null;
  91. BABYLON.Prim2DBase._isCanvasInit = false;
  92. if (!settings) {
  93. settings = {};
  94. }
  95. if (this._cachingStrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
  96. this._background = new BABYLON.Rectangle2D({ parent: this, id: "###CANVAS BACKGROUND###", size: settings.size }); //TODO CHECK when size is null
  97. this._background.zOrder = 1.0;
  98. this._background.isPickable = false;
  99. this._background.origin = BABYLON.Vector2.Zero();
  100. this._background.levelVisible = false;
  101. if (settings.backgroundRoundRadius != null) {
  102. this.backgroundRoundRadius = settings.backgroundRoundRadius;
  103. }
  104. if (settings.backgroundBorder != null) {
  105. if (typeof (settings.backgroundBorder) === "string") {
  106. this.backgroundBorder = Canvas2D.GetBrushFromString(settings.backgroundBorder);
  107. }
  108. else {
  109. this.backgroundBorder = settings.backgroundBorder;
  110. }
  111. }
  112. if (settings.backgroundBorderThickNess != null) {
  113. this.backgroundBorderThickness = settings.backgroundBorderThickNess;
  114. }
  115. if (settings.backgroundFill != null) {
  116. if (typeof (settings.backgroundFill) === "string") {
  117. this.backgroundFill = Canvas2D.GetBrushFromString(settings.backgroundFill);
  118. }
  119. else {
  120. this.backgroundFill = settings.backgroundFill;
  121. }
  122. }
  123. // Put a handler to resize the background whenever the canvas is resizing
  124. this.propertyChanged.add(function (e, s) {
  125. _this._background.size = _this.size;
  126. }, BABYLON.Group2D.sizeProperty.flagId);
  127. this._background._patchHierarchy(this);
  128. }
  129. var engine = scene.getEngine();
  130. this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
  131. this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
  132. this._capturedPointers = new BABYLON.StringDictionary();
  133. this._pickStartingPosition = BABYLON.Vector2.Zero();
  134. this._hierarchyLevelMaxSiblingCount = 50;
  135. this._hierarchyDepth = 0;
  136. this._zOrder = 0;
  137. this._zMax = 1;
  138. this._scene = scene;
  139. this._engine = engine;
  140. this._renderingSize = new BABYLON.Size(0, 0);
  141. this._designSize = settings.designSize || null;
  142. this._designUseHorizAxis = settings.designUseHorizAxis === true;
  143. this._trackedGroups = new Array();
  144. this._maxAdaptiveWorldSpaceCanvasSize = null;
  145. this._groupCacheMaps = new BABYLON.StringDictionary();
  146. this._patchHierarchy(this);
  147. var enableInteraction = (settings.enableInteraction == null) ? true : settings.enableInteraction;
  148. this._fitRenderingDevice = !settings.size;
  149. if (!settings.size) {
  150. settings.size = new BABYLON.Size(engine.getRenderWidth(), engine.getRenderHeight());
  151. }
  152. // Register scene dispose to also dispose the canvas when it'll happens
  153. scene.onDisposeObservable.add(function (d, s) {
  154. _this.dispose();
  155. });
  156. if (this._isScreenSpace) {
  157. this._afterRenderObserver = this._scene.onAfterRenderObservable.add(function (d, s) {
  158. _this._engine.clear(null, false, true, true);
  159. _this._render();
  160. });
  161. }
  162. else {
  163. this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add(function (d, s) {
  164. _this._render();
  165. });
  166. }
  167. this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
  168. //this._supprtInstancedArray = false; // TODO REMOVE!!!
  169. this._setupInteraction(enableInteraction);
  170. }
  171. Object.defineProperty(Canvas2D.prototype, "drawCallsOpaqueCounter", {
  172. get: function () {
  173. return this._drawCallsOpaqueCounter;
  174. },
  175. enumerable: true,
  176. configurable: true
  177. });
  178. Object.defineProperty(Canvas2D.prototype, "drawCallsAlphaTestCounter", {
  179. get: function () {
  180. return this._drawCallsAlphaTestCounter;
  181. },
  182. enumerable: true,
  183. configurable: true
  184. });
  185. Object.defineProperty(Canvas2D.prototype, "drawCallsTransparentCounter", {
  186. get: function () {
  187. return this._drawCallsTransparentCounter;
  188. },
  189. enumerable: true,
  190. configurable: true
  191. });
  192. Object.defineProperty(Canvas2D.prototype, "groupRenderCounter", {
  193. get: function () {
  194. return this._groupRenderCounter;
  195. },
  196. enumerable: true,
  197. configurable: true
  198. });
  199. Object.defineProperty(Canvas2D.prototype, "updateTransparentDataCounter", {
  200. get: function () {
  201. return this._updateTransparentDataCounter;
  202. },
  203. enumerable: true,
  204. configurable: true
  205. });
  206. Object.defineProperty(Canvas2D.prototype, "cachedGroupRenderCounter", {
  207. get: function () {
  208. return this._cachedGroupRenderCounter;
  209. },
  210. enumerable: true,
  211. configurable: true
  212. });
  213. Object.defineProperty(Canvas2D.prototype, "updateCachedStateCounter", {
  214. get: function () {
  215. return this._updateCachedStateCounter;
  216. },
  217. enumerable: true,
  218. configurable: true
  219. });
  220. Object.defineProperty(Canvas2D.prototype, "updateLayoutCounter", {
  221. get: function () {
  222. return this._updateLayoutCounter;
  223. },
  224. enumerable: true,
  225. configurable: true
  226. });
  227. Object.defineProperty(Canvas2D.prototype, "updatePositioningCounter", {
  228. get: function () {
  229. return this._updatePositioningCounter;
  230. },
  231. enumerable: true,
  232. configurable: true
  233. });
  234. Object.defineProperty(Canvas2D.prototype, "updateLocalTransformCounter", {
  235. get: function () {
  236. return this._updateLocalTransformCounter;
  237. },
  238. enumerable: true,
  239. configurable: true
  240. });
  241. Object.defineProperty(Canvas2D.prototype, "updateGlobalTransformCounter", {
  242. get: function () {
  243. return this._updateGlobalTransformCounter;
  244. },
  245. enumerable: true,
  246. configurable: true
  247. });
  248. Object.defineProperty(Canvas2D.prototype, "boundingInfoRecomputeCounter", {
  249. get: function () {
  250. return this._boundingInfoRecomputeCounter;
  251. },
  252. enumerable: true,
  253. configurable: true
  254. });
  255. Canvas2D.prototype._canvasPreInit = function (settings) {
  256. var cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_DONTCACHE : settings.cachingStrategy;
  257. this._cachingStrategy = cachingStrategy;
  258. this._isScreenSpace = (settings.isScreenSpace == null) ? true : settings.isScreenSpace;
  259. };
  260. Canvas2D.prototype._setupInteraction = function (enable) {
  261. var _this = this;
  262. // No change detection
  263. if (enable === this._interactionEnabled) {
  264. return;
  265. }
  266. // Set the new state
  267. this._interactionEnabled = enable;
  268. // ScreenSpace mode
  269. if (this._isScreenSpace) {
  270. // Disable interaction
  271. if (!enable) {
  272. if (this._scenePrePointerObserver) {
  273. this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
  274. this._scenePrePointerObserver = null;
  275. }
  276. return;
  277. }
  278. // Enable Interaction
  279. // Register the observable
  280. this._scenePrePointerObserver = this.scene.onPrePointerObservable.add(function (e, s) {
  281. if (_this.isVisible === false) {
  282. return;
  283. }
  284. var hs = 1 / _this.engine.getHardwareScalingLevel();
  285. var localPos = e.localPosition.multiplyByFloats(hs, hs);
  286. _this._handlePointerEventForInteraction(e, localPos, s);
  287. });
  288. }
  289. else {
  290. var scene = this.scene;
  291. if (enable) {
  292. scene.constantlyUpdateMeshUnderPointer = true;
  293. this._scenePointerObserver = scene.onPointerObservable.add(function (e, s) {
  294. if (_this.isVisible === false) {
  295. return;
  296. }
  297. if (e.pickInfo.hit && e.pickInfo.pickedMesh === _this._worldSpaceNode && _this.worldSpaceToNodeLocal) {
  298. var localPos = _this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
  299. _this._handlePointerEventForInteraction(e, localPos, s);
  300. }
  301. });
  302. }
  303. else {
  304. if (this._scenePointerObserver) {
  305. this.scene.onPointerObservable.remove(this._scenePointerObserver);
  306. this._scenePointerObserver = null;
  307. }
  308. }
  309. }
  310. };
  311. /**
  312. * Internal method, you should use the Prim2DBase version instead
  313. */
  314. Canvas2D.prototype._setPointerCapture = function (pointerId, primitive) {
  315. if (this.isPointerCaptured(pointerId)) {
  316. return false;
  317. }
  318. // Try to capture the pointer on the HTML side
  319. try {
  320. this.engine.getRenderingCanvas().setPointerCapture(pointerId);
  321. }
  322. catch (e) {
  323. }
  324. this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
  325. this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerGotCapture, null);
  326. this._capturedPointers.add(pointerId.toString(), primitive);
  327. return true;
  328. };
  329. /**
  330. * Internal method, you should use the Prim2DBase version instead
  331. */
  332. Canvas2D.prototype._releasePointerCapture = function (pointerId, primitive) {
  333. if (this._capturedPointers.get(pointerId.toString()) !== primitive) {
  334. return false;
  335. }
  336. // Try to release the pointer on the HTML side
  337. try {
  338. this.engine.getRenderingCanvas().releasePointerCapture(pointerId);
  339. }
  340. catch (e) {
  341. }
  342. this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
  343. this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerLostCapture, null);
  344. this._capturedPointers.remove(pointerId.toString());
  345. return true;
  346. };
  347. /**
  348. * Determine if the given pointer is captured or not
  349. * @param pointerId the Id of the pointer
  350. * @return true if it's captured, false otherwise
  351. */
  352. Canvas2D.prototype.isPointerCaptured = function (pointerId) {
  353. return this._capturedPointers.contains(pointerId.toString());
  354. };
  355. Canvas2D.prototype.getCapturedPrimitive = function (pointerId) {
  356. // Avoid unnecessary lookup
  357. if (this._capturedPointers.count === 0) {
  358. return null;
  359. }
  360. return this._capturedPointers.get(pointerId.toString());
  361. };
  362. Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, localPosition, eventState) {
  363. // Dispose check
  364. if (this.isDisposed) {
  365. return;
  366. }
  367. // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
  368. this._updatePointerInfo(eventData, localPosition);
  369. var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
  370. // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
  371. // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
  372. this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, capturedPrim !== null);
  373. // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
  374. this._updateOverStatus();
  375. // Check if we have nothing to raise
  376. if (!this._actualOverPrimitive && !capturedPrim) {
  377. return;
  378. }
  379. // Update the relatedTarget info with the over primitive or the captured one (if any)
  380. var targetPrim = capturedPrim || this._actualOverPrimitive.prim;
  381. var targetPointerPos = capturedPrim ? this._primPointerInfo.canvasPointerPos.subtract(new BABYLON.Vector2(targetPrim.globalTransform.m[12], targetPrim.globalTransform.m[13])) : this._actualOverPrimitive.intersectionLocation;
  382. this._primPointerInfo.updateRelatedTarget(targetPrim, targetPointerPos);
  383. // Analyze the pointer event type and fire proper events on the primitive
  384. var skip = false;
  385. if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
  386. skip = !this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMouseWheel, eventData);
  387. }
  388. else if (eventData.type === BABYLON.PointerEventTypes.POINTERMOVE) {
  389. skip = !this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMove, eventData);
  390. }
  391. else if (eventData.type === BABYLON.PointerEventTypes.POINTERDOWN) {
  392. skip = !this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerDown, eventData);
  393. }
  394. else if (eventData.type === BABYLON.PointerEventTypes.POINTERUP) {
  395. skip = !this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData);
  396. }
  397. eventState.skipNextObservers = skip;
  398. };
  399. Canvas2D.prototype._updatePointerInfo = function (eventData, localPosition) {
  400. var pii = this._primPointerInfo;
  401. if (!pii.canvasPointerPos) {
  402. pii.canvasPointerPos = BABYLON.Vector2.Zero();
  403. }
  404. var camera = this._scene.cameraToUseForPointers || this._scene.activeCamera;
  405. var engine = this._scene.getEngine();
  406. if (this._isScreenSpace) {
  407. var cameraViewport = camera.viewport;
  408. var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
  409. // Moving coordinates to local viewport world
  410. var x = localPosition.x - viewport.x;
  411. var y = localPosition.y - viewport.y;
  412. pii.canvasPointerPos.x = x - this.actualPosition.x;
  413. pii.canvasPointerPos.y = engine.getRenderHeight() - y - this.actualPosition.y;
  414. }
  415. else {
  416. pii.canvasPointerPos.x = localPosition.x;
  417. pii.canvasPointerPos.y = localPosition.y;
  418. }
  419. //console.log(`UpdatePointerInfo for ${this.id}, X:${pii.canvasPointerPos.x}, Y:${pii.canvasPointerPos.y}`);
  420. pii.mouseWheelDelta = 0;
  421. if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
  422. var event = eventData.event;
  423. if (event.wheelDelta) {
  424. pii.mouseWheelDelta = event.wheelDelta / (BABYLON.PrimitivePointerInfo.MouseWheelPrecision * 40);
  425. }
  426. else if (event.detail) {
  427. pii.mouseWheelDelta = -event.detail / BABYLON.PrimitivePointerInfo.MouseWheelPrecision;
  428. }
  429. }
  430. else {
  431. var pe = eventData.event;
  432. pii.ctrlKey = pe.ctrlKey;
  433. pii.altKey = pe.altKey;
  434. pii.shiftKey = pe.shiftKey;
  435. pii.metaKey = pe.metaKey;
  436. pii.button = pe.button;
  437. pii.buttons = pe.buttons;
  438. pii.pointerId = pe.pointerId;
  439. pii.width = pe.width;
  440. pii.height = pe.height;
  441. pii.presssure = pe.pressure;
  442. pii.tilt.x = pe.tiltX;
  443. pii.tilt.y = pe.tiltY;
  444. pii.isCaptured = this.getCapturedPrimitive(pe.pointerId) !== null;
  445. }
  446. };
  447. Canvas2D.prototype._updateIntersectionList = function (mouseLocalPos, isCapture) {
  448. if (this.scene.getRenderId() === this._intersectionRenderId) {
  449. return;
  450. }
  451. // A little safe guard, it might happens than the event is triggered before the first render and nothing is computed, this simple check will make sure everything will be fine
  452. if (!this._globalTransform) {
  453. this.updateCachedStates(true);
  454. }
  455. var ii = Canvas2D._interInfo;
  456. ii.pickPosition.x = mouseLocalPos.x;
  457. ii.pickPosition.y = mouseLocalPos.y;
  458. ii.findFirstOnly = false;
  459. // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
  460. if (!isCapture && !this.levelBoundingInfo.doesIntersect(ii.pickPosition)) {
  461. this._previousIntersectionList = this._actualIntersectionList;
  462. this._actualIntersectionList = null;
  463. this._previousOverPrimitive = this._actualOverPrimitive;
  464. this._actualOverPrimitive = null;
  465. return;
  466. }
  467. this.intersect(ii);
  468. this._previousIntersectionList = this._actualIntersectionList;
  469. this._actualIntersectionList = ii.intersectedPrimitives;
  470. this._previousOverPrimitive = this._actualOverPrimitive;
  471. this._actualOverPrimitive = ii.topMostIntersectedPrimitive;
  472. this._intersectionRenderId = this.scene.getRenderId();
  473. };
  474. // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
  475. Canvas2D.prototype._updateOverStatus = function () {
  476. if ((this.scene.getRenderId() === this._hoverStatusRenderId) || !this._previousIntersectionList || !this._actualIntersectionList) {
  477. return;
  478. }
  479. // Detect a change of over
  480. var prevPrim = this._previousOverPrimitive ? this._previousOverPrimitive.prim : null;
  481. var actualPrim = this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
  482. if (prevPrim !== actualPrim) {
  483. // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
  484. var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
  485. // Notify the previous "over" prim that the pointer is no longer over it
  486. if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim)) {
  487. this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
  488. this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
  489. }
  490. // Notify the new "over" prim that the pointer is over it
  491. if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
  492. this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
  493. this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver, null);
  494. }
  495. }
  496. this._hoverStatusRenderId = this.scene.getRenderId();
  497. };
  498. Canvas2D.prototype._updatePrimPointerPos = function (prim) {
  499. if (this._primPointerInfo.isCaptured) {
  500. this._primPointerInfo.primitivePointerPos = this._primPointerInfo.relatedTargetPointerPos;
  501. }
  502. else {
  503. for (var _i = 0, _a = this._actualIntersectionList; _i < _a.length; _i++) {
  504. var pii = _a[_i];
  505. if (pii.prim === prim) {
  506. this._primPointerInfo.primitivePointerPos = pii.intersectionLocation;
  507. return;
  508. }
  509. }
  510. }
  511. };
  512. Canvas2D.prototype._debugExecObserver = function (prim, mask) {
  513. if (!this._notifDebugMode) {
  514. return;
  515. }
  516. var debug = "";
  517. for (var i = 0; i < prim.hierarchyDepth; i++) {
  518. debug += " ";
  519. }
  520. var pii = this._primPointerInfo;
  521. debug += "[RID:" + this.scene.getRenderId() + "] [" + prim.hierarchyDepth + "] event:" + BABYLON.PrimitivePointerInfo.getEventTypeName(mask) + ", id: " + prim.id + " (" + BABYLON.Tools.getClassName(prim) + "), primPos: " + pii.primitivePointerPos.toString() + ", canvasPos: " + pii.canvasPointerPos.toString();
  522. console.log(debug);
  523. };
  524. Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask, eventData) {
  525. var ppi = this._primPointerInfo;
  526. var event = eventData ? eventData.event : null;
  527. // In case of PointerOver/Out we will first notify the parent with PointerEnter/Leave
  528. if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) !== 0) {
  529. this._notifParents(prim, mask);
  530. }
  531. var bubbleCancelled = false;
  532. var cur = prim;
  533. while (cur) {
  534. // Only trigger the observers if the primitive is intersected (except for out)
  535. if (!bubbleCancelled) {
  536. this._updatePrimPointerPos(cur);
  537. // Exec the observers
  538. this._debugExecObserver(cur, mask);
  539. if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof BABYLON.PointerInfoPre) {
  540. eventData.skipOnPointerObservable = true;
  541. return false;
  542. }
  543. this._triggerActionManager(cur, ppi, mask, event);
  544. // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
  545. // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
  546. if (ppi.cancelBubble) {
  547. if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) === 0) {
  548. return false;
  549. }
  550. // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
  551. bubbleCancelled = true;
  552. }
  553. }
  554. // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
  555. if (bubbleCancelled) {
  556. this._updatePrimPointerPos(cur);
  557. }
  558. // Trigger a PointerEnter corresponding to the PointerOver
  559. if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
  560. this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerEnter);
  561. cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerEnter);
  562. }
  563. else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
  564. this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerLeave);
  565. cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerLeave);
  566. }
  567. // Loop to the parent
  568. cur = cur.parent;
  569. }
  570. return true;
  571. };
  572. Canvas2D.prototype._triggerActionManager = function (prim, ppi, mask, eventData) {
  573. var _this = this;
  574. // A little safe guard, it might happens than the event is triggered before the first render and nothing is computed, this simple check will make sure everything will be fine
  575. if (!this._globalTransform) {
  576. this.updateCachedStates(true);
  577. }
  578. // Process Trigger related to PointerDown
  579. if ((mask & BABYLON.PrimitivePointerInfo.PointerDown) !== 0) {
  580. // On pointer down, record the current position and time to be able to trick PickTrigger and LongPressTrigger
  581. this._pickStartingPosition = ppi.primitivePointerPos.clone();
  582. this._pickStartingTime = new Date().getTime();
  583. this._pickedDownPrim = null;
  584. if (prim.actionManager) {
  585. this._pickedDownPrim = prim;
  586. if (prim.actionManager.hasPickTriggers) {
  587. var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
  588. switch (eventData.button) {
  589. case 0:
  590. prim.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
  591. break;
  592. case 1:
  593. prim.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
  594. break;
  595. case 2:
  596. prim.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
  597. break;
  598. }
  599. prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, actionEvent);
  600. }
  601. if (prim.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger)) {
  602. window.setTimeout(function () {
  603. var ppi = _this._primPointerInfo;
  604. var capturedPrim = _this.getCapturedPrimitive(ppi.pointerId);
  605. _this._updateIntersectionList(ppi.canvasPointerPos, capturedPrim !== null);
  606. var ii = new BABYLON.IntersectInfo2D();
  607. ii.pickPosition = ppi.canvasPointerPos.clone();
  608. ii.findFirstOnly = false;
  609. _this.intersect(ii);
  610. if (ii.isPrimIntersected(prim) !== null) {
  611. if (prim.actionManager) {
  612. if (_this._pickStartingTime !== 0 && ((new Date().getTime() - _this._pickStartingTime) > BABYLON.ActionManager.LongPressDelay) && (Math.abs(_this._pickStartingPosition.x - ii.pickPosition.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._pickStartingPosition.y - ii.pickPosition.y) < BABYLON.ActionManager.DragMovementThreshold)) {
  613. _this._pickStartingTime = 0;
  614. prim.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
  615. }
  616. }
  617. }
  618. }, BABYLON.ActionManager.LongPressDelay);
  619. }
  620. }
  621. }
  622. else if ((mask & BABYLON.PrimitivePointerInfo.PointerUp) !== 0) {
  623. this._pickStartingTime = 0;
  624. var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
  625. if (prim.actionManager) {
  626. // OnPickUpTrigger
  627. prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, actionEvent);
  628. // OnPickTrigger
  629. if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < BABYLON.ActionManager.DragMovementThreshold) {
  630. prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
  631. }
  632. }
  633. // OnPickOutTrigger
  634. if (this._pickedDownPrim && this._pickedDownPrim.actionManager && (this._pickedDownPrim !== prim)) {
  635. this._pickedDownPrim.actionManager.processTrigger(BABYLON.ActionManager.OnPickOutTrigger, actionEvent);
  636. }
  637. }
  638. else if ((mask & BABYLON.PrimitivePointerInfo.PointerOver) !== 0) {
  639. if (prim.actionManager) {
  640. var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
  641. prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, actionEvent);
  642. }
  643. }
  644. else if ((mask & BABYLON.PrimitivePointerInfo.PointerOut) !== 0) {
  645. if (prim.actionManager) {
  646. var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
  647. prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, actionEvent);
  648. }
  649. }
  650. };
  651. Canvas2D.prototype._notifParents = function (prim, mask) {
  652. var pii = this._primPointerInfo;
  653. var curPrim = this;
  654. while (curPrim) {
  655. this._updatePrimPointerPos(curPrim);
  656. // Fire the proper notification
  657. if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
  658. this._debugExecObserver(curPrim, BABYLON.PrimitivePointerInfo.PointerEnter);
  659. curPrim._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerEnter);
  660. }
  661. else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
  662. this._debugExecObserver(curPrim, BABYLON.PrimitivePointerInfo.PointerLeave);
  663. curPrim._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerLeave);
  664. }
  665. curPrim = curPrim.parent;
  666. }
  667. };
  668. /**
  669. * Don't forget to call the dispose method when you're done with the Canvas instance.
  670. * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
  671. */
  672. Canvas2D.prototype.dispose = function () {
  673. if (!_super.prototype.dispose.call(this)) {
  674. return false;
  675. }
  676. if (this._profilingCanvas) {
  677. this._profilingCanvas.dispose();
  678. this._profilingCanvas = null;
  679. }
  680. if (this.interactionEnabled) {
  681. this._setupInteraction(false);
  682. }
  683. if (this._beforeRenderObserver) {
  684. this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
  685. this._beforeRenderObserver = null;
  686. }
  687. if (this._afterRenderObserver) {
  688. this._scene.onAfterRenderObservable.remove(this._afterRenderObserver);
  689. this._afterRenderObserver = null;
  690. }
  691. if (this._groupCacheMaps) {
  692. this._groupCacheMaps.forEach(function (k, m) { return m.forEach(function (e) { return e.dispose(); }); });
  693. this._groupCacheMaps = null;
  694. }
  695. };
  696. Object.defineProperty(Canvas2D.prototype, "scene", {
  697. /**
  698. * Accessor to the Scene that owns the Canvas
  699. * @returns The instance of the Scene object
  700. */
  701. get: function () {
  702. return this._scene;
  703. },
  704. enumerable: true,
  705. configurable: true
  706. });
  707. Object.defineProperty(Canvas2D.prototype, "engine", {
  708. /**
  709. * Accessor to the Engine that drives the Scene used by this Canvas
  710. * @returns The instance of the Engine object
  711. */
  712. get: function () {
  713. return this._engine;
  714. },
  715. enumerable: true,
  716. configurable: true
  717. });
  718. Object.defineProperty(Canvas2D.prototype, "cachingStrategy", {
  719. /**
  720. * Accessor of the Caching Strategy used by this Canvas.
  721. * See Canvas2D.CACHESTRATEGY_xxxx static members for more information
  722. * @returns the value corresponding to the used strategy.
  723. */
  724. get: function () {
  725. return this._cachingStrategy;
  726. },
  727. enumerable: true,
  728. configurable: true
  729. });
  730. Object.defineProperty(Canvas2D.prototype, "isScreenSpace", {
  731. /**
  732. * Return true if the Canvas is a Screen Space one, false if it's a World Space one.
  733. * @returns {}
  734. */
  735. get: function () {
  736. return this._isScreenSpace;
  737. },
  738. enumerable: true,
  739. configurable: true
  740. });
  741. Object.defineProperty(Canvas2D.prototype, "worldSpaceCanvasNode", {
  742. /**
  743. * Only valid for World Space Canvas, returns the scene node that displays the canvas
  744. */
  745. get: function () {
  746. return this._worldSpaceNode;
  747. },
  748. set: function (val) {
  749. this._worldSpaceNode = val;
  750. },
  751. enumerable: true,
  752. configurable: true
  753. });
  754. Object.defineProperty(Canvas2D.prototype, "supportInstancedArray", {
  755. /**
  756. * Check if the WebGL Instanced Array extension is supported or not
  757. */
  758. get: function () {
  759. return this._supprtInstancedArray;
  760. },
  761. enumerable: true,
  762. configurable: true
  763. });
  764. Object.defineProperty(Canvas2D.prototype, "backgroundFill", {
  765. /**
  766. * Property that defines the fill object used to draw the background of the Canvas.
  767. * Note that Canvas with a Caching Strategy of
  768. * @returns If the background is not set, null will be returned, otherwise a valid fill object is returned.
  769. */
  770. get: function () {
  771. if (!this._background || !this._background.isVisible) {
  772. return null;
  773. }
  774. return this._background.fill;
  775. },
  776. set: function (value) {
  777. this.checkBackgroundAvailability();
  778. if (value === this._background.fill) {
  779. return;
  780. }
  781. this._background.fill = value;
  782. this._background.levelVisible = true;
  783. },
  784. enumerable: true,
  785. configurable: true
  786. });
  787. Object.defineProperty(Canvas2D.prototype, "backgroundBorder", {
  788. /**
  789. * Property that defines the border object used to draw the background of the Canvas.
  790. * @returns If the background is not set, null will be returned, otherwise a valid border object is returned.
  791. */
  792. get: function () {
  793. if (!this._background || !this._background.isVisible) {
  794. return null;
  795. }
  796. return this._background.border;
  797. },
  798. set: function (value) {
  799. this.checkBackgroundAvailability();
  800. if (value === this._background.border) {
  801. return;
  802. }
  803. this._background.border = value;
  804. this._background.levelVisible = true;
  805. },
  806. enumerable: true,
  807. configurable: true
  808. });
  809. Object.defineProperty(Canvas2D.prototype, "backgroundBorderThickness", {
  810. /**
  811. * Property that defines the thickness of the border object used to draw the background of the Canvas.
  812. * @returns If the background is not set, null will be returned, otherwise a valid number matching the thickness is returned.
  813. */
  814. get: function () {
  815. if (!this._background || !this._background.isVisible) {
  816. return null;
  817. }
  818. return this._background.borderThickness;
  819. },
  820. set: function (value) {
  821. this.checkBackgroundAvailability();
  822. if (value === this._background.borderThickness) {
  823. return;
  824. }
  825. this._background.borderThickness = value;
  826. },
  827. enumerable: true,
  828. configurable: true
  829. });
  830. Object.defineProperty(Canvas2D.prototype, "backgroundRoundRadius", {
  831. /**
  832. * You can set the roundRadius of the background
  833. * @returns The current roundRadius
  834. */
  835. get: function () {
  836. if (!this._background || !this._background.isVisible) {
  837. return null;
  838. }
  839. return this._background.roundRadius;
  840. },
  841. set: function (value) {
  842. this.checkBackgroundAvailability();
  843. if (value === this._background.roundRadius) {
  844. return;
  845. }
  846. this._background.roundRadius = value;
  847. this._background.levelVisible = true;
  848. },
  849. enumerable: true,
  850. configurable: true
  851. });
  852. Object.defineProperty(Canvas2D.prototype, "interactionEnabled", {
  853. /**
  854. * Enable/Disable interaction for this Canvas
  855. * When enabled the Prim2DBase.pointerEventObservable property will notified when appropriate events occur
  856. */
  857. get: function () {
  858. return this._interactionEnabled;
  859. },
  860. set: function (enable) {
  861. this._setupInteraction(enable);
  862. },
  863. enumerable: true,
  864. configurable: true
  865. });
  866. Object.defineProperty(Canvas2D.prototype, "designSize", {
  867. get: function () {
  868. return this._designSize;
  869. },
  870. enumerable: true,
  871. configurable: true
  872. });
  873. Object.defineProperty(Canvas2D.prototype, "designSizeUseHorizAxis", {
  874. get: function () {
  875. return this._designUseHorizAxis;
  876. },
  877. enumerable: true,
  878. configurable: true
  879. });
  880. Object.defineProperty(Canvas2D.prototype, "_engineData", {
  881. /**
  882. * Access the babylon.js' engine bound data, do not invoke this method, it's for internal purpose only
  883. * @returns {}
  884. */
  885. get: function () {
  886. return this.__engineData;
  887. },
  888. enumerable: true,
  889. configurable: true
  890. });
  891. Canvas2D.prototype.createCanvasProfileInfoCanvas = function () {
  892. if (this._profilingCanvas) {
  893. return this._profilingCanvas;
  894. }
  895. var canvas = new ScreenSpaceCanvas2D(this.scene, {
  896. id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children: [
  897. new BABYLON.Rectangle2D({
  898. id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, fill: "#C04040C0", marginAlignment: "h: left, v: top", margin: "10", padding: "10", children: [
  899. new BABYLON.Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
  900. ]
  901. })
  902. ]
  903. });
  904. this._profileInfoText = canvas.findById("ProfileInfoText");
  905. this._profilingCanvas = canvas;
  906. return canvas;
  907. };
  908. Canvas2D.prototype.checkBackgroundAvailability = function () {
  909. if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
  910. throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
  911. }
  912. };
  913. Canvas2D.prototype._initPerfMetrics = function () {
  914. this._drawCallsOpaqueCounter.fetchNewFrame();
  915. this._drawCallsAlphaTestCounter.fetchNewFrame();
  916. this._drawCallsTransparentCounter.fetchNewFrame();
  917. this._groupRenderCounter.fetchNewFrame();
  918. this._updateTransparentDataCounter.fetchNewFrame();
  919. this._cachedGroupRenderCounter.fetchNewFrame();
  920. this._updateCachedStateCounter.fetchNewFrame();
  921. this._updateLayoutCounter.fetchNewFrame();
  922. this._updatePositioningCounter.fetchNewFrame();
  923. this._updateLocalTransformCounter.fetchNewFrame();
  924. this._updateGlobalTransformCounter.fetchNewFrame();
  925. this._boundingInfoRecomputeCounter.fetchNewFrame();
  926. };
  927. Canvas2D.prototype._fetchPerfMetrics = function () {
  928. this._drawCallsOpaqueCounter.addCount(0, true);
  929. this._drawCallsAlphaTestCounter.addCount(0, true);
  930. this._drawCallsTransparentCounter.addCount(0, true);
  931. this._groupRenderCounter.addCount(0, true);
  932. this._updateTransparentDataCounter.addCount(0, true);
  933. this._cachedGroupRenderCounter.addCount(0, true);
  934. this._updateCachedStateCounter.addCount(0, true);
  935. this._updateLayoutCounter.addCount(0, true);
  936. this._updatePositioningCounter.addCount(0, true);
  937. this._updateLocalTransformCounter.addCount(0, true);
  938. this._updateGlobalTransformCounter.addCount(0, true);
  939. this._boundingInfoRecomputeCounter.addCount(0, true);
  940. };
  941. Canvas2D.prototype._updateProfileCanvas = function () {
  942. if (this._profileInfoText == null) {
  943. return;
  944. }
  945. var format = function (v) { return (Math.round(v * 100) / 100).toString(); };
  946. var p = "Draw Calls:\n" +
  947. (" - Opaque: " + format(this.drawCallsOpaqueCounter.current) + ", (avg:" + format(this.drawCallsOpaqueCounter.lastSecAverage) + ", t:" + format(this.drawCallsOpaqueCounter.total) + ")\n") +
  948. (" - AlphaTest: " + format(this.drawCallsAlphaTestCounter.current) + ", (avg:" + format(this.drawCallsAlphaTestCounter.lastSecAverage) + ", t:" + format(this.drawCallsAlphaTestCounter.total) + ")\n") +
  949. (" - Transparent: " + format(this.drawCallsTransparentCounter.current) + ", (avg:" + format(this.drawCallsTransparentCounter.lastSecAverage) + ", t:" + format(this.drawCallsTransparentCounter.total) + ")\n") +
  950. ("Group Render: " + this.groupRenderCounter.current + ", (avg:" + format(this.groupRenderCounter.lastSecAverage) + ", t:" + format(this.groupRenderCounter.total) + ")\n") +
  951. ("Update Transparent Data: " + this.updateTransparentDataCounter.current + ", (avg:" + format(this.updateTransparentDataCounter.lastSecAverage) + ", t:" + format(this.updateTransparentDataCounter.total) + ")\n") +
  952. ("Cached Group Render: " + this.cachedGroupRenderCounter.current + ", (avg:" + format(this.cachedGroupRenderCounter.lastSecAverage) + ", t:" + format(this.cachedGroupRenderCounter.total) + ")\n") +
  953. ("Update Cached States: " + this.updateCachedStateCounter.current + ", (avg:" + format(this.updateCachedStateCounter.lastSecAverage) + ", t:" + format(this.updateCachedStateCounter.total) + ")\n") +
  954. (" - Update Layout: " + this.updateLayoutCounter.current + ", (avg:" + format(this.updateLayoutCounter.lastSecAverage) + ", t:" + format(this.updateLayoutCounter.total) + ")\n") +
  955. (" - Update Positioning: " + this.updatePositioningCounter.current + ", (avg:" + format(this.updatePositioningCounter.lastSecAverage) + ", t:" + format(this.updatePositioningCounter.total) + ")\n") +
  956. (" - Update Local Trans: " + this.updateLocalTransformCounter.current + ", (avg:" + format(this.updateLocalTransformCounter.lastSecAverage) + ", t:" + format(this.updateLocalTransformCounter.total) + ")\n") +
  957. (" - Update Global Trans: " + this.updateGlobalTransformCounter.current + ", (avg:" + format(this.updateGlobalTransformCounter.lastSecAverage) + ", t:" + format(this.updateGlobalTransformCounter.total) + ")\n") +
  958. (" - BoundingInfo Recompute: " + this.boundingInfoRecomputeCounter.current + ", (avg:" + format(this.boundingInfoRecomputeCounter.lastSecAverage) + ", t:" + format(this.boundingInfoRecomputeCounter.total) + ")\n");
  959. this._profileInfoText.text = p;
  960. };
  961. Canvas2D.prototype._addDrawCallCount = function (count, renderMode) {
  962. switch (renderMode) {
  963. case BABYLON.Render2DContext.RenderModeOpaque:
  964. this._drawCallsOpaqueCounter.addCount(count, false);
  965. return;
  966. case BABYLON.Render2DContext.RenderModeAlphaTest:
  967. this._drawCallsAlphaTestCounter.addCount(count, false);
  968. return;
  969. case BABYLON.Render2DContext.RenderModeTransparent:
  970. this._drawCallsTransparentCounter.addCount(count, false);
  971. return;
  972. }
  973. };
  974. Canvas2D.prototype._addGroupRenderCount = function (count) {
  975. this._groupRenderCounter.addCount(count, false);
  976. };
  977. Canvas2D.prototype._addUpdateTransparentDataCount = function (count) {
  978. this._updateTransparentDataCounter.addCount(count, false);
  979. };
  980. Canvas2D.prototype.addCachedGroupRenderCounter = function (count) {
  981. this._cachedGroupRenderCounter.addCount(count, false);
  982. };
  983. Canvas2D.prototype.addUpdateCachedStateCounter = function (count) {
  984. this._updateCachedStateCounter.addCount(count, false);
  985. };
  986. Canvas2D.prototype.addUpdateLayoutCounter = function (count) {
  987. this._updateLayoutCounter.addCount(count, false);
  988. };
  989. Canvas2D.prototype.addUpdatePositioningCounter = function (count) {
  990. this._updatePositioningCounter.addCount(count, false);
  991. };
  992. Canvas2D.prototype.addupdateLocalTransformCounter = function (count) {
  993. this._updateLocalTransformCounter.addCount(count, false);
  994. };
  995. Canvas2D.prototype.addUpdateGlobalTransformCounter = function (count) {
  996. this._updateGlobalTransformCounter.addCount(count, false);
  997. };
  998. Canvas2D.prototype._updateTrackedNodes = function () {
  999. var cam = this.scene.cameraToUseForPointers || this.scene.activeCamera;
  1000. cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
  1001. var rh = this.engine.getRenderHeight();
  1002. var v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
  1003. for (var _i = 0, _a = this._trackedGroups; _i < _a.length; _i++) {
  1004. var group = _a[_i];
  1005. if (group.isDisposed || !group.isVisible) {
  1006. continue;
  1007. }
  1008. var node = group.trackedNode;
  1009. var worldMtx = node.getWorldMatrix();
  1010. var proj = BABYLON.Vector3.Project(Canvas2D._v, worldMtx, Canvas2D._m, v);
  1011. var s = this.scale;
  1012. group.x = Math.round(proj.x / s);
  1013. group.y = Math.round((rh - proj.y) / s);
  1014. }
  1015. };
  1016. /**
  1017. * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
  1018. */
  1019. Canvas2D.prototype.updateCanvasLayout = function (forceRecompute) {
  1020. this._updateCanvasState(forceRecompute);
  1021. };
  1022. Canvas2D.prototype._updateAdaptiveSizeWorldCanvas = function () {
  1023. if (this._globalTransformStep < 2) {
  1024. return;
  1025. }
  1026. var n = this.worldSpaceCanvasNode;
  1027. var bi = n.getBoundingInfo().boundingBox;
  1028. var v = bi.vectorsWorld;
  1029. var cam = this.scene.cameraToUseForPointers || this.scene.activeCamera;
  1030. cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
  1031. var vp = cam.viewport.toGlobal(this.engine.getRenderWidth(), this.engine.getRenderHeight());
  1032. var projPoints = new Array(4);
  1033. for (var i = 0; i < 4; i++) {
  1034. projPoints[i] = BABYLON.Vector3.Project(v[i], Canvas2D._mI, Canvas2D._m, vp);
  1035. }
  1036. var left = projPoints[3].subtract(projPoints[0]).length();
  1037. var top = projPoints[3].subtract(projPoints[1]).length();
  1038. var right = projPoints[1].subtract(projPoints[2]).length();
  1039. var bottom = projPoints[2].subtract(projPoints[0]).length();
  1040. var w = Math.round(Math.max(top, bottom));
  1041. var h = Math.round(Math.max(right, left));
  1042. var isW = w > h;
  1043. // Basically if it's under 256 we use 256, otherwise we take the biggest power of 2
  1044. var edge = Math.max(w, h);
  1045. if (edge < 256) {
  1046. edge = 256;
  1047. }
  1048. else {
  1049. edge = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
  1050. }
  1051. // Clip values if needed
  1052. edge = Math.min(edge, this._maxAdaptiveWorldSpaceCanvasSize);
  1053. var newScale = edge / ((isW) ? this.size.width : this.size.height);
  1054. if (newScale !== this.scale) {
  1055. var scale = newScale;
  1056. // console.log(`New adaptive scale for Canvas ${this.id}, w: ${w}, h: ${h}, scale: ${scale}, edge: ${edge}, isW: ${isW}`);
  1057. this._setRenderingScale(scale);
  1058. }
  1059. };
  1060. Canvas2D.prototype._updateCanvasState = function (forceRecompute) {
  1061. // Check if the update has already been made for this render Frame
  1062. if (!forceRecompute && this.scene.getRenderId() === this._updateRenderId) {
  1063. return;
  1064. }
  1065. // Detect a change of rendering size
  1066. var renderingSizeChanged = false;
  1067. var newWidth = this.engine.getRenderWidth();
  1068. if (newWidth !== this._renderingSize.width) {
  1069. renderingSizeChanged = true;
  1070. }
  1071. this._renderingSize.width = newWidth;
  1072. var newHeight = this.engine.getRenderHeight();
  1073. if (newHeight !== this._renderingSize.height) {
  1074. renderingSizeChanged = true;
  1075. }
  1076. this._renderingSize.height = newHeight;
  1077. // If the canvas fit the rendering size and it changed, update
  1078. if (renderingSizeChanged && this._fitRenderingDevice) {
  1079. this.size = this._renderingSize;
  1080. if (this._background) {
  1081. this._background.size = this.size;
  1082. }
  1083. // Dirty the Layout at the Canvas level to recompute as the size changed
  1084. this._setLayoutDirty();
  1085. }
  1086. // If there's a design size, update the scale according to the renderingSize
  1087. if (this._designSize) {
  1088. var scale;
  1089. if (this._designUseHorizAxis) {
  1090. scale = this._renderingSize.width / this._designSize.width;
  1091. }
  1092. else {
  1093. scale = this._renderingSize.height / this._designSize.height;
  1094. }
  1095. this.scale = scale;
  1096. }
  1097. var context = new BABYLON.PrepareRender2DContext();
  1098. ++this._globalTransformProcessStep;
  1099. this.updateCachedStates(false);
  1100. this._prepareGroupRender(context);
  1101. this._updateRenderId = this.scene.getRenderId();
  1102. };
  1103. /**
  1104. * Method that renders the Canvas, you should not invoke
  1105. */
  1106. Canvas2D.prototype._render = function () {
  1107. this._initPerfMetrics();
  1108. this._updateCanvasState(false);
  1109. this._updateTrackedNodes();
  1110. // Nothing to do is the Canvas is not visible
  1111. if (this.isVisible === false) {
  1112. return;
  1113. }
  1114. if (!this._isScreenSpace) {
  1115. this._updateAdaptiveSizeWorldCanvas();
  1116. }
  1117. this._updateCanvasState(false);
  1118. if (this._primPointerInfo.canvasPointerPos) {
  1119. this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
  1120. this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
  1121. }
  1122. this.engine.setState(false);
  1123. this._groupRender();
  1124. if (!this._isScreenSpace) {
  1125. if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagWorldCacheChanged)) {
  1126. this.worldSpaceCacheChanged();
  1127. this._clearFlags(BABYLON.SmartPropertyPrim.flagWorldCacheChanged);
  1128. }
  1129. }
  1130. // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
  1131. if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
  1132. this._cachedCanvasGroup._renderCachedCanvas();
  1133. }
  1134. this._fetchPerfMetrics();
  1135. this._updateProfileCanvas();
  1136. };
  1137. /**
  1138. * Internal method that allocate a cache for the given group.
  1139. * Caching is made using a collection of MapTexture where many groups have their bitmap cache stored inside.
  1140. * @param group The group to allocate the cache of.
  1141. * @return custom type with the PackedRect instance giving information about the cache location into the texture and also the MapTexture instance that stores the cache.
  1142. */
  1143. Canvas2D.prototype._allocateGroupCache = function (group, parent, minSize, useMipMap, anisotropicLevel) {
  1144. if (useMipMap === void 0) { useMipMap = false; }
  1145. if (anisotropicLevel === void 0) { anisotropicLevel = 1; }
  1146. var key = (useMipMap ? "MipMap" : "NoMipMap") + "_" + anisotropicLevel;
  1147. var rd = group._renderableData;
  1148. var noResizeScale = rd._noResizeOnScale;
  1149. var isCanvas = parent == null;
  1150. var scale;
  1151. if (noResizeScale) {
  1152. scale = isCanvas ? Canvas2D._unS : group.parent.actualScale;
  1153. }
  1154. else {
  1155. scale = group.actualScale;
  1156. }
  1157. // Determine size
  1158. var size = group.actualSize;
  1159. size = new BABYLON.Size(Math.ceil(size.width * scale.x), Math.ceil(size.height * scale.y));
  1160. if (minSize) {
  1161. size.width = Math.max(minSize.width, size.width);
  1162. size.height = Math.max(minSize.height, size.height);
  1163. }
  1164. var mapArray = this._groupCacheMaps.getOrAddWithFactory(key, function () { return new Array(); });
  1165. // Try to find a spot in one of the cached texture
  1166. var res = null;
  1167. var map;
  1168. for (var _i = 0; _i < mapArray.length; _i++) {
  1169. var _map = mapArray[_i];
  1170. map = _map;
  1171. var node = map.allocateRect(size);
  1172. if (node) {
  1173. res = { node: node, texture: map };
  1174. break;
  1175. }
  1176. }
  1177. // Couldn't find a map that could fit the rect, create a new map for it
  1178. if (!res) {
  1179. var mapSize = new BABYLON.Size(Canvas2D._groupTextureCacheSize, Canvas2D._groupTextureCacheSize);
  1180. // Check if the predefined size would fit, other create a custom size using the nearest bigger power of 2
  1181. if (size.width > mapSize.width || size.height > mapSize.height) {
  1182. mapSize.width = Math.pow(2, Math.ceil(Math.log(size.width) / Math.log(2)));
  1183. mapSize.height = Math.pow(2, Math.ceil(Math.log(size.height) / Math.log(2)));
  1184. }
  1185. var id = "groupsMapChache" + this._mapCounter++ + "forCanvas" + this.id;
  1186. map = new BABYLON.MapTexture(id, this._scene, mapSize, useMipMap ? BABYLON.Texture.TRILINEAR_SAMPLINGMODE : BABYLON.Texture.BILINEAR_SAMPLINGMODE, useMipMap);
  1187. map.hasAlpha = true;
  1188. map.anisotropicFilteringLevel = 4;
  1189. mapArray.splice(0, 0, map);
  1190. var node = map.allocateRect(size);
  1191. res = { node: node, texture: map };
  1192. }
  1193. // Check if we have to create a Sprite that will display the content of the Canvas which is cached.
  1194. // Don't do it in case of the group being a worldspace canvas (because its texture is bound to a WorldSpaceCanvas node)
  1195. if (group !== this || this._isScreenSpace) {
  1196. var node = res.node;
  1197. // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
  1198. var sprite;
  1199. if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
  1200. this._cachedCanvasGroup = BABYLON.Group2D._createCachedCanvasGroup(this);
  1201. sprite = new BABYLON.Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
  1202. sprite.zOrder = 1;
  1203. sprite.origin = BABYLON.Vector2.Zero();
  1204. }
  1205. else {
  1206. sprite = new BABYLON.Sprite2D(map, { parent: parent, id: "__cachedSpriteOfGroup__" + group.id, x: group.actualPosition.x, y: group.actualPosition.y, spriteSize: node.contentSize, spriteLocation: node.pos, dontInheritParentScale: true });
  1207. sprite.origin = group.origin.clone();
  1208. sprite.addExternalData("__cachedGroup__", group);
  1209. sprite.pointerEventObservable.add(function (e, s) {
  1210. if (group.pointerEventObservable !== null) {
  1211. group.pointerEventObservable.notifyObservers(e, s.mask);
  1212. }
  1213. });
  1214. res.sprite = sprite;
  1215. }
  1216. if (sprite && noResizeScale) {
  1217. var relScale = isCanvas ? group.actualScale : group.actualScale.divide(group.parent.actualScale);
  1218. sprite.scaleX = relScale.x;
  1219. sprite.scaleY = relScale.y;
  1220. }
  1221. }
  1222. return res;
  1223. };
  1224. /**
  1225. * Internal method used to register a Scene Node to track position for the given group
  1226. * Do not invoke this method, for internal purpose only.
  1227. * @param group the group to track its associated Scene Node
  1228. */
  1229. Canvas2D.prototype._registerTrackedNode = function (group) {
  1230. if (group._isFlagSet(BABYLON.SmartPropertyPrim.flagTrackedGroup)) {
  1231. return;
  1232. }
  1233. this._trackedGroups.push(group);
  1234. group._setFlags(BABYLON.SmartPropertyPrim.flagTrackedGroup);
  1235. };
  1236. /**
  1237. * Internal method used to unregister a tracked Scene Node
  1238. * Do not invoke this method, it's for internal purpose only.
  1239. * @param group the group to unregister its tracked Scene Node from.
  1240. */
  1241. Canvas2D.prototype._unregisterTrackedNode = function (group) {
  1242. if (!group._isFlagSet(BABYLON.SmartPropertyPrim.flagTrackedGroup)) {
  1243. return;
  1244. }
  1245. var i = this._trackedGroups.indexOf(group);
  1246. if (i !== -1) {
  1247. this._trackedGroups.splice(i, 1);
  1248. }
  1249. group._clearFlags(BABYLON.SmartPropertyPrim.flagTrackedGroup);
  1250. };
  1251. /**
  1252. * Get a Solid Color Brush instance matching the given color.
  1253. * @param color The color to retrieve
  1254. * @return A shared instance of the SolidColorBrush2D class that use the given color
  1255. */
  1256. Canvas2D.GetSolidColorBrush = function (color) {
  1257. return Canvas2D._solidColorBrushes.getOrAddWithFactory(color.toHexString(), function () { return new BABYLON.SolidColorBrush2D(color.clone(), true); });
  1258. };
  1259. /**
  1260. * Get a Solid Color Brush instance matching the given color expressed as a CSS formatted hexadecimal value.
  1261. * @param color The color to retrieve
  1262. * @return A shared instance of the SolidColorBrush2D class that uses the given color
  1263. */
  1264. Canvas2D.GetSolidColorBrushFromHex = function (hexValue) {
  1265. return Canvas2D._solidColorBrushes.getOrAddWithFactory(hexValue, function () { return new BABYLON.SolidColorBrush2D(BABYLON.Color4.FromHexString(hexValue), true); });
  1266. };
  1267. /**
  1268. * Get a Gradient Color Brush
  1269. * @param color1 starting color
  1270. * @param color2 engine color
  1271. * @param translation translation vector to apply. default is [0;0]
  1272. * @param rotation rotation in radian to apply to the brush, initial direction is top to bottom. rotation is counter clockwise. default is 0.
  1273. * @param scale scaling factor to apply. default is 1.
  1274. */
  1275. Canvas2D.GetGradientColorBrush = function (color1, color2, translation, rotation, scale) {
  1276. if (translation === void 0) { translation = BABYLON.Vector2.Zero(); }
  1277. if (rotation === void 0) { rotation = 0; }
  1278. if (scale === void 0) { scale = 1; }
  1279. return Canvas2D._gradientColorBrushes.getOrAddWithFactory(BABYLON.GradientColorBrush2D.BuildKey(color1, color2, translation, rotation, scale), function () { return new BABYLON.GradientColorBrush2D(color1, color2, translation, rotation, scale, true); });
  1280. };
  1281. /**
  1282. * Create a solid or gradient brush from a string value.
  1283. * @param brushString should be either
  1284. * - "solid: #RRGGBBAA" or "#RRGGBBAA"
  1285. * - "gradient: #FF808080, #FFFFFFF[, [10:20], 180, 1]" for color1, color2, translation, rotation (degree), scale. The last three are optionals, but if specified must be is this order. "gradient:" can be omitted.
  1286. */
  1287. Canvas2D.GetBrushFromString = function (brushString) {
  1288. // Note: yes, I hate/don't know RegEx.. Feel free to add your contribution to the cause!
  1289. brushString = brushString.trim();
  1290. var split = brushString.split(",");
  1291. // Solid, formatted as: "[solid:]#FF808080"
  1292. if (split.length === 1) {
  1293. var value = null;
  1294. if (brushString.indexOf("solid:") === 0) {
  1295. value = brushString.substr(6).trim();
  1296. }
  1297. else if (brushString.indexOf("#") === 0) {
  1298. value = brushString;
  1299. }
  1300. else {
  1301. return null;
  1302. }
  1303. return Canvas2D.GetSolidColorBrushFromHex(value);
  1304. }
  1305. else {
  1306. if (split[0].indexOf("gradient:") === 0) {
  1307. split[0] = split[0].substr(9).trim();
  1308. }
  1309. try {
  1310. var start = BABYLON.Color4.FromHexString(split[0].trim());
  1311. var end = BABYLON.Color4.FromHexString(split[1].trim());
  1312. var t = BABYLON.Vector2.Zero();
  1313. if (split.length > 2) {
  1314. var v = split[2].trim();
  1315. if (v.charAt(0) !== "[" || v.charAt(v.length - 1) !== "]") {
  1316. return null;
  1317. }
  1318. var sep = v.indexOf(":");
  1319. var x = parseFloat(v.substr(1, sep));
  1320. var y = parseFloat(v.substr(sep + 1, v.length - (sep + 1)));
  1321. t = new BABYLON.Vector2(x, y);
  1322. }
  1323. var r = 0;
  1324. if (split.length > 3) {
  1325. r = BABYLON.Tools.ToRadians(parseFloat(split[3].trim()));
  1326. }
  1327. var s = 1;
  1328. if (split.length > 4) {
  1329. s = parseFloat(split[4].trim());
  1330. }
  1331. return Canvas2D.GetGradientColorBrush(start, end, t, r, s);
  1332. }
  1333. catch (e) {
  1334. return null;
  1335. }
  1336. }
  1337. };
  1338. /**
  1339. * In this strategy only the direct children groups of the Canvas will be cached, their whole content (whatever the sub groups they have) into a single bitmap.
  1340. * This strategy doesn't allow primitives added directly as children of the Canvas.
  1341. * You typically want to use this strategy of a screenSpace fullscreen canvas: you don't want a bitmap cache taking the whole screen resolution but still want the main contents (say UI in the topLeft and rightBottom for instance) to be efficiently cached.
  1342. */
  1343. Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS = 1;
  1344. /**
  1345. * In this strategy each group will have its own cache bitmap (except if a given group explicitly defines the DONTCACHEOVERRIDE or CACHEINPARENTGROUP behaviors).
  1346. * This strategy is typically used if the canvas has some groups that are frequently animated. Unchanged ones will have a steady cache and the others will be refreshed when they change, reducing the redraw operation count to their content only.
  1347. * When using this strategy, group instances can rely on the DONTCACHEOVERRIDE or CACHEINPARENTGROUP behaviors to minimize the amount of cached bitmaps.
  1348. * Note that in this mode the Canvas itself is not cached, it only contains the sprites of its direct children group to render, there's no point to cache the whole canvas, sprites will be rendered pretty efficiently, the memory cost would be too great for the value of it.
  1349. */
  1350. Canvas2D.CACHESTRATEGY_ALLGROUPS = 2;
  1351. /**
  1352. * In this strategy the whole canvas is cached into a single bitmap containing every primitives it owns, at the exception of the ones that are owned by a group having the DONTCACHEOVERRIDE behavior (these primitives will be directly drawn to the viewport at each render for screenSpace Canvas or be part of the Canvas cache bitmap for worldSpace Canvas).
  1353. */
  1354. Canvas2D.CACHESTRATEGY_CANVAS = 3;
  1355. /**
  1356. * This strategy is used to recompose/redraw the canvas entirely at each viewport render.
  1357. * Use this strategy if memory is a concern above rendering performances and/or if the canvas is frequently animated (hence reducing the benefits of caching).
  1358. * Note that you can't use this strategy for WorldSpace Canvas, they need at least a top level group caching.
  1359. */
  1360. Canvas2D.CACHESTRATEGY_DONTCACHE = 4;
  1361. Canvas2D._zMinDelta = 1 / (Math.pow(2, 24) - 1);
  1362. Canvas2D._interInfo = new BABYLON.IntersectInfo2D();
  1363. Canvas2D._v = BABYLON.Vector3.Zero(); // Must stay zero
  1364. Canvas2D._m = BABYLON.Matrix.Identity();
  1365. Canvas2D._mI = BABYLON.Matrix.Identity(); // Must stay identity
  1366. Canvas2D._unS = new BABYLON.Vector2(1, 1);
  1367. /**
  1368. * Define the default size used for both the width and height of a MapTexture to allocate.
  1369. * Note that some MapTexture might be bigger than this size if the first node to allocate is bigger in width or height
  1370. */
  1371. Canvas2D._groupTextureCacheSize = 1024;
  1372. Canvas2D._solidColorBrushes = new BABYLON.StringDictionary();
  1373. Canvas2D._gradientColorBrushes = new BABYLON.StringDictionary();
  1374. Canvas2D = __decorate([
  1375. BABYLON.className("Canvas2D")
  1376. ], Canvas2D);
  1377. return Canvas2D;
  1378. })(BABYLON.Group2D);
  1379. BABYLON.Canvas2D = Canvas2D;
  1380. var WorldSpaceCanvas2D = (function (_super) {
  1381. __extends(WorldSpaceCanvas2D, _super);
  1382. /**
  1383. * Create a new 2D WorldSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a world transformation information to place it in the world space.
  1384. * This kind of canvas can't have its Primitives directly drawn in the Viewport, they need to be cached in a bitmap at some point, as a consequence the DONT_CACHE strategy is unavailable. For now only CACHESTRATEGY_CANVAS is supported, but the remaining strategies will be soon.
  1385. * @param scene the Scene that owns the Canvas
  1386. * @param size the dimension of the Canvas in World Space
  1387. * @param settings a combination of settings, possible ones are
  1388. * - children: an array of direct children primitives
  1389. * - id: a text identifier, for information purpose only, default is null.
  1390. * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
  1391. * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
  1392. * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
  1393. * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
  1394. * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
  1395. * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
  1396. * - backgroundRoundRadius: the round radius of the background, either backgroundFill or backgroundBorder must be specified.
  1397. * - backgroundFill: the brush to use to create a background fill for the canvas. can be a string value (see Canvas2D.GetBrushFromString) or a IBrush2D instance.
  1398. * - backgroundBorder: the brush to use to create a background border for the canvas. can be a string value (see Canvas2D.GetBrushFromString) or a IBrush2D instance.
  1399. * - backgroundBorderThickness: if a backgroundBorder is specified, its thickness can be set using this property
  1400. * - customWorldSpaceNode: if specified the Canvas will be rendered in this given Node. But it's the responsibility of the caller to set the "worldSpaceToNodeLocal" property to compute the hit of the mouse ray into the node (in world coordinate system) as well as rendering the cached bitmap in the node itself. The properties cachedRect and cachedTexture of Group2D will give you what you need to do that.
  1401. * - maxAdaptiveCanvasSize: set the max size (width and height) of the bitmap that will contain the cached version of the WorldSpace Canvas. Default is 1024 or less if it's not supported. In any case the value you give will be clipped by the maximum that WebGL supports on the running device. You can set any size, more than 1024 if you want, but testing proved it's a good max value for non "retina" like screens.
  1402. * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  1403. * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  1404. * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  1405. * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  1406. * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
  1407. */
  1408. function WorldSpaceCanvas2D(scene, size, settings) {
  1409. BABYLON.Prim2DBase._isCanvasInit = true;
  1410. var s = settings;
  1411. s.isScreenSpace = false;
  1412. s.size = size.clone();
  1413. settings.cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_CANVAS : settings.cachingStrategy;
  1414. if (settings.cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
  1415. throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
  1416. }
  1417. _super.call(this, scene, settings);
  1418. BABYLON.Prim2DBase._isCanvasInit = false;
  1419. this._renderableData._useMipMap = true;
  1420. this._renderableData._anisotropicLevel = 8;
  1421. //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
  1422. // throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
  1423. //}
  1424. var createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
  1425. var id = settings ? settings.id || null : null;
  1426. // Set the max size of texture allowed for the adaptive render of the world space canvas cached bitmap
  1427. var capMaxTextSize = this.engine.getCaps().maxRenderTextureSize;
  1428. var defaultTextSize = (Math.min(capMaxTextSize, 1024)); // Default is 4K if allowed otherwise the max allowed
  1429. if (settings.maxAdaptiveCanvasSize == null) {
  1430. this._maxAdaptiveWorldSpaceCanvasSize = defaultTextSize;
  1431. }
  1432. else {
  1433. // We still clip the given value with the max allowed, the user may not be aware of these limitations
  1434. this._maxAdaptiveWorldSpaceCanvasSize = Math.min(settings.maxAdaptiveCanvasSize, capMaxTextSize);
  1435. }
  1436. if (createWorldSpaceNode) {
  1437. var plane = new BABYLON.WorldSpaceCanvas2DNode(id, scene, this);
  1438. var vertexData = BABYLON.VertexData.CreatePlane({
  1439. width: size.width,
  1440. height: size.height,
  1441. sideOrientation: settings && settings.sideOrientation || BABYLON.Mesh.DEFAULTSIDE
  1442. });
  1443. var mtl = new BABYLON.StandardMaterial(id + "_Material", scene);
  1444. this.applyCachedTexture(vertexData, mtl);
  1445. vertexData.applyToMesh(plane, true);
  1446. mtl.specularColor = new BABYLON.Color3(0, 0, 0);
  1447. mtl.disableLighting = true;
  1448. mtl.useAlphaFromDiffuseTexture = true;
  1449. plane.position = settings && settings.worldPosition || BABYLON.Vector3.Zero();
  1450. plane.rotationQuaternion = settings && settings.worldRotation || BABYLON.Quaternion.Identity();
  1451. plane.material = mtl;
  1452. this._worldSpaceNode = plane;
  1453. }
  1454. else {
  1455. this._worldSpaceNode = settings.customWorldSpaceNode;
  1456. this.applyCachedTexture(null, null);
  1457. }
  1458. }
  1459. WorldSpaceCanvas2D = __decorate([
  1460. BABYLON.className("WorldSpaceCanvas2D")
  1461. ], WorldSpaceCanvas2D);
  1462. return WorldSpaceCanvas2D;
  1463. })(Canvas2D);
  1464. BABYLON.WorldSpaceCanvas2D = WorldSpaceCanvas2D;
  1465. var ScreenSpaceCanvas2D = (function (_super) {
  1466. __extends(ScreenSpaceCanvas2D, _super);
  1467. /**
  1468. * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the bottom/left corner of the screen.
  1469. * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
  1470. * All caching strategies will be available.
  1471. * PLEASE NOTE: the origin of a Screen Space Canvas is set to [0;0] (bottom/left) which is different than the default origin of a Primitive which is centered [0.5;0.5]
  1472. * @param scene the Scene that owns the Canvas
  1473. * @param settings a combination of settings, possible ones are
  1474. * - children: an array of direct children primitives
  1475. * - id: a text identifier, for information purpose only
  1476. * - x: the position along the x axis (horizontal), relative to the left edge of the viewport. you can alternatively use the position setting.
  1477. * - y: the position along the y axis (vertically), relative to the bottom edge of the viewport. you can alternatively use the position setting.
  1478. * - position: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
  1479. * - width: the width of the Canvas. you can alternatively use the size setting.
  1480. * - height: the height of the Canvas. you can alternatively use the size setting.
  1481. * - size: the Size of the canvas. Alternatively the width and height properties can be set. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
  1482. * - designSize: if you want to set the canvas content based on fixed coordinates whatever the final canvas dimension would be, set this. For instance a designSize of 360*640 will give you the possibility to specify all the children element in this frame. The Canvas' true size will be the HTMLCanvas' size: for instance it could be 720*1280, then a uniform scale of 2 will be applied on the Canvas to keep the absolute coordinates working as expecting. If the ratios of the designSize and the true Canvas size are not the same, then the scale is computed following the designUseHorizAxis member by using either the size of the horizontal axis or the vertical axis.
  1483. * - designUseHorizAxis: you can set this member if you use designSize to specify which axis is priority to compute the scale when the ratio of the canvas' size is different from the designSize's one.
  1484. * - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_DONTCACHE
  1485. * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is true.
  1486. * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
  1487. * - backgroundRoundRadius: the round radius of the background, either backgroundFill or backgroundBorder must be specified.
  1488. * - backgroundFill: the brush to use to create a background fill for the canvas. can be a string value (see BABYLON.Canvas2D.GetBrushFromString) or a IBrush2D instance.
  1489. * - backgroundBorder: the brush to use to create a background border for the canvas. can be a string value (see BABYLON.Canvas2D.GetBrushFromString) or a IBrush2D instance.
  1490. * - backgroundBorderThickness: if a backgroundBorder is specified, its thickness can be set using this property
  1491. * - customWorldSpaceNode: if specified the Canvas will be rendered in this given Node. But it's the responsibility of the caller to set the "worldSpaceToNodeLocal" property to compute the hit of the mouse ray into the node (in world coordinate system) as well as rendering the cached bitmap in the node itself. The properties cachedRect and cachedTexture of Group2D will give you what you need to do that.
  1492. * - paddingTop: top padding, can be a number (will be pixels) or a string (see BABYLON.PrimitiveThickness.fromString)
  1493. * - paddingLeft: left padding, can be a number (will be pixels) or a string (see BABYLON.PrimitiveThickness.fromString)
  1494. * - paddingRight: right padding, can be a number (will be pixels) or a string (see BABYLON.PrimitiveThickness.fromString)
  1495. * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see BABYLON.PrimitiveThickness.fromString)
  1496. * - padding: top, left, right and bottom padding formatted as a single string (see BABYLON.PrimitiveThickness.fromString)
  1497. */
  1498. function ScreenSpaceCanvas2D(scene, settings) {
  1499. BABYLON.Prim2DBase._isCanvasInit = true;
  1500. _super.call(this, scene, settings);
  1501. }
  1502. ScreenSpaceCanvas2D = __decorate([
  1503. BABYLON.className("ScreenSpaceCanvas2D")
  1504. ], ScreenSpaceCanvas2D);
  1505. return ScreenSpaceCanvas2D;
  1506. })(Canvas2D);
  1507. BABYLON.ScreenSpaceCanvas2D = ScreenSpaceCanvas2D;
  1508. })(BABYLON || (BABYLON = {}));