Polyvector Question

Previous topic - Next topic

fuzzy70

I have finally got round to playing with the POLYVECTOR command and have a couple of questions. Here is a snippet of my current code
Code (glbasic) Select

// csz=character size in pixels, pchar=the character to plot, colour[]= array of colours
FOR y=0 TO 24
FOR x=0 TO 39

STARTPOLY 0
  POLYVECTOR   (x*csz)*2,       (y*csz)*2,       pchar*csz,      0,   colour[2]
  POLYVECTOR   (x*csz)*2,      ((y*csz)+csz)*2,  pchar*csz,      csz, colour[2]
  POLYVECTOR  ((x*csz)+csz)*2, ((y*csz)+csz)*2, (pchar*csz)+csz, csz, colour[2]
  POLYVECTOR  ((x*csz)+csz)*2,  (y*csz)*2,      (pchar*csz)+csz, 0,   colour[2]
ENDPOLY

NEXT
NEXT


Imagine a text display of 40x25 chars setup as an array DISPLAY[40][25] (ignoring the fact that the above code will just print the same char at the moment). The characters are stored as an image strip 2048px x 8px & the colours are stored in the colour array as I wanted different coloured text without having to create a bitmap font for each colour. After I got my head around the texture co-ords to find the right char the above code works fine, now for the questions  :D


  • Is the above method the best/right way to do it with polyvectors, i.e using polymode 0
  • If not would using mode 2 (strip) be a better way of doing it & if so can POLYNEWSTRIP be used in a loop otherwise I would end up with 40 blocks of polyvector code per line  :D
[li]
[/li][/list]

Lee
"Why don't you just make ten louder and make ten be the top number and make that a little louder?"
- "These go to eleven."

This Is Spinal Tap (1984)

Slydog

I use the 'strip' mode using a constant I defined:
Code (glbasic) Select
CONSTANT POLYMODE_STRIP = 2

I simplified my POLYVECTOR code by using a common function:
Code (glbasic) Select
FUNCTION Poly_Draw: xy AS TXyXy, uv AS TXyXy, colour%
POLYNEWSTRIP
POLYVECTOR xy.x1, xy.y1, uv.x1, uv.y1, colour // TL
POLYVECTOR xy.x1, xy.y2, uv.x1, uv.y2, colour // BL
POLYVECTOR xy.x2, xy.y1, uv.x2, uv.y1, colour // TR
POLYVECTOR xy.x2, xy.y2, uv.x2, uv.y2, colour // BR
ENDFUNCTION

// It uses this TYPE:
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
ENDTYPE


Then, just start with a "STARTPOLY sprite_id, POLYMODE_STRIP" before calling the 'POLY_DRAW()' function for each polygon sharing this sprite id.

Also, I would start and end a new polygon pair for each letter / character.  That way you can control the letter spacing.  Having one long strip of polyvectors may use less memory / processing, but offers you no spacing options.

I would create my font file in a square multiple of two size file.  Since fonts are twice as high as tall, and by only using the 1st 128 characters, this will give you a square file.  Then indexing each character should be easy by its ASCII value. 
The square image is also required / recommended if you are planing to target certain portable devices.

Or better yet, use the font tools and libraries found on this forum for defining where each letter is in the font file, and using these values in your POLYVECTOR routines.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

fuzzy70

Thanks Slydog,

Sorry for this question but I must be having a bad day for things "sinking in"  O_O . Using characters as my example, Am I right in assuming you are using the TYPE to store where each char is located in the sprite, or are you using the type to store what polyvectors need to be drawn. Again sorry if it's a dumb question, I think I got out of bed the wrong side today as even simple things are not going to plan  :D

Lee
"Why don't you just make ten louder and make ten be the top number and make that a little louder?"
- "These go to eleven."

This Is Spinal Tap (1984)

Slydog

Actually, I use that TYPE in a lot of places.
But yes, I use it to store each character's x,y location in the sprite file (as long as its width and height in the 2nd x,y pair).
I read this in from a pre-generated text file.  This allows for proportional fonts too, with no 'width' hard coding.

Also, text tends to be static, so when I call my 'print' routine, I calculate each character's location on the screen for that string, and store it into a character / string array, using that TYPE.  This makes each frame's text displaying very simple as everything is pre-calculated.  I only have to update the locations each frame for dynamic strings such as score and time.

This saves me processing time by not figuring each character's position out each frame, especially for more demanding algorithms such as right justify, center, or full justify, plus line wrapping, etc.

My font library is quite complicated for this reason, as I was going for speed, not simplicity.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

