Author Topic: Texture Atlas - DarkFunction importer  (Read 5963 times)

Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Is anyone using an app to create Texture Atlas with GLBasic?  If so; just wondering what is a good product for this?

And along those same lines; is anyone doing any type of scripted sprite management?  Creating some type of sprite/entity TYPE then storing the data for each sprite in files and the scripting the load process indirectly through these files?
« Last Edit: 2011-Sep-14 by Kitty Hello »

Offline Slydog

  • Prof. Inline
  • *****
  • Posts: 929
  • KodeSource
    • View Profile
    • KodeSource
Re: Texture Atlas?
« Reply #1 on: 2011-Sep-13 »
A few weeks ago I found a great sprite atlas manager called 'darkFunction', here:
http://darkfunction.com/

They only have a 'free' version, although they mention a 'pro' version is coming soon.

It outputs an XML file that looks like this:
http://darkfunction.com/index.php?id=documentation

I love how it lets you define hierarchies for the sprites so you can categorize them in code such as 'gui/button', 'gui/label', 'enemy/ufo', etc!

I wrote a TYPE and function to read this file. (I originally was going to use the XML reader I found in the forums, but hard coded it instead!).  I included my other TYPES and supporting functions if you want to see how I manage everything.

Here's my TSprite and TSpriteSheet TYPEs: (TSpriteSheet.Load() is the code to load the XML file)
Code: GLBasic [Select]
CONSTANT POLYMODE_STRIP = 2

TYPE TSprite
        name$
        uv                      AS TXyXy
        colour%

        FUNCTION Draw: x%, y%, w%=-1, h%=-1, colour%=BLANK, stretch%=TRUE, scale#=1.0
                LOCAL ix%, iy%
                LOCAL xy AS TXyXy
                LOCAL uv_size AS TSize
                xy.x1 = x
                xy.y1 = y
                IF w < 0 THEN w = (self.uv.x2 - self.uv.x1)
                IF h < 0 THEN h = (self.uv.y2 - self.uv.y1)
                w = w * scale
                h = h * scale
                xy.x2 = x + w
                xy.y2 = y + h
                IF colour = BLANK THEN colour = self.colour

                IF stretch
                        Poly_Draw(xy, self.uv, colour)
                // or else tile the image to fit boundary
                ELSE
                        uv_size.Set(self.uv.x2 - self.uv.x1, self.uv.y2 - self.uv.y1)
                        FOR ix = 0 TO (w / uv_size.w)
                                xy.x1 = ix * uv_size.w + x
                                xy.x2 = xy.x1 + uv_size.w
                                FOR iy = 0 TO (h / uv_size.h)
                                        xy.y1 = iy * uv_size.h + y
                                        xy.y2 = xy.y1 + uv_size.h
                                        Poly_Draw(xy, self.uv, colour)
                                NEXT
                        NEXT
                ENDIF
        ENDFUNCTION

ENDTYPE


