babylon.text2d.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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 engine = instanceInfo.owner.owner.engine;
  35. this.fontTexture.update();
  36. var effect = context.useInstancing ? this.effectInstanced : this.effect;
  37. engine.enableEffect(effect);
  38. effect.setTexture("diffuseSampler", this.fontTexture);
  39. engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
  40. var curAlphaMode = engine.getAlphaMode();
  41. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE, true);
  42. var pid = context.groupInfoPartData[0];
  43. if (context.useInstancing) {
  44. if (!this.instancingAttributes) {
  45. this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
  46. }
  47. engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
  48. engine.draw(true, 0, 6, pid._partData.usedElementCount);
  49. engine.unbindInstanceAttributes();
  50. }
  51. else {
  52. for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  53. this.setupUniforms(effect, 0, pid._partData, i);
  54. engine.draw(true, 0, 6);
  55. }
  56. }
  57. engine.setAlphaMode(curAlphaMode);
  58. return true;
  59. };
  60. Text2DRenderCache.prototype.dispose = function () {
  61. if (!_super.prototype.dispose.call(this)) {
  62. return false;
  63. }
  64. if (this.vb) {
  65. this._engine._releaseBuffer(this.vb);
  66. this.vb = null;
  67. }
  68. if (this.ib) {
  69. this._engine._releaseBuffer(this.ib);
  70. this.ib = null;
  71. }
  72. if (this.fontTexture) {
  73. this.fontTexture.dispose();
  74. this.fontTexture = null;
  75. }
  76. if (this.effect) {
  77. this._engine._releaseEffect(this.effect);
  78. this.effect = null;
  79. }
  80. if (this.effectInstanced) {
  81. this._engine._releaseEffect(this.effectInstanced);
  82. this.effectInstanced = null;
  83. }
  84. return true;
  85. };
  86. return Text2DRenderCache;
  87. }(BABYLON.ModelRenderCache));
  88. BABYLON.Text2DRenderCache = Text2DRenderCache;
  89. var Text2DInstanceData = (function (_super) {
  90. __extends(Text2DInstanceData, _super);
  91. function Text2DInstanceData(partId, dataElementCount) {
  92. _super.call(this, partId, dataElementCount);
  93. }
  94. Object.defineProperty(Text2DInstanceData.prototype, "topLeftUV", {
  95. get: function () {
  96. return null;
  97. },
  98. enumerable: true,
  99. configurable: true
  100. });
  101. Object.defineProperty(Text2DInstanceData.prototype, "sizeUV", {
  102. get: function () {
  103. return null;
  104. },
  105. enumerable: true,
  106. configurable: true
  107. });
  108. Object.defineProperty(Text2DInstanceData.prototype, "textureSize", {
  109. get: function () {
  110. return null;
  111. },
  112. enumerable: true,
  113. configurable: true
  114. });
  115. Object.defineProperty(Text2DInstanceData.prototype, "color", {
  116. get: function () {
  117. return null;
  118. },
  119. enumerable: true,
  120. configurable: true
  121. });
  122. __decorate([
  123. BABYLON.instanceData()
  124. ], Text2DInstanceData.prototype, "topLeftUV", null);
  125. __decorate([
  126. BABYLON.instanceData()
  127. ], Text2DInstanceData.prototype, "sizeUV", null);
  128. __decorate([
  129. BABYLON.instanceData()
  130. ], Text2DInstanceData.prototype, "textureSize", null);
  131. __decorate([
  132. BABYLON.instanceData()
  133. ], Text2DInstanceData.prototype, "color", null);
  134. return Text2DInstanceData;
  135. }(BABYLON.InstanceDataBase));
  136. BABYLON.Text2DInstanceData = Text2DInstanceData;
  137. var Text2D = (function (_super) {
  138. __extends(Text2D, _super);
  139. function Text2D() {
  140. _super.apply(this, arguments);
  141. }
  142. Object.defineProperty(Text2D.prototype, "fontName", {
  143. get: function () {
  144. return this._fontName;
  145. },
  146. set: function (value) {
  147. if (this._fontName) {
  148. throw new Error("Font Name change is not supported right now.");
  149. }
  150. this._fontName = value;
  151. },
  152. enumerable: true,
  153. configurable: true
  154. });
  155. Object.defineProperty(Text2D.prototype, "defaultFontColor", {
  156. get: function () {
  157. return this._defaultFontColor;
  158. },
  159. set: function (value) {
  160. this._defaultFontColor = value;
  161. },
  162. enumerable: true,
  163. configurable: true
  164. });
  165. Object.defineProperty(Text2D.prototype, "text", {
  166. get: function () {
  167. return this._text;
  168. },
  169. set: function (value) {
  170. this._text = value;
  171. this._actualSize = null; // A change of text will reset the Actual Area Size which will be recomputed next time it's used
  172. this._updateCharCount();
  173. },
  174. enumerable: true,
  175. configurable: true
  176. });
  177. Object.defineProperty(Text2D.prototype, "areaSize", {
  178. get: function () {
  179. return this._areaSize;
  180. },
  181. set: function (value) {
  182. this._areaSize = value;
  183. },
  184. enumerable: true,
  185. configurable: true
  186. });
  187. Object.defineProperty(Text2D.prototype, "actualSize", {
  188. get: function () {
  189. if (this.areaSize) {
  190. return this.areaSize;
  191. }
  192. if (this._actualSize) {
  193. return this._actualSize;
  194. }
  195. this._actualSize = this.fontTexture.measureText(this._text, this._tabulationSize);
  196. return this._actualSize;
  197. },
  198. enumerable: true,
  199. configurable: true
  200. });
  201. Object.defineProperty(Text2D.prototype, "fontTexture", {
  202. get: function () {
  203. if (this._fontTexture) {
  204. return this._fontTexture;
  205. }
  206. this._fontTexture = BABYLON.FontTexture.GetCachedFontTexture(this.owner.scene, this.fontName);
  207. return this._fontTexture;
  208. },
  209. enumerable: true,
  210. configurable: true
  211. });
  212. Text2D.prototype.dispose = function () {
  213. if (!_super.prototype.dispose.call(this)) {
  214. return false;
  215. }
  216. if (this._fontTexture) {
  217. BABYLON.FontTexture.ReleaseCachedFontTexture(this.owner.scene, this.fontName);
  218. this._fontTexture = null;
  219. }
  220. return true;
  221. };
  222. Text2D.prototype.updateLevelBoundingInfo = function () {
  223. BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo, this.origin);
  224. };
  225. Text2D.prototype.setupText2D = function (owner, parent, id, position, origin, fontName, text, areaSize, defaultFontColor, tabulationSize, isVisible, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
  226. this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
  227. this.fontName = fontName;
  228. this.defaultFontColor = defaultFontColor;
  229. this.text = text;
  230. this.areaSize = areaSize;
  231. this._tabulationSize = tabulationSize;
  232. this.isAlphaTest = true;
  233. };
  234. /**
  235. * Create a Text primitive
  236. * @param parent the parent primitive, must be a valid primitive (or the Canvas)
  237. * @param text the text to display
  238. * Options:
  239. * - id a text identifier, for information purpose
  240. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  241. * - origin: define the normalized origin point location, default [0.5;0.5]
  242. * - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
  243. * - defaultColor: the color by default to apply on each letter of the text to display, default is plain white.
  244. * - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
  245. * - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
  246. * - isVisible: true if the text must be visible, false for hidden. Default is true.
  247. * - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
  248. * - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  249. * - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  250. */
  251. Text2D.Create = function (parent, text, options) {
  252. BABYLON.Prim2DBase.CheckParent(parent);
  253. var text2d = new Text2D();
  254. if (!options) {
  255. text2d.setupText2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, "12pt Arial", text, null, new BABYLON.Color4(1, 1, 1, 1), 4, true, null, null, null, null, null, null);
  256. }
  257. else {
  258. var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
  259. text2d.setupText2D(parent.owner, parent, options.id || null, pos, options.origin || null, options.fontName || "12pt Arial", text, options.areaSize, options.defaultFontColor || new BABYLON.Color4(1, 1, 1, 1), (options.tabulationSize == null) ? 4 : options.tabulationSize, (options.isVisible == null) ? true : options.isVisible, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
  260. }
  261. return text2d;
  262. };
  263. Text2D.prototype.levelIntersect = function (intersectInfo) {
  264. // 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...
  265. return true;
  266. };
  267. Text2D.prototype.createModelRenderCache = function (modelKey) {
  268. var renderCache = new Text2DRenderCache(this.owner.engine, modelKey);
  269. return renderCache;
  270. };
  271. Text2D.prototype.setupModelRenderCache = function (modelRenderCache) {
  272. var renderCache = modelRenderCache;
  273. var engine = this.owner.engine;
  274. renderCache.fontTexture = this.fontTexture;
  275. var vb = new Float32Array(4);
  276. for (var i = 0; i < 4; i++) {
  277. vb[i] = i;
  278. }
  279. renderCache.vb = engine.createVertexBuffer(vb);
  280. var ib = new Float32Array(6);
  281. ib[0] = 0;
  282. ib[1] = 2;
  283. ib[2] = 1;
  284. ib[3] = 0;
  285. ib[4] = 3;
  286. ib[5] = 2;
  287. renderCache.ib = engine.createIndexBuffer(ib);
  288. // 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
  289. var ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"], true);
  290. if (ei) {
  291. renderCache.effectInstanced = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
  292. }
  293. ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"], false);
  294. renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
  295. return renderCache;
  296. };
  297. Text2D.prototype.createInstanceDataParts = function () {
  298. return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
  299. };
  300. // Looks like a hack!? Yes! Because that's what it is!
  301. // 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
  302. // 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)
  303. Text2D.prototype.beforeRefreshForLayoutConstruction = function (part) {
  304. if (!this._charCount) {
  305. var curText = this._text;
  306. this.text = "A";
  307. return curText;
  308. }
  309. };
  310. // if obj contains something, we restore the _text property
  311. Text2D.prototype.afterRefreshForLayoutConstruction = function (part, obj) {
  312. if (obj !== undefined) {
  313. this.text = obj;
  314. }
  315. };
  316. Text2D.prototype.refreshInstanceDataPart = function (part) {
  317. if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
  318. return false;
  319. }
  320. if (part.id === Text2D.TEXT2D_MAINPARTID) {
  321. var d = part;
  322. var texture = this.fontTexture;
  323. var ts = texture.getSize();
  324. var textSize = texture.measureText(this.text, this._tabulationSize);
  325. var offset = BABYLON.Vector2.Zero();
  326. var charxpos = 0;
  327. d.dataElementCount = this._charCount;
  328. d.curElement = 0;
  329. for (var _i = 0, _a = this.text; _i < _a.length; _i++) {
  330. var char = _a[_i];
  331. // Line feed
  332. if (char === "\n") {
  333. offset.x = 0;
  334. offset.y -= texture.lineHeight;
  335. }
  336. // Tabulation ?
  337. if (char === "\t") {
  338. var nextPos = charxpos + this._tabulationSize;
  339. nextPos = nextPos - (nextPos % this._tabulationSize);
  340. offset.x += (nextPos - charxpos) * texture.spaceWidth;
  341. charxpos = nextPos;
  342. continue;
  343. }
  344. if (char < " ") {
  345. continue;
  346. }
  347. this.updateInstanceDataPart(d, offset, textSize);
  348. var ci = texture.getChar(char);
  349. offset.x += ci.charWidth;
  350. d.topLeftUV = ci.topLeftUV;
  351. var suv = ci.bottomRightUV.subtract(ci.topLeftUV);
  352. d.sizeUV = suv;
  353. d.textureSize = new BABYLON.Vector2(ts.width, ts.height);
  354. d.color = this.defaultFontColor;
  355. ++d.curElement;
  356. }
  357. }
  358. return true;
  359. };
  360. Text2D.prototype._updateCharCount = function () {
  361. var count = 0;
  362. for (var _i = 0, _a = this._text; _i < _a.length; _i++) {
  363. var char = _a[_i];
  364. if (char === "\r" || char === "\n" || char === "\t" || char < " ") {
  365. continue;
  366. }
  367. ++count;
  368. }
  369. this._charCount = count;
  370. };
  371. Text2D.TEXT2D_MAINPARTID = 1;
  372. __decorate([
  373. BABYLON.modelLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, function (pi) { return Text2D.fontProperty = pi; }, false, true)
  374. ], Text2D.prototype, "fontName", null);
  375. __decorate([
  376. BABYLON.dynamicLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, function (pi) { return Text2D.defaultFontColorProperty = pi; })
  377. ], Text2D.prototype, "defaultFontColor", null);
  378. __decorate([
  379. BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, function (pi) { return Text2D.textProperty = pi; }, false, true)
  380. ], Text2D.prototype, "text", null);
  381. __decorate([
  382. BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, function (pi) { return Text2D.areaSizeProperty = pi; })
  383. ], Text2D.prototype, "areaSize", null);
  384. Text2D = __decorate([
  385. BABYLON.className("Text2D")
  386. ], Text2D);
  387. return Text2D;
  388. }(BABYLON.RenderablePrim2D));
  389. BABYLON.Text2D = Text2D;
  390. })(BABYLON || (BABYLON = {}));