fuzzy70

Thanks again, that makes perfect sense now  =D

I will have a play around with that later as like I said earlier things are not going good today  :D . For example, I spent 30mins trying to figure out why on earth my chars had a black background until I noticed that I had forgotten to save them with a blank alpha background  :noggin:

Think I will go & watch a film & come back to this later when hopefully my brain is in gear.

Lee
"Why don't you just make ten louder and make ten be the top number and make that a little louder?"
- "These go to eleven."

This Is Spinal Tap (1984)

Slydog

If you're interested, I use 'Bitmap Font Generator':
http://www.angelcode.com/products/bmfont/

It lets you define a sprite file, plus saves each character's location to a file.
There is a better one on these forums that is similar to the 'DiNGSFont' creator built into GLBasic, but i was already done my routines before that came out.  (I'm not sure if the new one creates a character location text file).

If you're curious about my font library, here it is:
(As is it wont work as it relies on a ton of my other libraries, but it may give you an idea of what needs to be done to create your own library.)
Oh, and it doesn't use my TXyXy, but two types TPoint (just x,y), and TSize (w,h).  I must later calculate a TXyXy variable for using my Poly_Draw() function).

I load the BMFont text file in the TGlyph.New() routine, if you want to see how I parse it out.
Warning, this has been rewritten a few times and code is getting very messy.
Code (glbasic) Select
CONSTANT FONT_TEXTURE_QTY_X = 16 // How many glyphs horizontally in texture
CONSTANT FONT_TEXTURE_QTY_Y = 8 // How many glyphs vertically in texture

CONSTANT FONT_TITLE = 0
CONSTANT FONT_BODY = 1
CONSTANT FONT_MISC = 2
CONSTANT FONT_TOTAL = 2

GLOBAL FONT_PATH$ = "Graphics/Fonts/" // Path to 'Fonts' folder

GLOBAL _text_offset AS TPoint





TYPE TGlyphChar
xy AS TPoint // x,y position of character in font texture
size AS TSize // Size (w,h) of character
ENDTYPE

TYPE TGlyph
id% // Font ID (and the sprite that holds this font texture)
sprite_size  AS TSize // Width / Height of font texture
spacing%
height% // General height of a font character
chars[] AS TGlyphChar

FUNCTION New%: fn$, height%, spacing%, space_size%
//LOCAL fh% // File Handle
LOCAL font AS TGlyph // Temporary 'TGlyph' type
LOCAL ascii% // Ascii value of current character
LOCAL line$ // Current line data from '.fnt' file
LOCAL pos AS TPoint
LOCAL size AS TSize
LOCAL file AS TFile
fn$ = FONT_PATH$ + fn$

//LOG(">Font_New: " + fn$ + ", id:" + font_id)
//clear prev sprite
self.id = Sprite_Load(fn$ + ".png") // Load font texture sprite
//LOG("font.id: " + font.id)
self.spacing = spacing // Default spacing between characters
self.height = height // Default character height
Sprite_GetSize(self.id, self.sprite_size) // Get font texture width, height
size.w = self.sprite_size.w / FONT_TEXTURE_QTY_X
size.h = self.sprite_size.h / FONT_TEXTURE_QTY_Y
DIM self.chars[ASC_MAX + 1] // Initialize character array

// --------------------------------------------------------------------
// PARSE ".DAT" FILE
// --------------------------------------------------------------------
IF NOT file.Open(FILE_MODE_READ, fn$ + ".dat") // Open font's '.fnt' file
LOG(">FON:{TGlyph.New%     } *** Error Opening Glyph File: [" + fn$ + ".dat" + "] ***")
RETURN FALSE // Exit and return 'FALSE' if can't open
ENDIF

// Read and parse character data from file
REPEAT
line$ = file.GetLine$()
IF file.eof THEN BREAK // End of file
IF LEN(line$) <= 0 THEN CONTINUE // Skip blank lines
ascii = ASC(MID$(line$, 0, 1)) // Ascii value
IF (ascii < 0) OR (ascii > ASC_MAX) THEN CONTINUE // Ascii value is out of range, skip to next line
self.chars[ascii].xy.x = MID$(line$,  2, 3) // [X Position]
INC self.chars[ascii].xy.x, MOD(ascii, FONT_TEXTURE_QTY_X) * size.w
self.chars[ascii].xy.y = MID$(line$,  6, 3) // [Y Position]
INC self.chars[ascii].xy.y, INTEGER(ascii / FONT_TEXTURE_QTY_X) * size.h
self.chars[ascii].size.w = MID$(line$,  10, 3) // [Width]
self.chars[ascii].size.h = MID$(line$,  14, 3) // [Height]
UNTIL file.eof
file.Close()
self.chars[32].xy.x = 0
self.chars[32].size.w = space_size
//Debug_Font(font)

RETURN TRUE // Font added successfully
ENDFUNCTION

ENDTYPE
GLOBAL _glyphs[] AS TGlyph // FONT_TITLE





TYPE TTextStyle
font_id%
justify% = JUSTIFY_CENTER
spacing% // Padding between each character
margin AS TXyXy
colour AS TPolyColour

FUNCTION Copyasd%: style AS TTextStyle
self.font_id = style.font_id
self.spacing = style.spacing
self.justify = style.justify
self.margin.Set(style.margin.x1, style.margin.x2, style.margin.y1, style.margin.y2)
self.colour.Set(style.colour.TL, style.colour.TR, style.colour.BL, style.colour.BR)
ENDFUNCTION

FUNCTION SetMargin%: left%, top%, right%, bottom%
self.margin.Set(left, top, right, bottom)
ENDFUNCTION

FUNCTION SetColour%: rgb_tl%, rgb_tr%=-1, rgb_bl%=-1, rgb_br%=-1
self.colour.Set(rgb_tl, rgb_tr, rgb_bl, rgb_br)
ENDFUNCTION

FUNCTION Clear%:
self.font_id = BLANK
self.margin.Set(BLANK, BLANK, BLANK, BLANK)
self.justify = JUSTIFY_CENTER
self.spacing = 0
self.colour.Clear()
ENDFUNCTION

ENDTYPE

TYPE TTextLine
ascii%[]
xy AS TPoint
ENDTYPE

TYPE TText
enabled%
string$
style AS TTextStyle
lines[] AS TTextLine
pos AS TXyWh
justify%

FUNCTION New%: string$, style AS TTextStyle, pos AS TXyWh, colour AS TPolyColour, justify%=BLANK, spacing%=BLANK
self.enabled = TRUE
self.string$ = string$
self.pos.Set(pos.x, pos.y, pos.w, pos.h)
self.style = style
IF colour.TL <> BLANK THEN self.style.colour = colour // Override default style
IF justify <> BLANK THEN self.style.justify = justify // Override default style
IF spacing <> BLANK THEN self.style.spacing = spacing // Override default style
self.AsciiSet(string$)
ENDFUNCTION

FUNCTION Clear%:
self.enabled = FALSE
self.string$ = ""
self.style.Clear()
DIM self.lines[0]
ENDFUNCTION

FUNCTION Update: enabled%, string_new$, colour_new AS TPolyColour, x_new%=BLANK, y_new%=BLANK
self.enabled = enabled // Update 'ENABLED'
IF string_new$ <> "" THEN self.AsciiSet(string_new$) // New 'STRING'?
IF (x_new <> BLANK) THEN self.pos.x = x_new // New 'X POS'?
IF (y_new <> BLANK) THEN self.pos.y = y_new // New 'Y POS'?
IF (colour_new.TL <> BLANK) THEN self.style.colour = colour_new // New 'COLOUR'?
ENDFUNCTION

FUNCTION Draw: colour AS TPolyColour
LOCAL fx%
LOCAL lx%, ax%
LOCAL y_pos%
LOCAL cursor AS TPoints
LOCAL uv AS TPoints

cursor.xy1.Set(self.pos.x, 0) // Start drawing from this x,y point
IF (colour=BLANK_POLY_COLOUR) THEN colour = self.style.colour

// Step thru each line in text message
FOR lx = 0 TO BOUNDS(self.lines[], 0) - 1
cursor.xy1.x = self.pos.x + self.lines[lx].xy.x + _text_offset.x
y_pos = self.pos.y + self.lines[lx].xy.y + _text_offset.y
// Step thru each ascii character in the current line
FOR ax = 0 TO BOUNDS(self.lines[lx].ascii[], 0) - 1
LOCAL cx%
cx = self.lines[lx].ascii[ax]
cursor.xy1.y = y_pos
cursor.xy2.y = cursor.xy1.y + _glyphs[self.style.font_id].chars[cx].size.h
cursor.xy2.x = cursor.xy1.x + _glyphs[self.style.font_id].chars[cx].size.w
uv.xy1 = _glyphs[self.style.font_id].chars[cx].xy
uv.xy2 = uv.xy1
INC uv.xy2.x, _glyphs[self.style.font_id].chars[cx].size.w
INC uv.xy2.y, _glyphs[self.style.font_id].chars[cx].size.h
Poly_Draw_Gradient(cursor, uv, colour)
INC cursor.xy1.x, _glyphs[self.style.font_id].chars[cx].size.w + self.style.spacing
NEXT
NEXT
ENDFUNCTION


// Fill 'ascii[]' array from 'string$'
FUNCTION AsciiSet: string$
LOCAL sx%, ax%, bx%
LOCAL ascii%
LOCAL ascii_word%
LOCAL line AS TTextLine
LOCAL word_width% = 0
LOCAL line_width% = 0
LOCAL b_newline% = TRUE
LOCAL y_pos%
DIM self.lines[0]
y_pos = self.style.margin.y1

FOR sx = 0 TO LEN(string$)
// Add current line to list?
IF b_newline = TRUE
IF line_width > 0
DEC line_width, self.style.spacing
ENDIF

SELECT self.style.justify
CASE JUSTIFY_LEFT
line.xy.x = self.style.margin.x1
CASE JUSTIFY_CENTER
IF self.pos.w > 0
line.xy.x = (self.pos.w - line_width) / 2
ELSE
line.xy.x = -(line_width / 2)
ENDIF
CASE JUSTIFY_RIGHT
IF self.pos.w > 0
line.xy.x = self.pos.w - line_width - self.style.margin.x2
ELSE
line.xy.x = -line_width
ENDIF
ENDSELECT
line.xy.y = y_pos
IF line_width > 0
DIMPUSH self.lines[], line
INC y_pos, _glyphs[self.style.font_id].height
ENDIF
IF sx = LEN(string$) THEN BREAK
DIM line.ascii[0]
ax = 0
line_width = 0
b_newline = FALSE
ENDIF

ascii% = ASC(MID$(string$, sx, 1))
ascii = self.AsciiValidate(ascii, self.style.font_id) // Validate ASCII value

// New Line Ascii?
IF ascii = ASC_NEWLINE
b_newline = TRUE
CONTINUE
ENDIF

REDIM line.ascii[ax+1]

IF (self.pos.w > 0) AND (ascii = 32)
// Calculate width of next 'word'
word_width = 0
FOR bx = sx + 1 TO LEN(string$) - 1
ascii_word = ASC(MID$(string$, bx, 1))
ascii_word = self.AsciiValidate(ascii_word, self.style.font_id) // Validate ASCII value
IF ascii_word <> 32
INC word_width, _glyphs[self.style.font_id].chars[ascii_word].size.w
// Only add text spacing if NOT last character
IF bx < LEN(string$) - 1 THEN INC word_width, self.style.spacing
ENDIF
IF (ascii_word = 32) OR (ascii_word = ASC_NEWLINE) THEN BREAK // End of word?
NEXT

// Is the next word too wide?
IF (line_width + word_width) > self.pos.w - self.style.margin.x2 - self.style.margin.x1
b_newline = TRUE
CONTINUE
ENDIF
ENDIF

line.ascii[ax] = ascii
INC ax

INC line_width, _glyphs[self.style.font_id].chars[ascii].size.w + self.style.spacing

// If last character, finish off last line
IF sx = LEN(string$) - 1 THEN b_newline = TRUE
NEXT
ENDFUNCTION

FUNCTION AsciiValidate%: ascii%, font_id%
IF font_id >= FONT_TOTAL THEN RETURN
ALIAS font AS _glyphs[font_id]
IF (ascii >= ASC_A) AND (ascii <= ASC_Z) AND (font.chars[ascii].size.w <= 0) THEN INC ascii, (ASC_a - ASC_A) // If a CAPTIAL letter, and no ascii defined for that, use a SMALL CASE version
IF (ascii >= ASC_a) AND (ascii <= ASC_z) AND (font.chars[ascii].size.w <= 0) THEN DEC ascii, (ASC_a - ASC_A) // If a SMALL letter, and no ascii defined for that, use a CAPITIAL version
IF font.chars[ascii].size.w <= 0 THEN ascii = 32 // Ascii not defined in font?  -> Default to 'space'
IF ascii > ASC_MAX THEN ascii = 32 // Aascii value out of range?  -> Default to 'space'
RETURN ascii
ENDFUNCTION

ENDTYPE


GLOBAL _font_active%

FUNCTION Font_Load%: font_ix%, fn$, height%, spacing%, space_size%
IF font_ix >= FONT_TOTAL THEN RETURN
_glyphs[font_ix].New(fn$, height, spacing, space_size)
ENDFUNCTION

FUNCTION Font_PrintStart%: font_ix%
_font_active = font_ix%
ALPHAMODE -1.0
STARTPOLY _glyphs[font_ix].id, POLYMODE_STRIP
ENDFUNCTION

FUNCTION Font_PrintEnd%:
_font_active = BLANK
ENDFUNCTION

FUNCTION Font_TextOffset: offset AS TPoint
_text_offset = offset
ENDFUNCTION

FUNCTION Font_Initialize:
DIM _glyphs[FONT_TOTAL]
ENDFUNCTION


[Edit] Nope!  I use a weird type called TPoints that is just two TPoint variables: (along with a different Draw_Poly to handle gradients.  Boy do I have to tidy up my code and remove some TYPEs and make more consistent!)
Code (glbasic) Select
TYPE TPoint
x%
y%

FUNCTION Set%: x%, y%
self.x = x
self.y = y
ENDFUNCTION

FUNCTION Clear%:
self.x = 0
self.y = 0
ENDFUNCTION

FUNCTION Add%: xy1 AS TPoint, xy2 AS TPoint
self.x = xy1.x + xy2.x
self.y = xy1.y + xy2.x
ENDFUNCTION

FUNCTION Dump$: width%=3
//LOCAL rv$
RETURN "[" + FormatInt$(self.x, width) + "," + FormatInt$(self.y, width) + "]"
//RETURN rv$
ENDFUNCTION
ENDTYPE

TYPE TPoints
xy1 AS TPoint
xy2 AS TPoint
ENDTYPE

FUNCTION Poly_Draw_Gradient: xy_px AS TPoints, xy_uv AS TPoints, colours AS TPolyColour
POLYNEWSTRIP
POLYVECTOR xy_px.xy1.x, xy_px.xy1.y, xy_uv.xy1.x, xy_uv.xy1.y, colours.TL // TL
POLYVECTOR xy_px.xy1.x, xy_px.xy2.y, xy_uv.xy1.x, xy_uv.xy2.y, colours.BL // BL
POLYVECTOR xy_px.xy2.x, xy_px.xy1.y, xy_uv.xy2.x, xy_uv.xy1.y, colours.TR // TR
POLYVECTOR xy_px.xy2.x, xy_px.xy2.y, xy_uv.xy2.x, xy_uv.xy2.y, colours.BR // BR
ENDFUNCTION
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

fuzzy70

Yet again thanks for the tips & the code :nw:

I have downloaded the bmfont program & will give that a play, also there is a texture generator on that site that looks like it might be interesting so downloaded that as well  =D, am a firm believer of "unless you try as many tools as you can, how do you know what ones best for you or has a feature that you need"

I do use the "Updated" font program that was posted in the forum occasionally instead of the standard one & I have numerous font programs.

One problem I am having (which is not a fault of any of the programs) is creating small fonts which are scaled correctly, for example if I want a font that's 8x8 pixels some chars do not scale correctly. I know that is a small size but it happens on a lot of fonts & the size which each font looks "pixel perfect" as such varies. It is mainly due to the nature of scalable fonts I think rather than the programs & to be fair small sizes are where bitmap fonts excel over scalable ones. I got round the problem by using my Amiga font to Sprite program seeing as 95%+ of Amiga fonts are bitmap type (that & the fact that I have 100's of them).

BTW the reason for the small fonts is for a little project I am toying with which again stems from the Amiga. The other day I fired up my Amiga emulator & loaded up Pinball Fantasies, a game which I lost many an hour to when it came out  :D. While playing it I noticed the dot matrix style scoreboard (same as on real pinball tables tbh) & thought that it would be fun to create my own. At the moment it is still in it's early stages but you can setup up the pixel size (i.e 2x2px with a 1px gap) & the colours as 1 TYPE, another TYPE creates the matrix itself & its size as well as displaying it. I am currently working on the routine to convert a font sprite sheet chars into a dot matrix font as converting them on the fly takes to much time.

The planned features are as follows

  • A print command that works the same as the GLB one but for the display
  • Animated sprites on the display (again converted in advance)
  • Scrolling, both text and/or a graphic
  • As many displays as you want & all independent of each other, using an array of types to cycle through them
  • Pretty much anything you can do on a dot matrix display

I am using separate TYPE's at the moment but some will be integrated with the goal of making it as simple as possible to use. It is still very much a work in progress & as I am still learning more about GLB I am finding better ways of doing it.

Lee
"Why don't you just make ten louder and make ten be the top number and make that a little louder?"
- "These go to eleven."

This Is Spinal Tap (1984)