1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Lead your forces to battle in the 15th Techtree Contest. The call is yours, commander!
    Dismiss Notice
  4. The reforging of the races is complete. Come see the 14th Techtree Contest Results.
    Dismiss Notice
  5. It's time to choose your horse in the race - the 32nd Modeling Contest Poll is up!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Wurst] TextSplat

Discussion in 'The Lab' started by muzzel, Feb 7, 2014.

  1. muzzel

    muzzel

    Joined:
    Jun 27, 2008
    Messages:
    1,303
    Resources:
    2
    JASS:
    1
    Wurst:
    1
    Resources:
    2
    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:
    Code (WurstScript):

    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):
    Code (WurstScript):
    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\\")
     
     

    Attached Files:

    Last edited: Feb 7, 2014
  2. gorillabull

    gorillabull

    Joined:
    Jul 17, 2011
    Messages:
    1,368
    Resources:
    2
    Spells:
    2
    Resources:
    2
    um i fairly certain that can be done with just regular text and not thousands of imports oh yea the text just follows the player's camera this is very cool
     
  3. muzzel

    muzzel

    Joined:
    Jun 27, 2008
    Messages:
    1,303
    Resources:
    2
    JASS:
    1
    Wurst:
    1
    Resources:
    2
    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
     
  4. Crigges

    Crigges

    Joined:
    Nov 20, 2011
    Messages:
    201
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    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
     
  5. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,758
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    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.
     
  6. muzzel

    muzzel

    Joined:
    Jun 27, 2008
    Messages:
    1,303
    Resources:
    2
    JASS:
    1
    Wurst:
    1
    Resources:
    2
    What?
     
  7. Crigges

    Crigges

    Joined:
    Nov 20, 2011
    Messages:
    201
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    shoud be cursor = vec2(pos.x, pos.y - fontSize) instead