TYPE TSpriteSheet
        fn_image$                                       // Filename of image
        id_sprite%                                      // Sprite ID of image
        size            AS TSize                // Size of image
        sprites[]       AS TSprite

        FUNCTION Load%: path$, fn$
                LOCAL ix%
                LOCAL fh%                                                                                                                               // File Handle
                LOCAL line$
                LOCAL tags$[]
                LOCAL key$
                LOCAL value$
                LOCAL name$
                LOCAL sprite AS TSprite

                self.fn_image$ = ""
                self.id_sprite = 0
                self.size.Set(0,0)
                DIM self.sprites[0]
                path$ = TRIM$(path$)

                fh = GENFILE()
                IF OPENFILE(fh, path$ + fn$, 1) = FALSE                                                                                 // Open SpriteSheet file
                        DEBUG ">TSpriteSheet.Load(): [" + fn$ + "] -> Error opening file\n"
                        RETURN FALSE                                                                                                            // Exit and return 'FALSE' if can't open
                ENDIF

                WHILE ENDOFFILE(fh) = FALSE
                        READLINE fh, line$                                                                                                      // Read next line of data for the next shortacter
                        line$ = TRIM$(line$)
                        line$ = MID$(line$, 1)                                                                                          // Strip off first '<' character
                        line$ = LEFT$(line$, LEN(line$) - 1)                                                            // Strip off last '>' character
                        IF RIGHT$(line$, 1) = "/" THEN line$ = LEFT$(line$, LEN(line$) - 1)     // Strip off last '/' character if present
                        SPLITSTR(line$, tags$[], " ", TRUE)
                        IF LEN(tags$[]) < 1 THEN CONTINUE                                                                       // Ignore elements with no tags
                        tags$[0] = TRIM$(UCASE$(tags$[0]))
                        SELECT tags$[0]

                        CASE "DIR"
                                // Image FileName
                                String_KeyValue(tags$[1], key$, value$)
                                value$ = UCASE$(TRIM$(value$))
                                IF value$ <> "/" THEN name$ = name$ + value$
                                name$ = name$ + "/"

                        CASE "/DIR"
                                ix = REVINSTR(name$, "/", LEN(name$)-2)
                                IF ix >= 0
                                        name$ = LEFT$(name$, ix+1)
                                ENDIF

                        CASE "IMG"
                                // Image FileName
                                String_KeyValue(tags$[1], key$, value$)
                                self.fn_image$ = path$ + value$
                                // Image Width
                                String_KeyValue(tags$[2], key$, value$)
                                self.size.w = INTEGER(value$)
                                // Image Height
                                String_KeyValue(tags$[3], key$, value$)
                                self.size.h = INTEGER(value$)
                                DEBUG "  >IMG: fn:[" + self.fn_image$ + "] w:[" + self.size.w + "] h:[" + self.size.h + "]\n"

                        CASE "SPR"
                                // Sprite Name
                                String_KeyValue(tags$[1], key$, value$)
                                sprite.name$ = name$ + UCASE$(value$)
                                // Sprite X
                                String_KeyValue(tags$[2], key$, value$)
                                sprite.uv.x1 = INTEGER(value$)
                                // Sprite Y
                                String_KeyValue(tags$[3], key$, value$)
                                sprite.uv.y1 = INTEGER(value$)
                                // Sprite W
                                String_KeyValue(tags$[4], key$, value$)
                                sprite.uv.x2 = sprite.uv.x1 + INTEGER(value$)
                                // Sprite H
                                String_KeyValue(tags$[5], key$, value$)
                                sprite.uv.y2 = sprite.uv.y1 + INTEGER(value$)
                                sprite.colour = RGB(255,255,255)
                                DIMPUSH self.sprites[], sprite
                                //[EDIT] removed following line as the .Dump$ function isn't included as it uses other formatting functions not included
                                //DEBUG "  >SPR: name:[" + sprite.name$ + "] xywh:" + sprite.uv.Dump$() + "\n"

                        ENDSELECT
                WEND
                CLOSEFILE fh                                                                                                                    // Close '.fnt' file

                self.id_sprite = Sprite_Load(self.fn_image$, FALSE)
        ENDFUNCTION

        FUNCTION PolyStart:
                STARTPOLY self.id_sprite, POLYMODE_STRIP
        ENDFUNCTION

        FUNCTION PolyEnd:
                ENDPOLY
        ENDFUNCTION

        FUNCTION Find%: name$
                LOCAL sx%
                name$ = UCASE$(TRIM$(name$))
                FOR sx = 0 TO LEN(self.sprites[]) - 1
                        IF INSTR(self.sprites[sx].name$, name$) >= 0
                                DEBUG "Sprite.Find(): [" + name$ + "], sx:[" + sx + "]\n"
                                RETURN sx
                        ENDIF
                NEXT
                RETURN 0
        ENDFUNCTION

        FUNCTION Draw%: id%, x%, y%, w%=-1, h%=-1, colour%=BLANK, stretch%=TRUE, scale#=1.0
                IF id < 0 THEN RETURN
                IF id > LEN(self.sprites[])-1 THEN RETURN
                self.sprites[id].Draw(x, y, w, h, colour, stretch, scale)
        ENDFUNCTION

        // Use this function to return the UV values to use for a 3D mesh quad, not used for 2D.
        // 'is_top_left' = TRUE :: returns the top/left UV point, or else it returns the bottom/right UV point.
        FUNCTION CalcUv AS TUv: id_sprite%, is_top_left%=TRUE
                LOCAL uv AS TUv
                LOCAL sw#, sh#
                sw = self.size.w
                sh = self.size.h
                IF is_top_left
                        uv.Set(self.sprites[id_sprite].uv.x1 / sw, self.sprites[id_sprite].uv.y1 / sh)
                ELSE
                        uv.Set(self.sprites[id_sprite].uv.x2 / sw, self.sprites[id_sprite].uv.y2 / sh)
                ENDIF
                RETURN uv
        ENDFUNCTION

