babylon.text2d.ts 12 KB

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