Scrolling with floating point coords

BlitzMax Forums/BlitzMax Programming/Scrolling with floating point coords

Grey Alien(Posted 2007) [#1]
Hi, I've recently been testing out scrolling tile maps in BlitzMax and have run into some problems and would appreciate some help.

When scrolling a tile map there are several options:

1) Lock the screen refresh rate to say 60Hz and scroll 1 pixel or more per frame (integer amounts only), and therefore you will be drawing at integer coordinates. This *does* look very smooth (like an Amiga) but you can't do this in windowed mode because you can't lock the refresh rate. Also if someone has set their video card to Force VSync Off then the game will run at a silly speed.

2) Use delta time or fixed rate logic and scroll by a floating point amount each frame. When drawing ROUND the coordinates. This works but the scrolling is a tiny bit jittery due to the rounding (moire effect) of the floating point coords. So basically OK (loads of PC games do this) but not great.

3) Same as 2 except *don't* round the coordinates. Let BlitzMax draw the tiles at floating point coordinates, which it can do - it just anti-aliases the whole tile to look like it's at a fractional coordinate. Lovely and smooth BUT where the tiles join they have a nasty flickering because the edge is anti-aliased to nothing (see my demo below). I tried adding a 1 pixel border to the tiles (this works for sprites) but it didn't make any difference to the tiles.

4) I had an idea to draw the entire screen on a pixmap and then draw than on the screen at a floating point coordinate, this should look nice as the whole thing will be anti-aliased and there won't be any dodgy tile edges. BUT this would most likely be very slow as drawing tiles to a pixmap and then uploading it to VRAM each frame wouldn't be quick at all (I think).

5) Indiepath suggested using a "mesh" but not being a 3D programmer I only have the vaguest notion of what he's on about. I gather all the tiles could be added to a mesh and then the mesh would be drawn and all the tile edges would blend in together nicely. This make sense and if it was a lot faster than pixmaps, then it's the way to go. But there are no "mesh" commands in BMax do I guess I'd have to go low level DX to get this sorted. However, I've no clue how to do that and that is what this thread is about. Does anyone have any suggestions of resources that may help, thanks! :-)

Anywhere here is a test app:

http://www.greyaliengames.com/misc/scrolltest.zip (685Kb)



Strict

Graphics 800,600,0

Local x# = 0
Local y#=50
Local speed#=0.23
Local size=64

Local tiles1:TImage = LoadImage("tiles1.png")
Local tiles2:TImage = LoadImage("tiles2.png")
Local tiles3:TImage = LoadImage("tiles3.png")

While Not KeyHit(KEY_Escape)	
	SetBlend AlphaBlend
	Cls
	'rectangles
	SetColor 200,200,200
	DrawRect x,y,size,size
	SetColor 200,100,100
	DrawRect x+20,y+20,20,20
	SetColor 100,100,100
	DrawRect x+size,y,size,size
	'two images
	SetColor 255,255,255
	DrawImage tiles1,x,y+100
	DrawImage tiles1,x+size,y+100
	'single combined image
	DrawImage tiles2,x,y+200
	'draw tile with 1 pixel border
	DrawImage tiles2,x,y+300

	'move
	x:+speed
	DrawText x,0,0
	Flip 1
Wend


You can see that I am moving the images across the screen by a floating point amount (I know there is no delta time, forget that for now). Things to note:

1) Note how the rectangles made with DrawRect are not anti-aliased at all (because the floating point coord is not taking into acount), I posted this in the bug forum. This is what drawing tiles at integer coords looks like, not very nice (i.e. all jiggly).

2) The first pair of tiles show the horrible join problem, see it flickering in the middle as they move? Also the right edge flickers badly.

3) The second pair are actually one large tile loaded in like that, and thus the join is perfect (it's what I imagine the pixmap or mesh method would look like).

4) The last pair are again one large tile but with a 1 pixel border all around to see if that helps the right edge problem, but it doesn't. The right edge problem is only because it's bright white, it will be occuring on the left edge too (and top/bottom if I was scrolling that way as well). Anyway, the edge problem wouldn't be an issue if you predrew the entire screen from tiles using a mesh. BUT it would be an issue if say you had a bitmap background and plonked square platforms on it, these platforms would have dodgy anti-aliasing on the edge, but I'm not sure there is any way around this at all...it's just the way it is.

Anyway, any help and viewpoints are greatly appreciated thanks in advance!

One thing, it's probably not worth trying to persuade me to go the route of option 1) as any games I write need a windowed mode. If I can't find a solution with meshes, then I need to decide on 2) integer jiggling or 3) tile edge flickering, neither of which is particularly great.