ENDTYPE
 

Supporting TYPEs:
Code: GLBasic [Select]
TYPE TSize
        w%
        h%

        FUNCTION Set%: w%, h%
                self.w = w
                self.h = h
        ENDFUNCTION
ENDTYPE

TYPE TUv
        u#
        v#

        FUNCTION Set: u#, v#
                self.u = u
                self.v = v
        ENDFUNCTION
ENDTYPE

TYPE TXyXy
        x1%
        y1%
        x2%
        y2%

        FUNCTION Set: x1%, y1%, x2%, y2%
                self.x1 = x1
                self.y1 = y1
                self.x2 = x2
                self.y2 = y2
        ENDFUNCTION

        FUNCTION Clear:
                self.x1 = 0
                self.x2 = 0
                self.y1 = 0
                self.y2 = 0
        ENDFUNCTION

        FUNCTION Move: point AS TPoint
                INC self.x1, point.x
                INC self.x2, point.x
                INC self.y1, point.y
                INC self.y2, point.y
        ENDFUNCTION

ENDTYPE
 

Supporting Functions:
Code: GLBasic [Select]
CONSTANT BLANK% = -32760

FUNCTION Sprite_Load%: fn$, is_bump%=FALSE
        LOCAL id%
        IF DOESFILEEXIST(fn$)
                id = Sprite_GetNextId()
                DEBUG ">GFX:{Sprite_Load     } fn:[" + fn$ + "] | id:[" + id + "]\n"
                IF is_bump
                        LOADBUMPTEXTURE fn$, id
                ELSE
                        LOADSPRITE fn$, id
                ENDIF
                RETURN id
        ELSE
                DEBUG ">GFX:{Sprite_Load     } *** File NOT FOUND!: [" + fn$ + "]\n"
                RETURN 1
        ENDIF
ENDFUNCTION

// Find free sprite slot
FUNCTION Sprite_GetNextId%:
        LOCAL id% = 1
        LOCAL sx%, sy%
        GETSPRITESIZE id, sx, sy
        WHILE sx>0 OR sy>0
                INC id
                IF id > 10000 THEN RETURN 1
                GETSPRITESIZE id, sx, sy
        WEND
        RETURN id
ENDFUNCTION


FUNCTION Poly_Draw: xy AS TXyXy, uv AS TXyXy, colour%, offset_x%=0, offset_y%=0
        POLYNEWSTRIP
        POLYVECTOR xy.x1 + offset_x, xy.y1 + offset_y, uv.x1, uv.y1, colour             // TL
        POLYVECTOR xy.x1 + offset_x, xy.y2 + offset_y, uv.x1, uv.y2, colour             // BL
        POLYVECTOR xy.x2 + offset_x, xy.y1 + offset_y, uv.x2, uv.y1, colour             // TR
        POLYVECTOR xy.x2 + offset_x, xy.y2 + offset_y, uv.x2, uv.y2, colour             // BR
ENDFUNCTION



FUNCTION String_KeyValue%: string$, BYREF keyword$, BYREF value$, strip_quotes%=TRUE
        LOCAL ix%
        ix = INSTR(string$, "=")
        IF ix < 0
                keyword$ = ""
                value$   = ""
        ELSE
                keyword$ = UCASE$(TRIM$(LEFT$(string$, ix)))  // Fixed bug pointed out by bigtunacan
                value$   =        TRIM$( MID$(string$, ix+1))
        ENDIF
        IF strip_quotes
                IF LEFT$(value$, 1) = CHR$(34)  THEN value$ = MID$(value$, 1)
                IF RIGHT$(value$, 1) = CHR$(34) THEN value$ = LEFT$(value$, LEN(value$)-1)
        ENDIF
ENDFUNCTION
 

Sample code usage:
Code: GLBasic [Select]
GLOBAL sprites AS TSpriteSheet
GLOBAL sp_gui_button%

// Load and initialize the global Sprite Sheet
sprites.Load("Graphics/", "sprites.sprites")

// Locate and define the 'button' gui element
sp_gui_button = sprites.Find("Gui/Button")

