babylon.text2d.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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. @instanceData()
  58. get color(): Color4 {
  59. return null;
  60. }
  61. }
  62. @className("Text2D")
  63. export class Text2D extends RenderablePrim2D {
  64. static TEXT2D_MAINPARTID = 1;
  65. public static fontProperty: Prim2DPropInfo;
  66. public static defaultFontColorProperty: Prim2DPropInfo;
  67. public static textProperty: Prim2DPropInfo;
  68. public static areaSizeProperty: Prim2DPropInfo;
  69. public static vAlignProperty: Prim2DPropInfo;
  70. public static hAlignProperty: Prim2DPropInfo;
  71. public static TEXT2D_VALIGN_TOP = 1;
  72. public static TEXT2D_VALIGN_CENTER = 2;
  73. public static TEXT2D_VALIGN_BOTTOM = 3;
  74. public static TEXT2D_HALIGN_LEFT = 1;
  75. public static TEXT2D_HALIGN_CENTER = 2;
  76. public static TEXT2D_HALIGN_RIGHT = 3;
  77. @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Text2D.fontProperty = pi, false, true)
  78. public get fontName(): string {
  79. return this._fontName;
  80. }
  81. public set fontName(value: string) {
  82. if (this._fontName) {
  83. throw new Error("Font Name change is not supported right now.");
  84. }
  85. this._fontName = value;
  86. }
  87. @dynamicLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Text2D.defaultFontColorProperty = pi)
  88. public get defaultFontColor(): Color4 {
  89. return this._defaultFontColor;
  90. }
  91. public set defaultFontColor(value: Color4) {
  92. this._defaultFontColor = value;
  93. }
  94. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Text2D.textProperty = pi, false, true)
  95. public get text(): string {
  96. return this._text;
  97. }
  98. public set text(value: string) {
  99. this._text = value;
  100. this._actualAreaSize = null; // A change of text will reset the Actual Area Size which will be recomputed next time it's used
  101. this._updateCharCount();
  102. }
  103. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Text2D.areaSizeProperty = pi)
  104. public get areaSize(): Size {
  105. return this._areaSize;
  106. }
  107. public set areaSize(value: Size) {
  108. this._areaSize = value;
  109. }
  110. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Text2D.vAlignProperty = pi)
  111. public get vAlign(): number {
  112. return this._vAlign;
  113. }
  114. public set vAlign(value: number) {
  115. this._vAlign = value;
  116. }
  117. @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Text2D.hAlignProperty = pi)
  118. public get hAlign(): number {
  119. return this._hAlign;
  120. }
  121. public set hAlign(value: number) {
  122. this._hAlign = value;
  123. }
  124. public get actualAreaSize(): Size {
  125. if (this.areaSize) {
  126. return this.areaSize;
  127. }
  128. if (this._actualAreaSize) {
  129. return this._actualAreaSize;
  130. }
  131. this._actualAreaSize = this.fontTexture.measureText(this._text, this._tabulationSize);
  132. return this._actualAreaSize;
  133. }
  134. protected get fontTexture(): FontTexture {
  135. if (this._fontTexture) {
  136. return this._fontTexture;
  137. }
  138. this._fontTexture = FontTexture.GetCachedFontTexture(this.owner.scene, this.fontName);
  139. return this._fontTexture;
  140. }
  141. public dispose(): boolean {
  142. if (!super.dispose()) {
  143. return false;
  144. }
  145. if (this._fontTexture) {
  146. FontTexture.ReleaseCachedFontTexture(this.owner.scene, this.fontName);
  147. this._fontTexture = null;
  148. }
  149. return true;
  150. }
  151. protected updateLevelBoundingInfo() {
  152. BoundingInfo2D.CreateFromSizeToRef(this.actualAreaSize, this._levelBoundingInfo);
  153. }
  154. protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, vAlign, hAlign, tabulationSize: number) {
  155. this.setupRenderablePrim2D(owner, parent, id, position, true);
  156. this.fontName = fontName;
  157. this.defaultFontColor = defaultFontColor;
  158. this.text = text;
  159. this.areaSize = areaSize;
  160. this.vAlign = vAlign;
  161. this.hAlign = hAlign;
  162. this._tabulationSize = tabulationSize;
  163. this._isTransparent = true;
  164. this.origin = Vector2.Zero();
  165. }
  166. public static Create(parent: Prim2DBase, id: string, x: number, y: number, fontName: string, text: string, defaultFontColor?: Color4, areaSize?: Size, vAlign = Text2D.TEXT2D_VALIGN_TOP, hAlign = Text2D.TEXT2D_HALIGN_LEFT, tabulationSize: number = 4): Text2D {
  167. Prim2DBase.CheckParent(parent);
  168. let text2d = new Text2D();
  169. text2d.setupText2D(parent.owner, parent, id, new Vector2(x, y), fontName, text, areaSize, defaultFontColor || new Color4(0,0,0,1), vAlign, hAlign, tabulationSize);
  170. return text2d;
  171. }
  172. protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
  173. let renderCache = new Text2DRenderCache(modelKey, isTransparent);
  174. return renderCache;
  175. }
  176. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  177. let renderCache = <Text2DRenderCache>modelRenderCache;
  178. let engine = this.owner.engine;
  179. renderCache.fontTexture = this.fontTexture;
  180. let vb = new Float32Array(4);
  181. for (let i = 0; i < 4; i++) {
  182. vb[i] = i;
  183. }
  184. renderCache.vb = engine.createVertexBuffer(vb);
  185. let ib = new Float32Array(6);
  186. ib[0] = 0;
  187. ib[1] = 2;
  188. ib[2] = 1;
  189. ib[3] = 0;
  190. ib[4] = 3;
  191. ib[5] = 2;
  192. renderCache.ib = engine.createIndexBuffer(ib);
  193. // Effects
  194. let ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
  195. renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null, e => {
  196. // renderCache.setupUniformsLocation(e, ei.uniforms, Text2D.TEXT2D_MAINPARTID);
  197. });
  198. return renderCache;
  199. }
  200. protected createInstanceDataParts(): InstanceDataBase[] {
  201. return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
  202. }
  203. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  204. if (!super.refreshInstanceDataPart(part)) {
  205. return false;
  206. }
  207. if (part.id === Text2D.TEXT2D_MAINPARTID) {
  208. let d = <Text2DInstanceData>part;
  209. let texture = this.fontTexture;
  210. let ts = texture.getSize();
  211. let offset = Vector2.Zero();
  212. let charxpos = 0;
  213. d.curElement = 0;
  214. for (let char of this.text) {
  215. // Line feed
  216. if (char === "\n") {
  217. offset.x = 0;
  218. offset.y += texture.lineHeight;
  219. }
  220. // Tabulation ?
  221. if (char === "\t") {
  222. let nextPos = charxpos + this._tabulationSize;
  223. nextPos = nextPos - (nextPos % this._tabulationSize);
  224. offset.x += (nextPos - charxpos) * texture.spaceWidth;
  225. charxpos = nextPos;
  226. continue;
  227. }
  228. if (char < " ") {
  229. continue;
  230. }
  231. this.updateInstanceDataPart(d, offset);
  232. let ci = texture.getChar(char);
  233. offset.x += ci.charWidth;
  234. d.topLeftUV = ci.topLeftUV;
  235. let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
  236. d.sizeUV = suv;
  237. d.textureSize = new Vector2(ts.width, ts.height);
  238. d.color = this.defaultFontColor;
  239. ++d.curElement;
  240. }
  241. }
  242. return true;
  243. }
  244. private _updateCharCount() {
  245. let count = 0;
  246. for (let char of this._text) {
  247. if (char === "\r" || char === "\n" || char === "\t" || char < " ") {
  248. continue;
  249. }
  250. ++count;
  251. }
  252. this._charCount = count;
  253. }
  254. private _fontTexture: FontTexture;
  255. private _tabulationSize: number;
  256. private _charCount: number;
  257. private _fontName: string;
  258. private _defaultFontColor: Color4;
  259. private _text: string;
  260. private _areaSize: Size;
  261. private _actualAreaSize: Size;
  262. private _vAlign: number;
  263. private _hAlign: number;
  264. }
  265. }