babylon.text2d.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. module BABYLON {
  2. export class Text2DRenderCache extends ModelRenderCache {
  3. vb: WebGLBuffer;
  4. ib: WebGLBuffer;
  5. borderVB: WebGLBuffer;
  6. borderIB: WebGLBuffer;
  7. instancingAttributes: InstancingAttributeInfo[];
  8. fontTexture: FontTexture;
  9. effect: Effect;
  10. render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
  11. // Do nothing if the shader is still loading/preparing
  12. if (!this.effect.isReady() || !this.fontTexture.isReady()) {
  13. return false;
  14. }
  15. // Compute the offset locations of the attributes in the vertexshader that will be mapped to the instance buffer data
  16. if (!this.instancingAttributes) {
  17. this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, this.effect);
  18. }
  19. var engine = instanceInfo._owner.owner.engine;
  20. this.fontTexture.update();
  21. engine.enableEffect(this.effect);
  22. this.effect.setTexture("diffuseSampler", this.fontTexture);
  23. engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
  24. var cur = engine.getAlphaMode();
  25. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  26. let count = instanceInfo._instancesPartsData[0].usedElementCount;
  27. if (instanceInfo._owner.owner.supportInstancedArray) {
  28. engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
  29. engine.draw(true, 0, 6, count);
  30. engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], this.instancingAttributes);
  31. } else {
  32. for (let i = 0; i < count; i++) {
  33. this.setupUniforms(this.effect, 0, instanceInfo._instancesPartsData[0], i);
  34. engine.draw(true, 0, 6);
  35. }
  36. }
  37. engine.setAlphaMode(cur);
  38. return true;
  39. }
  40. }
  41. export class Text2DInstanceData extends InstanceDataBase {
  42. constructor(partId: number, dataElementCount: number) {
  43. super(partId, dataElementCount);
  44. }
  45. @instanceData()
  46. get topLeftUV(): Vector2 {
  47. return null;
  48. }
  49. @instanceData()
  50. get sizeUV(): Vector2 {
  51. return null;
  52. }
  53. @instanceData()
  54. get textureSize(): Vector2 {
  55. return null;
  56. }
  57. }
  58. @className("Text2D")
  59. export class Text2D extends RenderablePrim2D {
  60. static TEXT2D_MAINPARTID = 1;
  61. public static fontProperty: Prim2DPropInfo;
  62. public static textProperty: Prim2DPropInfo;
  63. public static areaSizeProperty: Prim2DPropInfo;
  64. public static vAlignProperty: Prim2DPropInfo;
  65. public static hAlignProperty: Prim2DPropInfo;
  66. public static TEXT2D_VALIGN_TOP = 1;
  67. public static TEXT2D_VALIGN_CENTER = 2;
  68. public static TEXT2D_VALIGN_BOTTOM = 3;
  69. public static TEXT2D_HALIGN_LEFT = 1;
  70. public static TEXT2D_HALIGN_CENTER = 2;
  71. public static TEXT2D_HALIGN_RIGHT = 3;
  72. @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Text2D.fontProperty = pi, false, true)
  73. public get fontName(): string {
  74. return this._fontName;
  75. }
  76. public set fontName(value: string) {
  77. if (this._fontName) {
  78. throw new Error("Font Name change is not supported right now.");
  79. }
  80. this._fontName = value;
  81. }
  82. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Text2D.textProperty = pi, false, true)
  83. public get text(): string {
  84. return this._text;
  85. }
  86. public set text(value: string) {
  87. this._text = value;
  88. this._actualAreaSize = null; // A change of text will reset the Actual Area Size which will be recomputed next time it's used
  89. this._updateCharCount();
  90. }
  91. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Text2D.areaSizeProperty = pi)
  92. public get areaSize(): Size {
  93. return this._areaSize;
  94. }
  95. public set areaSize(value: Size) {
  96. this._areaSize = value;
  97. }
  98. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Text2D.vAlignProperty = pi)
  99. public get vAlign(): number {
  100. return this._vAlign;
  101. }
  102. public set vAlign(value: number) {
  103. this._vAlign = value;
  104. }
  105. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Text2D.hAlignProperty = pi)
  106. public get hAlign(): number {
  107. return this._hAlign;
  108. }
  109. public set hAlign(value: number) {
  110. this._hAlign = value;
  111. }
  112. public get actualAreaSize(): Size {
  113. if (this.areaSize) {
  114. return this.areaSize;
  115. }
  116. if (this._actualAreaSize) {
  117. return this._actualAreaSize;
  118. }
  119. this._actualAreaSize = this.fontTexture.measureText(this._text, this._tabulationSize);
  120. return this._actualAreaSize;
  121. }
  122. protected get fontTexture(): FontTexture {
  123. if (this._fontTexture) {
  124. return this._fontTexture;
  125. }
  126. this._fontTexture = FontTexture.GetCachedFontTexture(this.owner.scene, this.fontName);
  127. return this._fontTexture;
  128. }
  129. public dispose(): boolean {
  130. if (!super.dispose()) {
  131. return false;
  132. }
  133. if (this._fontTexture) {
  134. FontTexture.ReleaseCachedFontTexture(this.owner.scene, this.fontName);
  135. this._fontTexture = null;
  136. }
  137. return true;
  138. }
  139. protected updateLevelBoundingInfo() {
  140. BoundingInfo2D.ConstructFromSizeToRef(this.actualAreaSize, this._levelBoundingInfo);
  141. }
  142. protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, fontName: string, text: string, areaSize: Size, vAlign, hAlign, tabulationSize: number) {
  143. this.setupRenderablePrim2D(owner, parent, id, position, true);
  144. this.fontName = fontName;
  145. this.text = text;
  146. this.areaSize = areaSize;
  147. this.vAlign = vAlign;
  148. this.hAlign = hAlign;
  149. this._tabulationSize = tabulationSize;
  150. this._isTransparent = true;
  151. this.origin = Vector2.Zero();
  152. }
  153. public static Create(parent: Prim2DBase, id: string, x: number, y: number, fontName: string, text: string, areaSize?: Size, vAlign = Text2D.TEXT2D_VALIGN_TOP, hAlign = Text2D.TEXT2D_HALIGN_LEFT, tabulationSize: number = 4): Text2D {
  154. Prim2DBase.CheckParent(parent);
  155. let text2d = new Text2D();
  156. text2d.setupText2D(parent.owner, parent, id, new Vector2(x, y), fontName, text, areaSize, vAlign, hAlign, tabulationSize);
  157. return text2d;
  158. }
  159. protected createModelRenderCache(): ModelRenderCache {
  160. let renderCache = new Text2DRenderCache();
  161. return renderCache;
  162. }
  163. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  164. let renderCache = <Text2DRenderCache>modelRenderCache;
  165. let engine = this.owner.engine;
  166. renderCache.fontTexture = this.fontTexture;
  167. let vb = new Float32Array(4);
  168. for (let i = 0; i < 4; i++) {
  169. vb[i] = i;
  170. }
  171. renderCache.vb = engine.createVertexBuffer(vb);
  172. let ib = new Float32Array(6);
  173. ib[0] = 0;
  174. ib[1] = 2;
  175. ib[2] = 1;
  176. ib[3] = 0;
  177. ib[4] = 3;
  178. ib[5] = 2;
  179. renderCache.ib = engine.createIndexBuffer(ib);
  180. // Effects
  181. let ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
  182. renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null, e => {
  183. // renderCache.setupUniformsLocation(e, ei.uniforms, Text2D.TEXT2D_MAINPARTID);
  184. });
  185. return renderCache;
  186. }
  187. protected createInstanceDataParts(): InstanceDataBase[] {
  188. return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
  189. }
  190. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  191. if (!super.refreshInstanceDataPart(part)) {
  192. return false;
  193. }
  194. if (part.id === Text2D.TEXT2D_MAINPARTID) {
  195. let d = <Text2DInstanceData>part;
  196. let texture = this.fontTexture;
  197. let ts = texture.getSize();
  198. let offset = Vector2.Zero();
  199. let charxpos = 0;
  200. d.curElement = 0;
  201. for (let char of this.text) {
  202. // Line feed
  203. if (char === "\n") {
  204. offset.x = 0;
  205. offset.y += texture.lineHeight;
  206. }
  207. // Tabulation ?
  208. if (char === "\t") {
  209. let nextPos = charxpos + this._tabulationSize;
  210. nextPos = nextPos - (nextPos % this._tabulationSize);
  211. offset.x += (nextPos - charxpos) * texture.spaceWidth;
  212. charxpos = nextPos;
  213. continue;
  214. }
  215. if (char < " ") {
  216. continue;
  217. }
  218. this.updateInstanceDataPart(d, offset);
  219. let ci = texture.getChar(char);
  220. offset.x += ci.charWidth;
  221. d.topLeftUV = ci.topLeftUV;
  222. let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
  223. d.sizeUV = suv;
  224. d.textureSize = new Vector2(ts.width, ts.height);
  225. ++d.curElement;
  226. }
  227. }
  228. return true;
  229. }
  230. private _updateCharCount() {
  231. let count = 0;
  232. for (let char of this._text) {
  233. if (char === "\r" || char === "\n" || char === "\t" || char < " ") {
  234. continue;
  235. }
  236. ++count;
  237. }
  238. this._charCount = count;
  239. }
  240. private _fontTexture: FontTexture;
  241. private _tabulationSize: number;
  242. private _charCount: number;
  243. private _fontName: string;
  244. private _text: string;
  245. private _areaSize: Size;
  246. private _actualAreaSize: Size;
  247. private _vAlign: number;
  248. private _hAlign: number;
  249. }
  250. }