ImaginaryHuman(Posted 2007) [#2]
Hi Mr Alien. Happy New Year!

When you draw a tile what you're doing at the lower level is drawing a quad. A quad is actually 2 triangles. The thing is, because the system is told both triangles as a `triangle strip`, it can take the second triangle into account when antialiasing the first, so you never notice a diagonal seam running accross the tile. If the individual triangles were drawn separately, the system would not know about the second triangle so would have to attempt to antialiase to the background, which would create a dark flickering line across the diagonal. I would think the same applies to what you are referring to as a mesh.

In OpenGL you have various types of geometry you can draw. You could be drawing GL_QUADS which in turn at a lower level is like GL_TRIANGLE_STRIP, where the vertices are shared so it actually only takes 4 corners to define two triangles, rather than 6. A `mesh` would be several `rows` of triangle strips. What you do is you start by defining the first two points of the first triangle in the top left corner of the mesh, then you only need to define 1 more corner per triangle to add another triangle - it shares two of the previous corners. And because you're telling the system about all these interconnected corners, it can use that information to properly blend them together at the edges with no seams or jittering. You then proceed to make a whole row of triangles in a strip and when you get to the end you do a little `about turn` and proceed with a second row going from right to left. Then when you get back to the left edge you turn again and do another row left to right. All the triangles are effectively joined together so you should have perfect antialiasing between triangles. You would need to code your own GL and DX commands to create strips, define vertices, vertex colors and texture coordinates, basically kind of a textured polygon module. You have to include all visible tiles in the polygon strip. I'm not sure if there'll still be issues between rows. No idea of any resources for DX but for GL just read the parts of the `opengl red book` online that talk about geometry/triangle strips.

One other option is draw the tile scrolling at an integer offset, grab the whole thing into a texture, then draw the full-screen texture at the floating point offset. Might help.

Forget the pixmap idea it is too slow.

There is also such a thing as `polygon smoothing` which you can enable. It requires a certain blend mode to be used but will smooth the edges of polygons correctly (supposedly) so that when you draw further polygons they appear to be perfectly antialiased together at the seam. I guess you might have to enable polygon smoothing for triangle strips to work correctly.

Not sure that this helps. There doesn't seem to be an easy solution besides coding your own graphics stuff.

I'm glad you raised this topic through your explorations as I wasn't aware the antialiasing was a problem.


Grey Alien(Posted 2007) [#3]
Hi AngelDaniel, Happy New Year to you to!

Thanks for the explanation of meshes, yes it makes sense. However, if there was a row above and below a row then I guess the points are shared by even more triangles, if fact you'd need that to prevent an anti-aliasing issue in between rows.

As for grabbing the whole thing into a texture then redrawing, I thought that it would be as slow as Pixmaps wouldn't it because GrabImage is pretty slow as it all comes from VRAM into RAM then has to go back to VRAM to draw? Or is there some way to grab to a texture that's already in VRAM (for speed) that I don't know about? If so I'd like try that.

Actually for stuff like this, it's a shame there is no little Amiga-style register to offset the screen a little, then you could draw it at integers, then shift it left half a pixel or whatever ;-) In fact you could do this in DOS, I made a scrolling platformer in Borland C++ some years ago.

So other than me going on a huge study of this stuff (and having to write a DX AND OpenGL version) the best answer may be integer based scrolling. At least for demoing to people who have my framework how to make a simple scrolling platformer using native BMax commands.


ImaginaryHuman(Posted 2007) [#4]
In GL at least you can do glCopyTexSubImage2d() which basically means copy an area of the backbuffer into a previously created texture (image), it doesn't involve pixmaps or main memory at all and is usually fast enough for realtime decent framerate full-screen.

I would try looking into the polygon antialiasing (e.g. glEnable(GL_POLYGON_SMOOTH) and setting the blend mode appropriately to accomodate multiple polygons. I think that's probably what you're meant to do, but I can't see how it'd work when you want a different blend mode at the same time.

If you're having non-rotating tiles, maybe you can figure out what extra `line of pixels` with a given value you need to draw inbetween the tiles to offset the effect that it produces?

Amiga games didn't seem to demonstrate any issue using just integers for scrolling, although maybe people were using the 1/4 pixel hardware scroll on AGA games.

Also have you tried drawing to coordinates where you add 0.5 to the x and y coords? - In GL at least they recommend positioning pixels in the exact center of a pixel at 0.5,0.5 rather than the top left corner 0,0.


Grey Alien(Posted 2007) [#5]
OK that command is worth knowing, I guess I need to find a DX version too and then try it out. Actually I don't need any fancy blend mode on the tiles and they won't be rotated scale, mind you that could be pretty cool too ;-)

Reason Amiga was fine with ints is screen drawing was tied to VSync and coders moved the screen by whole pixels each frame, not floating point amounts.


dmaz(Posted 2007) [#6]
create a mesh made up of quads the size of your tiles plus one row at the top and one at the bottom, left and right. the grid would be the size of the screen plus those extra rows/cols. so basically you would have an extra row/col off screen on each side.

the tiles need to be in one texture and you set each quad with the proper uv coords that allow you to display the right tile.

scrolling is done kind a like it was on the amiga, basically... by just moving the whole mesh around by floating point. when you get to an edge you change all the tiles in the grid by using the right uv coords again and shifting the whole grid back to center.


Grey Alien(Posted 2007) [#7]
dmaz:
so basically you would have an extra row/col off screen on each side
Yeah I figured that, I've just now Idea how to make a mesh of quads using uv coords of one texture. Do you have any links to suitable DX and GL resources? Thanks. btw, is it quick to make a mesh (in CPU time?) and what about drawing it, also quick?


ImaginaryHuman(Posted 2007) [#8]
Same speed or faster than doing individual DrawImage calls.


Grey Alien(Posted 2007) [#9]
OK great thanks. Now all I need is some skilled people to help take up the gauntlett on this to get some mesh generation code written for DX/OpenGL in BMax...any takers?


TartanTangerine (was Indiepath)(Posted 2007) [#10]
I'll knock something up as soon as I've got my modules working with the new version of BMAX - it's basically going to be the texturedPoly module with a load of transformed vertices chucked at it.


popcade(Posted 2007) [#11]
Indie, the Light/PrjMatrix/TexPoly modules are pretty good, hope to see them live again.


Jake L.(Posted 2007) [#12]
@Grey, check your mail.


Grey Alien(Posted 2007) [#13]
Indiepath. Sounds great. Could be the start of some great games.

Jake L. Very kind, will check out ASAP. Have a job on tomorrow so may have to wait until later in the week. Thanks again.