• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Wurst] TextSplat

Status
Not open for further replies.
Level 14
Joined
Jun 27, 2008
Messages
1,325
Still missing:
- Update FontConverter
- Add ".appendImage()" method
- Write documentation :(
- Minor optimization
- For future versions: add dynamic fields which can be changed without having to rebuild the whole TextSplat.
- €: i think i forgot the destructor ^_°

Code:
Wurst:
package TextSplat
import TextSplatConfig
import Char

constant int DISABLE_COLOR_ALPHA = -1
constant real DISABLE_MAX_WIDTH = 1.*'Peng'+'uins'

public class TextSplat
	private vec2 pos // absolute world coordinates
	private vec2 dimensions // bottom right corner in world coordinates
	private vec2 cursor // absolute world coordinates
	private real maxWidth // absolute world x-coordinate
	private ImageList images
	private Layer layer
	private real fontSize // in world coordinates
	
	/** Creates a TextSplat at specified position with the specified fontSize (in world coordinates). */
	construct(vec2 pos, real fontSize)
		this.pos = pos
		this.fontSize = fontSize
		images = new ImageList()
		layer = Layer.L1
		maxWidth = DISABLE_MAX_WIDTH
		cursor = vec2(pos.y, pos.y - fontSize)
		dimensions = pos
	
	/** Defines a maximum width after which a line break will be inserted. */
	function setMaxWidth(real maxWidth)
		this.maxWidth = pos.x + maxWidth
	
	/** Disables a previously set maximum width. */
	function disableMaxWidth()
		this.maxWidth = DISABLE_MAX_WIDTH
	
	/** Returns the dimensions (width, height) of the whole TextSplat. */
	function getDimensions() returns vec2
		return vec2(dimensions.x - pos.x, pos.y - dimensions.y)
	
	/** Clears the current text, if any, and displays the specified text. */
	function setText(string text)
		clear()
		append(text)
	
	/** Appends the specified text at the current cursor position. */
	function append(string text)
		append(text, colorA(0, 0, 0, DISABLE_COLOR_ALPHA))
	
	/** Appends the specified text at the current cursor position and colorizes it .*/
	function append(string text, colorA col)
		char c
		real charWidth
		for n = 0 to text.length()-1
			c = char(text.charAt(n))
			charWidth = fontSize * getFontWidth(c) / getFontResolution() // optimize: write fraction to array instead of width
			if cursor.x + charWidth > maxWidth
				newline()
			if c != char(' ')
				let img = CreateImage(getFontPath(c), fontSize, fontSize, 0, cursor.x, cursor.y, 0, 0, 0, 0, layer castTo int + 1)
					..show()
				if col.alpha != DISABLE_COLOR_ALPHA
					img.setColor(col)
				images.add(img)
			cursor.x += charWidth
			updateDimX()
	
	/** Inserts a line break. */
	function newline()
		cursor.x = pos.x
		cursor.y -= fontSize * getFontHeight() / getFontResolution()
		updateDimY()
	
	/** Moves the cursor horizontally. */
	function hspace(real width)
		cursor.x += width
		updateDimX()
	
	/** Moves the cursor vertically. */
	function vspace(real height)
		cursor.y -= height
		updateDimY()
	
	/** Sets the cursor x position (relative to the TextSplat position). */
	function setCursorX(real x)
		cursor.x = pos.x + x
		updateDimX()
	
	/** Sets the cursor y position (relative to the TextSplat position). */
	function setCursorY(real y)
		cursor.y = pos.y - y
		updateDimY()
	
	/** Returns the cursors position (relative to the TextSplat position). */
	function getCursor() returns vec2
		return vec2(cursor.x - pos.x, pos.y - cursor.y)
	
	/** Clears this TextSplat and removes all its images. */
	function clear()
		images.eraseAll()
		cursor = vec2(pos.y, pos.y - fontSize)
		dimensions = pos

	private function updateDimX()
		if cursor.x > dimensions.x
			dimensions.x = cursor.x
	
	private function updateDimY()
		if cursor.y < dimensions.y
			dimensions.y = cursor.y

class ImageListEntry
	image img
	ImageListEntry prev
	
	construct(image img, ImageListEntry prev)
		this.img = img
		this.prev = prev

class ImageList
	ImageListEntry last
	
	construct()
		last = null
	
	function add(image i)
		last = new ImageListEntry(i, last)
	
	/** Removes all images from the list AND destroys them. */
	function eraseAll()
		var e = last
		while e != null
			e.img.remove()
			destroy e
			e = e.prev
		last = null
		
	function iterator() returns ImageListIterator
		return new ImageListIterator(this)

class ImageListIterator
	ImageListEntry current
	
	construct(ImageList parent)
		current = parent.last
	
	function hasNext() returns boolean
		return current != null
	
	function next() returns image
		let tmp = current
		current = current.prev
		return tmp.img
	
	function close()
		destroy this

Font (generated):
Wurst:
package FontArial
import Char

constant int FONT_HEIGHT = 56
constant int FONT_RESOLUTION = 64
string array paths
int array widths

public function getFontHeight() returns int
	return FONT_HEIGHT

public function getFontResolution() returns int
	return FONT_RESOLUTION

public function getFontPath(char c) returns string
	return paths[c.toInt()]

public function getFontWidth(char c) returns int
	return widths[c.toInt()]

public function initFont(string path)
	paths['!'] = path + "char1.blp"
	paths['.'] = path + "char2.blp"
	paths['#'] = path + "char3.blp"
	paths['$'] = path + "char4.blp"
	paths['%'] = path + "char5.blp"
	paths['&'] = path + "char6.blp"
	paths['''] = path + "char7.blp"
	paths['('] = path + "char8.blp"
	paths[')'] = path + "char9.blp"
	paths['*'] = path + "char10.blp"
	paths['+'] = path + "char11.blp"
	paths[','] = path + "char12.blp"
	paths['-'] = path + "char13.blp"
	paths['.'] = path + "char14.blp"
	paths['/'] = path + "char15.blp"
	paths['0'] = path + "char16.blp"
	paths['1'] = path + "char17.blp"
	paths['2'] = path + "char18.blp"
	paths['3'] = path + "char19.blp"
	paths['4'] = path + "char20.blp"
	paths['5'] = path + "char21.blp"
	paths['6'] = path + "char22.blp"
	paths['7'] = path + "char23.blp"
	paths['8'] = path + "char24.blp"
	paths['9'] = path + "char25.blp"
	paths[':'] = path + "char26.blp"
	paths[';'] = path + "char27.blp"
	paths['<'] = path + "char28.blp"
	paths['='] = path + "char29.blp"
	paths['>'] = path + "char30.blp"
	paths['.'] = path + "char31.blp"
	paths['@'] = path + "char32.blp"
	paths['A'] = path + "char33.blp"
	paths['B'] = path + "char34.blp"
	paths['C'] = path + "char35.blp"
	paths['D'] = path + "char36.blp"
	paths['E'] = path + "char37.blp"
	paths['F'] = path + "char38.blp"
	paths['G'] = path + "char39.blp"
	paths['H'] = path + "char40.blp"
	paths['I'] = path + "char41.blp"
	paths['J'] = path + "char42.blp"
	paths['K'] = path + "char43.blp"
	paths['L'] = path + "char44.blp"
	paths['M'] = path + "char45.blp"
	paths['N'] = path + "char46.blp"
	paths['O'] = path + "char47.blp"
	paths['P'] = path + "char48.blp"
	paths['Q'] = path + "char49.blp"
	paths['R'] = path + "char50.blp"
	paths['S'] = path + "char51.blp"
	paths['T'] = path + "char52.blp"
	paths['U'] = path + "char53.blp"
	paths['V'] = path + "char54.blp"
	paths['W'] = path + "char55.blp"
	paths['X'] = path + "char56.blp"
	paths['Y'] = path + "char57.blp"
	paths['Z'] = path + "char58.blp"
	paths['['] = path + "char59.blp"
	paths['.'] = path + "char60.blp"
	paths[']'] = path + "char61.blp"
	paths['^'] = path + "char62.blp"
	paths['_'] = path + "char63.blp"
	paths['`'] = path + "char64.blp"
	paths['a'] = path + "char65.blp"
	paths['b'] = path + "char66.blp"
	paths['c'] = path + "char67.blp"
	paths['d'] = path + "char68.blp"
	paths['e'] = path + "char69.blp"
	paths['f'] = path + "char70.blp"
	paths['g'] = path + "char71.blp"
	paths['h'] = path + "char72.blp"
	paths['i'] = path + "char73.blp"
	paths['j'] = path + "char74.blp"
	paths['k'] = path + "char75.blp"
	paths['l'] = path + "char76.blp"
	paths['m'] = path + "char77.blp"
	paths['n'] = path + "char78.blp"
	paths['o'] = path + "char79.blp"
	paths['p'] = path + "char80.blp"
	paths['q'] = path + "char81.blp"
	paths['r'] = path + "char82.blp"
	paths['s'] = path + "char83.blp"
	paths['t'] = path + "char84.blp"
	paths['u'] = path + "char85.blp"
	paths['v'] = path + "char86.blp"
	paths['w'] = path + "char87.blp"
	paths['x'] = path + "char88.blp"
	paths['y'] = path + "char89.blp"
	paths['z'] = path + "char90.blp"
	paths['{'] = path + "char91.blp"
	paths['|'] = path + "char92.blp"
	paths['}'] = path + "char93.blp"
	paths['~'] = path + "char94.blp"
	widths['!'] = 13
	widths['.'] = 14
	widths['#'] = 29
	widths['$'] = 28
	widths['%'] = 45
	widths['&'] = 33
	widths['''] = 9
	widths['('] = 17
	widths[')'] = 16
	widths['*'] = 19
	widths['+'] = 30
	widths[','] = 14
	widths['-'] = 18
	widths['.'] = 14
	widths['/'] = 13
	widths['0'] = 28
	widths['1'] = 28
	widths['2'] = 27
	widths['3'] = 27
	widths['4'] = 28
	widths['5'] = 28
	widths['6'] = 28
	widths['7'] = 27
	widths['8'] = 28
	widths['9'] = 28
	widths[':'] = 14
	widths[';'] = 13
	widths['<'] = 29
	widths['='] = 30
	widths['>'] = 29
	widths['.'] = 14
	widths['@'] = 51
	widths['A'] = 33
	widths['B'] = 34
	widths['C'] = 35
	widths['D'] = 36
	widths['E'] = 33
	widths['F'] = 30
	widths['G'] = 39
	widths['H'] = 36
	widths['I'] = 14
	widths['J'] = 24
	widths['K'] = 33
	widths['L'] = 28
	widths['M'] = 42
	widths['N'] = 36
	widths['O'] = 38
	widths['P'] = 33
	widths['Q'] = 39
	widths['R'] = 36
	widths['S'] = 34
	widths['T'] = 30
	widths['U'] = 36
	widths['V'] = 32
	widths['W'] = 46
	widths['X'] = 32
	widths['Y'] = 32
	widths['Z'] = 30
	widths['['] = 13
	widths['.'] = 14
	widths[']'] = 13
	widths['^'] = 23
	widths['_'] = 27
	widths['`'] = 17
	widths['a'] = 28
	widths['b'] = 28
	widths['c'] = 25
	widths['d'] = 28
	widths['e'] = 28
	widths['f'] = 15
	widths['g'] = 28
	widths['h'] = 27
	widths['i'] = 11
	widths['j'] = 7
	widths['k'] = 25
	widths['l'] = 11
	widths['m'] = 41
	widths['n'] = 27
	widths['o'] = 28
	widths['p'] = 28
	widths['q'] = 28
	widths['r'] = 17
	widths['s'] = 25
	widths['t'] = 14
	widths['u'] = 28
	widths['v'] = 24
	widths['w'] = 35
	widths['x'] = 24
	widths['y'] = 24
	widths['z'] = 25
	widths['{'] = 16
	widths['|'] = 14
	widths['}'] = 16
	widths['~'] = 29
	// Space:
	widths[' '] = 29

init
	initFont("war3mapImported\\")
 

Attachments

  • textsplat.jpg
    textsplat.jpg
    147.1 KB · Views: 230
  • TextSplat.w3x
    162.4 KB · Views: 83
Last edited:
Level 14
Joined
Jun 27, 2008
Messages
1,325
Texttags are rendered on the screen, so if you scroll in they take up the same size on the screen (in pixels). TextSplats are rendered onto the terrain, so if you zoom in the text will be magnified too. This has the advantage that you can create text that always looks the same (compared to the terrain), no matter how you move your camera.
Also normal texttags have some problems, like looking different on different screen resolutions, limit to 100 instances,... this resource aims to provide an alternative.

This has been done before, for vJass:
http://www.wc3c.net/showthread.php?t=87798
 
Level 8
Joined
Nov 20, 2011
Messages
202
This is realy great and well written!

Maybe it is better to fill the paths array with an loop to lower mapscriptsize?!

You should also check out if there is the possibility to increase fps by:
-adjusting mipmap count
-lower texture resolution
-try different blp1 and blp2 configurations
 
You should also check out if there is the possibility to increase fps by:
-adjusting mipmap count
-lower texture resolution
-try different blp1 and blp2 configurations

Sure it would be nice if muzzel found the optimal settings, however I think the quality ratio should be decided by the user.

As for the code, I personally would explicitly state the handle types (image) for better readability.
 
Status
Not open for further replies.
Top