babylon.text2d.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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. var Text2DRenderCache = (function (_super) {
  15. __extends(Text2DRenderCache, _super);
  16. function Text2DRenderCache() {
  17. _super.apply(this, arguments);
  18. this.effectsReady = false;
  19. this.vb = null;
  20. this.ib = null;
  21. this.instancingAttributes = null;
  22. this.fontTexture = null;
  23. this.effect = null;
  24. this.effectInstanced = null;
  25. }
  26. Text2DRenderCache.prototype.render = function (instanceInfo, context) {
  27. // Do nothing if the shader is still loading/preparing
  28. if (!this.effectsReady) {
  29. if ((this.effect && (!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady())))) {
  30. return false;
  31. }
  32. this.effectsReady = true;
  33. }
  34. var canvas = instanceInfo.owner.owner;
  35. var engine = canvas.engine;
  36. this.fontTexture.update();
  37. var effect = context.useInstancing ? this.effectInstanced : this.effect;
  38. engine.enableEffect(effect);
  39. effect.setTexture("diffuseSampler", this.fontTexture);
  40. engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
  41. var curAlphaMode = engine.getAlphaMode();
  42. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE, true);
  43. var pid = context.groupInfoPartData[0];
  44. if (context.useInstancing) {
  45. if (!this.instancingAttributes) {
  46. this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
  47. }
  48. var glBuffer = context.instancedBuffers ? context.instancedBuffers[0] : pid._partBuffer;
  49. var count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
  50. canvas._addDrawCallCount(1, context.renderMode);
  51. engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingAttributes);
  52. engine.draw(true, 0, 6, count);
  53. engine.unbindInstanceAttributes();
  54. }
  55. else {
  56. canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
  57. for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  58. this.setupUniforms(effect, 0, pid._partData, i);
  59. engine.draw(true, 0, 6);
  60. }
  61. }
  62. engine.setAlphaMode(curAlphaMode, true);
  63. return true;
  64. };
  65. Text2DRenderCache.prototype.dispose = function () {
  66. if (!_super.prototype.dispose.call(this)) {
  67. return false;
  68. }
  69. if (this.vb) {
  70. this._engine._releaseBuffer(this.vb);
  71. this.vb = null;
  72. }
  73. if (this.ib) {
  74. this._engine._releaseBuffer(this.ib);
  75. this.ib = null;
  76. }
  77. if (this.fontTexture) {
  78. this.fontTexture.decCachedFontTextureCounter();
  79. this.fontTexture = null;
  80. }
  81. this.effect = null;
  82. this.effectInstanced = null;
  83. return true;
  84. };
  85. return Text2DRenderCache;
  86. }(BABYLON.ModelRenderCache));
  87. BABYLON.Text2DRenderCache = Text2DRenderCache;
  88. var Text2DInstanceData = (function (_super) {
  89. __extends(Text2DInstanceData, _super);
  90. function Text2DInstanceData(partId, dataElementCount) {
  91. _super.call(this, partId, dataElementCount);
  92. }
  93. Object.defineProperty(Text2DInstanceData.prototype, "topLeftUV", {
  94. get: function () {
  95. return null;
  96. },
  97. enumerable: true,
  98. configurable: true
  99. });
  100. Object.defineProperty(Text2DInstanceData.prototype, "sizeUV", {
  101. get: function () {
  102. return null;
  103. },
  104. enumerable: true,
  105. configurable: true
  106. });
  107. Object.defineProperty(Text2DInstanceData.prototype, "textureSize", {
  108. get: function () {
  109. return null;
  110. },
  111. enumerable: true,
  112. configurable: true
  113. });
  114. Object.defineProperty(Text2DInstanceData.prototype, "color", {
  115. get: function () {
  116. return null;
  117. },
  118. enumerable: true,
  119. configurable: true
  120. });
  121. Object.defineProperty(Text2DInstanceData.prototype, "superSampleFactor", {
  122. get: function () {
  123. return null;
  124. },
  125. enumerable: true,
  126. configurable: true
  127. });
  128. __decorate([
  129. BABYLON.instanceData()
  130. ], Text2DInstanceData.prototype, "topLeftUV", null);
  131. __decorate([
  132. BABYLON.instanceData()
  133. ], Text2DInstanceData.prototype, "sizeUV", null);
  134. __decorate([
  135. BABYLON.instanceData()
  136. ], Text2DInstanceData.prototype, "textureSize", null);
  137. __decorate([
  138. BABYLON.instanceData()
  139. ], Text2DInstanceData.prototype, "color", null);
  140. __decorate([
  141. BABYLON.instanceData()
  142. ], Text2DInstanceData.prototype, "superSampleFactor", null);
  143. return Text2DInstanceData;
  144. }(BABYLON.InstanceDataBase));
  145. BABYLON.Text2DInstanceData = Text2DInstanceData;
  146. var Text2D = (function (_super) {
  147. __extends(Text2D, _super);
  148. /**
  149. * Create a Text primitive
  150. * @param text the text to display
  151. * @param settings a combination of settings, possible ones are
  152. * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
  153. * - children: an array of direct children
  154. * - id a text identifier, for information purpose
  155. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  156. * - rotation: the initial rotation (in radian) of the primitive. default is 0
  157. * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
  158. * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
  159. * - zOrder: override the zOrder with the specified value
  160. * - origin: define the normalized origin point location, default [0.5;0.5]
  161. * - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
  162. * - fontSuperSample: if true the text will be rendered with a superSampled font (the font is twice the given size). Use this settings if the text lies in world space or if it's scaled in.
  163. * - defaultFontColor: the color by default to apply on each letter of the text to display, default is plain white.
  164. * - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
  165. * - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
  166. * - isVisible: true if the text must be visible, false for hidden. Default is true.
  167. * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
  168. * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
  169. * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
  170. * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  171. * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  172. * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  173. * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  174. * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
  175. * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
  176. * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
  177. * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
  178. * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  179. * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  180. * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  181. * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  182. * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
  183. */
  184. function Text2D(text, settings) {
  185. if (!settings) {
  186. settings = {};
  187. }
  188. _super.call(this, settings);
  189. this.fontName = (settings.fontName == null) ? "12pt Arial" : settings.fontName;
  190. this._fontSuperSample = (settings.fontSuperSample != null && settings.fontSuperSample);
  191. this.defaultFontColor = (settings.defaultFontColor == null) ? new BABYLON.Color4(1, 1, 1, 1) : settings.defaultFontColor;
  192. this._tabulationSize = (settings.tabulationSize == null) ? 4 : settings.tabulationSize;
  193. this._textSize = null;
  194. this.text = text;
  195. this.size = (settings.size == null) ? null : settings.size;
  196. this._updateRenderMode();
  197. }
  198. Object.defineProperty(Text2D.prototype, "fontName", {
  199. get: function () {
  200. return this._fontName;
  201. },
  202. set: function (value) {
  203. if (this._fontName) {
  204. throw new Error("Font Name change is not supported right now.");
  205. }
  206. this._fontName = value;
  207. },
  208. enumerable: true,
  209. configurable: true
  210. });
  211. Object.defineProperty(Text2D.prototype, "defaultFontColor", {
  212. get: function () {
  213. return this._defaultFontColor;
  214. },
  215. set: function (value) {
  216. this._defaultFontColor = value;
  217. },
  218. enumerable: true,
  219. configurable: true
  220. });
  221. Object.defineProperty(Text2D.prototype, "text", {
  222. get: function () {
  223. return this._text;
  224. },
  225. set: function (value) {
  226. this._text = value;
  227. this._textSize = null; // A change of text will reset the TextSize which will be recomputed next time it's used
  228. this._size = null;
  229. this._updateCharCount();
  230. // Trigger a textSize to for a sizeChange if necessary, which is needed for layout to recompute
  231. var s = this.textSize;
  232. },
  233. enumerable: true,
  234. configurable: true
  235. });
  236. Object.defineProperty(Text2D.prototype, "size", {
  237. get: function () {
  238. if (this._size != null) {
  239. return this._size;
  240. }
  241. return this.textSize;
  242. },
  243. set: function (value) {
  244. this._size = value;
  245. },
  246. enumerable: true,
  247. configurable: true
  248. });
  249. Object.defineProperty(Text2D.prototype, "actualSize", {
  250. /**
  251. * Get the actual size of the Text2D primitive
  252. */
  253. get: function () {
  254. if (this._actualSize) {
  255. return this._actualSize;
  256. }
  257. return this.size;
  258. },
  259. enumerable: true,
  260. configurable: true
  261. });
  262. Object.defineProperty(Text2D.prototype, "textSize", {
  263. /**
  264. * Get the area that bounds the text associated to the primitive
  265. */
  266. get: function () {
  267. if (!this._textSize) {
  268. if (this.owner) {
  269. var newSize = this.fontTexture.measureText(this._text, this._tabulationSize);
  270. if (!newSize.equals(this._textSize)) {
  271. this.onPrimitivePropertyDirty(BABYLON.Prim2DBase.sizeProperty.flagId);
  272. this._positioningDirty();
  273. }
  274. this._textSize = newSize;
  275. }
  276. else {
  277. return Text2D.nullSize;
  278. }
  279. }
  280. return this._textSize;
  281. },
  282. enumerable: true,
  283. configurable: true
  284. });
  285. Object.defineProperty(Text2D.prototype, "fontTexture", {
  286. get: function () {
  287. if (this._fontTexture) {
  288. return this._fontTexture;
  289. }
  290. if (this.fontName == null || this.owner == null || this.owner.scene == null) {
  291. return null;
  292. }
  293. this._fontTexture = BABYLON.FontTexture.GetCachedFontTexture(this.owner.scene, this.fontName, this._fontSuperSample);
  294. return this._fontTexture;
  295. },
  296. enumerable: true,
  297. configurable: true
  298. });
  299. /**
  300. * Dispose the primitive, remove it from its parent
  301. */
  302. Text2D.prototype.dispose = function () {
  303. if (!_super.prototype.dispose.call(this)) {
  304. return false;
  305. }
  306. if (this._fontTexture) {
  307. BABYLON.FontTexture.ReleaseCachedFontTexture(this.owner.scene, this.fontName, this._fontSuperSample);
  308. this._fontTexture = null;
  309. }
  310. return true;
  311. };
  312. Text2D.prototype.updateLevelBoundingInfo = function () {
  313. BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
  314. };
  315. Text2D.prototype.levelIntersect = function (intersectInfo) {
  316. // For now I can't do something better that boundingInfo is a hit, detecting an intersection on a particular letter would be possible, but do we really need it? Not for now...
  317. return true;
  318. };
  319. Text2D.prototype.createModelRenderCache = function (modelKey) {
  320. var renderCache = new Text2DRenderCache(this.owner.engine, modelKey);
  321. return renderCache;
  322. };
  323. Text2D.prototype.setupModelRenderCache = function (modelRenderCache) {
  324. var renderCache = modelRenderCache;
  325. var engine = this.owner.engine;
  326. renderCache.fontTexture = this.fontTexture;
  327. renderCache.fontTexture.incCachedFontTextureCounter();
  328. var vb = new Float32Array(4);
  329. for (var i = 0; i < 4; i++) {
  330. vb[i] = i;
  331. }
  332. renderCache.vb = engine.createVertexBuffer(vb);
  333. var ib = new Float32Array(6);
  334. ib[0] = 0;
  335. ib[1] = 2;
  336. ib[2] = 1;
  337. ib[3] = 0;
  338. ib[4] = 3;
  339. ib[5] = 2;
  340. renderCache.ib = engine.createIndexBuffer(ib);
  341. // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
  342. var ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"], null, true);
  343. if (ei) {
  344. renderCache.effectInstanced = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
  345. }
  346. ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"], null, false);
  347. renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
  348. return renderCache;
  349. };
  350. Text2D.prototype.createInstanceDataParts = function () {
  351. return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
  352. };
  353. // Looks like a hack!? Yes! Because that's what it is!
  354. // For the InstanceData layer to compute correctly we need to set all the properties involved, which won't be the case if there's no text
  355. // This method is called before the layout construction for us to detect this case, set some text and return the initial one to restore it after (there can be some text without char to display, say "\t\n" for instance)
  356. Text2D.prototype.beforeRefreshForLayoutConstruction = function (part) {
  357. if (!this._charCount) {
  358. var curText = this._text;
  359. this.text = "A";
  360. return curText;
  361. }
  362. };
  363. // if obj contains something, we restore the _text property
  364. Text2D.prototype.afterRefreshForLayoutConstruction = function (part, obj) {
  365. if (obj !== undefined) {
  366. this.text = obj;
  367. }
  368. };
  369. Text2D.prototype.refreshInstanceDataPart = function (part) {
  370. if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
  371. return false;
  372. }
  373. if (part.id === Text2D.TEXT2D_MAINPARTID) {
  374. var d = part;
  375. var texture = this.fontTexture;
  376. var superSampleFactor = texture.isSuperSampled ? 0.5 : 1;
  377. var ts = texture.getSize();
  378. var offset = BABYLON.Vector2.Zero();
  379. var lh = this.fontTexture.lineHeight;
  380. offset.y = ((this.textSize.height / lh) - 1) * lh; // Origin is bottom, not top, so the offset is starting with a y that is the top location of the text
  381. var charxpos = 0;
  382. d.dataElementCount = this._charCount;
  383. d.curElement = 0;
  384. for (var _i = 0, _a = this.text; _i < _a.length; _i++) {
  385. var char = _a[_i];
  386. // Line feed
  387. if (char === "\n") {
  388. offset.x = 0;
  389. offset.y -= texture.lineHeight;
  390. }
  391. // Tabulation ?
  392. if (char === "\t") {
  393. var nextPos = charxpos + this._tabulationSize;
  394. nextPos = nextPos - (nextPos % this._tabulationSize);
  395. offset.x += (nextPos - charxpos) * texture.spaceWidth;
  396. charxpos = nextPos;
  397. continue;
  398. }
  399. if (char < " ") {
  400. continue;
  401. }
  402. this.updateInstanceDataPart(d, offset);
  403. var ci = texture.getChar(char);
  404. offset.x += ci.charWidth;
  405. d.topLeftUV = ci.topLeftUV;
  406. var suv = ci.bottomRightUV.subtract(ci.topLeftUV);
  407. d.sizeUV = suv;
  408. d.textureSize = new BABYLON.Vector2(ts.width, ts.height);
  409. d.color = this.defaultFontColor;
  410. d.superSampleFactor = superSampleFactor;
  411. ++d.curElement;
  412. }
  413. }
  414. return true;
  415. };
  416. Text2D.prototype._updateCharCount = function () {
  417. var count = 0;
  418. for (var _i = 0, _a = this._text; _i < _a.length; _i++) {
  419. var char = _a[_i];
  420. if (char === "\r" || char === "\n" || char === "\t" || char < " ") {
  421. continue;
  422. }
  423. ++count;
  424. }
  425. this._charCount = count;
  426. };
  427. Text2D.prototype._useTextureAlpha = function () {
  428. return this.fontTexture != null && this.fontTexture.hasAlpha;
  429. };
  430. Text2D.prototype._shouldUseAlphaFromTexture = function () {
  431. return true;
  432. };
  433. Text2D.TEXT2D_MAINPARTID = 1;
  434. __decorate([
  435. BABYLON.modelLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, function (pi) { return Text2D.fontProperty = pi; }, false, true)
  436. ], Text2D.prototype, "fontName", null);
  437. __decorate([
  438. BABYLON.dynamicLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, function (pi) { return Text2D.defaultFontColorProperty = pi; })
  439. ], Text2D.prototype, "defaultFontColor", null);
  440. __decorate([
  441. BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, function (pi) { return Text2D.textProperty = pi; }, false, true)
  442. ], Text2D.prototype, "text", null);
  443. __decorate([
  444. BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, function (pi) { return Text2D.sizeProperty = pi; })
  445. ], Text2D.prototype, "size", null);
  446. Text2D = __decorate([
  447. BABYLON.className("Text2D")
  448. ], Text2D);
  449. return Text2D;
  450. }(BABYLON.RenderablePrim2D));
  451. BABYLON.Text2D = Text2D;
  452. })(BABYLON || (BABYLON = {}));