///
module BABYLON {
/**
* AsciiArtFontTexture is the helper class used to easily create your ascii art font texture.
*
* It basically takes care rendering the font front the given font size to a texture.
* This is used later on in the postprocess.
*/
export class AsciiArtFontTexture extends BaseTexture {
@serialize("font")
private _font: string;
@serialize("text")
private _text: string;
private _charSize: number;
/**
* Gets the size of one char in the texture (each char fits in size * size space in the texture).
*/
public get charSize(): number {
return this._charSize;
}
/**
* Create a new instance of the Ascii Art FontTexture class
* @param name the name of the texture
* @param font the font to use, use the W3C CSS notation
* @param text the caracter set to use in the rendering.
* @param scene the scene that owns the texture
*/
constructor(name: string, font: string, text: string, scene: Scene) {
super(scene);
this.name = name;
this._text == text;
this._font == font;
this.wrapU = Texture.CLAMP_ADDRESSMODE;
this.wrapV = Texture.CLAMP_ADDRESSMODE;
//this.anisotropicFilteringLevel = 1;
// Get the font specific info.
var maxCharHeight = this.getFontHeight(font);
var maxCharWidth = this.getFontWidth(font);
this._charSize = Math.max(maxCharHeight.height, maxCharWidth);
// This is an approximate size, but should always be able to fit at least the maxCharCount.
var textureWidth = Math.ceil(this._charSize * text.length);
var textureHeight = this._charSize;
// Create the texture that will store the font characters.
this._texture = scene.getEngine().createDynamicTexture(textureWidth, textureHeight, false, Texture.NEAREST_SAMPLINGMODE);
//scene.getEngine().setclamp
var textureSize = this.getSize();
// Create a canvas with the final size: the one matching the texture.
var canvas = document.createElement("canvas");
canvas.width = textureSize.width;
canvas.height = textureSize.height;
var context = canvas.getContext("2d");
context.textBaseline = "top";
context.font = font;
context.fillStyle = "white";
context.imageSmoothingEnabled = false;
// Sets the text in the texture.
for (var i = 0; i < text.length; i++) {
context.fillText(text[i], i * this._charSize, -maxCharHeight.offset);
}
// Flush the text in the dynamic texture.
this.getScene().getEngine().updateDynamicTexture(this._texture, canvas, false, true);
}
/**
* Gets the max char width of a font.
* @param font the font to use, use the W3C CSS notation
* @return the max char width
*/
private getFontWidth(font: string): number {
var fontDraw = document.createElement("canvas");
var ctx = fontDraw.getContext('2d');
ctx.fillStyle = 'white';
ctx.font = font;
return ctx.measureText("W").width;
}
// More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
/**
* Gets the max char height of a font.
* @param font the font to use, use the W3C CSS notation
* @return the max char height
*/
private getFontHeight(font: string): {height: number, offset: number} {
var fontDraw = document.createElement("canvas");
var ctx = fontDraw.getContext('2d');
ctx.fillRect(0, 0, fontDraw.width, fontDraw.height);
ctx.textBaseline = 'top';
ctx.fillStyle = 'white';
ctx.font = font;
ctx.fillText('jH|', 0, 0);
var pixels = ctx.getImageData(0, 0, fontDraw.width, fontDraw.height).data;
var start = -1;
var end = -1;
for (var row = 0; row < fontDraw.height; row++) {
for (var column = 0; column < fontDraw.width; column++) {
var index = (row * fontDraw.width + column) * 4;
if (pixels[index] === 0) {
if (column === fontDraw.width - 1 && start !== -1) {
end = row;
row = fontDraw.height;
break;
}
continue;
}
else {
if (start === -1) {
start = row;
}
break;
}
}
}
return { height: (end - start)+1, offset: start-1}
}
/**
* Clones the current AsciiArtTexture.
* @return the clone of the texture.
*/
public clone(): AsciiArtFontTexture {
return new AsciiArtFontTexture(this.name, this._font, this._text, this.getScene());
}
/**
* Parses a json object representing the texture and returns an instance of it.
* @param source the source JSON representation
* @param scene the scene to create the texture for
* @return the parsed texture
*/
public static Parse(source: any, scene: Scene): AsciiArtFontTexture {
var texture = SerializationHelper.Parse(() => new AsciiArtFontTexture(source.name, source.font, source.text, scene),
source, scene, null);
return texture;
}
}
/**
* Option available in the Ascii Art Post Process.
*/
export interface IAsciiArtPostProcessOptions {
/**
* The font to use following the w3c font definition.
*/
font?: string;
/**
* The caracter set to use in the postprocess.
*/
caracterSet?: string;
/**
* This defines the amount you want to mix the "tile" or caracter space colored in the ascii art.
* This number is defined between 0 and 1;
*/
mixToTile?:number;
/**
* This defines the amount you want to mix the normal rendering pass in the ascii art.
* This number is defined between 0 and 1;
*/
mixToNormal?:number;
}
/**
* AsciiArtPostProcess helps rendering everithing in Ascii Art.
*
* Simmply add it to your scene and let the nerd that lives in you have fun.
* Example usage: var pp = new AsciiArtPostProcess("myAscii", "20px Monospace", camera);
*/
export class AsciiArtPostProcess extends PostProcess {
/**
* The font texture used to render the char in the post process.
*/
private _asciiArtFontTexture: AsciiArtFontTexture;
/**
* This defines the amount you want to mix the "tile" or caracter space colored in the ascii art.
* This number is defined between 0 and 1;
*/
public mixToTile:number = 0;
/**
* This defines the amount you want to mix the normal rendering pass in the ascii art.
* This number is defined between 0 and 1;
*/
public mixToNormal:number = 0;
/**
* Instantiates a new Ascii Art Post Process.
* @param name the name to give to the postprocess
* @camera the camera to apply the post process to.
* @param options can either be the font name or an option object following the IAsciiArtPostProcessOptions format
*/
constructor(name: string, camera: Camera, options?: string | IAsciiArtPostProcessOptions) {
super(name,
'asciiart',
['asciiArtFontInfos', 'asciiArtOptions'],
['asciiArtFont'],
{
width: camera.getEngine().getRenderWidth(),
height: camera.getEngine().getRenderHeight()
},
camera,
Texture.TRILINEAR_SAMPLINGMODE,
camera.getEngine(),
true);
// Default values.
var font = "40px Monospace";
var caracterSet = " `-.'_:,\"=^;<+!*?/cL\\zrs7TivJtC{3F)Il(xZfY5S2eajo14[nuyE]P6V9kXpKwGhqAUbOd8#HRDB0$mgMW&Q%N@";
// Use options.
if (options) {
if (typeof(options) === "string") {
font = options;
}
else {
font = (options).font || font;
caracterSet = (options).caracterSet || caracterSet;
this.mixToTile = (options).mixToTile || this.mixToTile;
this.mixToNormal = (options).mixToNormal || this.mixToNormal;
}
}
this._asciiArtFontTexture = new AsciiArtFontTexture(name, font, caracterSet, camera.getScene());
var textureSize = this._asciiArtFontTexture.getSize();
this.onApply = (effect: Effect) => {
effect.setTexture("asciiArtFont", this._asciiArtFontTexture);
effect.setFloat4("asciiArtFontInfos",
this._asciiArtFontTexture.charSize,
caracterSet.length,
textureSize.width,
textureSize.height);
effect.setFloat4("asciiArtOptions",
this.width,
this.height,
this.mixToNormal,
this.mixToTile);
};
}
}
}