// In main loop . . .

// Start new OpenGL Material / Texture
sprites.PolyStart()

// Draw all your sprites from this sprite sheet here . . .
sprites.Draw(sp_gui_button, 10, 10, -1, -1, RGB(0,0,250))
sprites.Draw(sp_gui_button, 10, 90, -1, -1, RGB(250,0,0))

// Close Texture
sprites.PolyEnd()
 

I think I included all required code and functions, but I may be missing something.

[Edit]
I should add that 'darkFunction' also has a decent animation manager too, and outputs a separate animation XML file.
I haven't created an associated TYPE yet for this as I currently have no need, but it seems like a trivial task. 
It would use the TSprite and TSpriteSheet TYPES for managing the sprite portion, and it would just handle loading the XML data and handling frame state (and which sprite ids from the Sprite Sheet), etc.

[Edit2] Fixed bug pointed out by bigtunacan
« Last Edit: 2011-Sep-21 by Slydog »
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
Re: Texture Atlas?
« Reply #2 on: 2011-Sep-14 »
I'll give it a shot! Nice find!

Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Re: Texture Atlas?
« Reply #3 on: 2011-Sep-14 »
Thanks for the reply and double thanks for the loader code!


Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #4 on: 2011-Sep-21 »
@Slydog,

There is a bug in your String_KeyValue% function; I have it listed as line 6 below. You should instead set keyword$ as

Code: GLBasic [Select]
keyword$ = UCASE$(TRIM$(LEFT$(string$, ix)))
 

Currently it has an off by one where the last letter is cut off of the keyword; this is because both INSTR() and MID() are using a zero based index; but LEFT is just a strict number of characters.

Code: GLBasic [Select]
1:: ix = INSTR(string$, "=")
2:: IF ix < 0
3::     keyword$ = ""
4::     value$   = ""
5:: ELSE
6::     keyword$ = UCASE$(TRIM$(LEFT$(string$, ix-1)))
7::     value$   =        TRIM$( MID$(string$, ix+1))
8:: ENDIF
 


Offline Slydog

  • Prof. Inline
  • *****
  • Posts: 929
  • KodeSource
    • View Profile
    • KodeSource
Re: Texture Atlas - DarkFunction importer
« Reply #5 on: 2011-Sep-21 »
Oops! 
Thanks for pointing that out.
I'll update my code.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Offline AndreasLoew

  • Mc. Print
  • *
  • Posts: 5
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #6 on: 2011-Sep-21 »
Hi,

I have a tool called TexturePacker (http://www.texturepacker.com). It's a sprite atlas creator and image optimizer with a big load of features.
I would be happy to add an exporter for GLBasic.
If anybody is interested in helping me (since I am no GLBasic expert) I would give a free license as reward.

Please contact me at support <mail-symbol> code-and-web.de

Cheers
Andreas
« Last Edit: 2011-Sep-21 by Kitty Hello »

Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #7 on: 2011-Sep-21 »
Andreas,

I just wrote an importer for your tool yesterday that I was planning to make public here anyway once I get some more features implemented.  I have sent you an email regarding this.

Thanks

Offline AndreasLoew

  • Mc. Print
  • *
  • Posts: 5
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #8 on: 2011-Sep-21 »
Nice ;-)

Which exporter did you use?

Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #9 on: 2011-Sep-21 »
CEGUI/OGRE.  I'm using the essential version of Texture Packer currently; so I have only tested with standard block layout of texture atlas; may need some tweaks to get it working with rotated/better packed texture atlas, but I don't currently have access to the full version of your tool.

Offline AndreasLoew

  • Mc. Print
  • *
  • Posts: 5
    • View Profile

Offline bigtunacan

  • Mr. Polyvector
  • ***
  • Posts: 195
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #11 on: 2011-Sep-21 »
Submitted

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #12 on: 2011-Sep-21 »
Very handy. Having to manually edit a texture atlas in a photo editor is horribly time consuming.

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2568
    • View Profile
Re: Texture Atlas - DarkFunction importer
« Reply #13 on: 2011-Sep-29 »
Almost missed this, nice post and tool!  :good:
Cheers,

Ian.

“It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration.”
(E. W. Dijkstra)

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
Re: Texture Atlas - DarkFunction importer
« Reply #14 on: 2011-Dec-12 »
Nice I'm using it from now! (Dark Function importer)