All of this is handled by the Cocos2d tile engine :)
Richard
My PyWeek entry from March (http://partiallydisassembled.net/make_me/)
used tile rendering using batches. I didn't use sprites because the
creation time for each sprite was too high. I use Batch.add(..) to
create the vertices for the tile map. If all the images are located
in the same texture, the entire screen can be drawn as one primitive.
Culling is done by creating separate vertex lists for approximately
screen-sized areas of the map. These can then be added and removed
from the batch on-demand as the player moves around (tuning this took
quite some time). I didn't have any animated tiles, but these can be
done by either modifying the vertex texture coordinates to point into
the desired frame, or by swapping entire vertex lists (which would
probably be faster but more memory heavy).
I'm afraid the code in Make Me is far from publishable (it was written
in a week for a single game, after all), but you may find it useful to
mine for ideas.
Alex.
Cocos tile engine uses sprites in a single batch, creating and destroying as
needed. It doesn't appear to suck when scrolling :)
Richard
that definately should be possible, but you might need to blit the tiles first
to a seperate surface/texture, and then blit that every gameframe instead of the
2300 tiles. If you want scrolling, you could just blit the borders and keep the
rest. For animated tiles etc. you might need to have a seperate layer, and only
update that.
But things like this, you should be able to pull off on the hardware you
specified; I did something similair in SDL back in the days, on similair
hardware.
Good luck!
Laurens
Actually the vertex colours will scale the texture colour; effectively
tinting it. Usually the colour needs to be white, which you've done.
> then i add those four "corners" of my tile with their respective
> coordinates and colors to the batch as a GL_QUAD.
> the batch is then drawn in the on_draw() method
>
> unfortunately my texture will not show up so a hint on what i'm still
> doing wrong is very much appreciated.
Just need to enable texturing and bind the texture. Ideally you would
do this in the group, so you can use different textures within the
same batch; but for now you can just add it where your blend state is
set.
glEnable(self.tile.target)
glBindTexture(self.tile.target, self.tile.id)
Alex.
>
One a side note, you can use cocos mostly without overhead by creating
a scene and replacing its draw method.
You control what transformations you get on that scene. So you can be
as fast as you want/can. But you also get the benefits of cocos if you
want to add more stuff.
Lucio.
Make sure you at least run python with "-O" option to disable OpenGL
error checking, which is expensive. Keep in mind that most of the
benefits of using batches come when batches are large -- you may find
that you get only reasonable performance now, but that same reasonable
performance with 10x the amount of geometry (in the same batch).
> also i got a bit troubled with the TextureGroup. It needs a texture
> that it later will enable and bind and this works fine with using a
> TextureAtlas as in my current code but when i try to use the
> recommended resource.image module which automatically will create new
> TextureAtlas'es - how do i get the (maybe multiple) textures then?
Use the texture target and id from the region returned by
resource.image. For images on a shared texture atlas (common case
with small images), the target and id will be the same. You can
safely create multiple TextureGroup's with the same target and id --
Batch will know that they can be grouped together efficiently if your
TextureGroup class correctly implements __eq__ and __hash__.
>
> future questions:
> when i now want to move on the map i have to refill the batch - i do
> this via deleting the vertex_list and refilling the batch again with
> add() right?
> for animation it should suffice to only change the texture coordinates
> in the vertex_list? another way to do animation would be to prepare
> several vertex_lists with each animation step? i have to try out both
> methods and compare how they perform later but at least for map
> objects i have to change the textures directly as the objects there do
> have different numbers of animation frames.
Sounds like a plan.
Alex.
Use one group for every image, if you like. Batch will know how to
merge them back together.
> do i have to get this info manually by iterating over the created
> TextureRegions and finding out how many Textures where needed?
> you mentioned i should use several TextureGroups but this would imply
> that i do have to call batch.add() several times for each group that i
> create, right? i would then start creating one group per image loaded
> and doing a batch.add() for each image separately.
> but isnt it better to add as many vertices as possible in one
> batch.add() ?
Nope, once it's in the batch, if the groups are compatible (according
to their __eq__ and __hash__ methods), it doesn't matter how many
vertex lists were created with Batch.add; they all end up in the same
vertex domain.
Alex.
thanks for the feedback josch! im just too much into being a salesman maybe :)
Lucio.
The pyglet Sprites used by the cocos tile engine use vertex lists underneath -
approximately as fast as implementing a direct-to-vertex-list approach, with
a small overhead when the visible tiles set changes. It has the benefit that
it can use animated tiles through the standard pyglet Sprite animation
mechanism (I believe, I've not actually tried to use them).
Richard
Sounds good. Note that the TextureAtlas above will automatically be
sized up to 512x512 (textures usually need to have dimensions of
powers of 2), so you can use some of the extra space in there for
"free" if you need it.
Alex.
You can actually migrate a vertex list from one group to another, but
this has a similar performance penalty. Ideally you would keep frames
of an animation in a single texture; vertices can then keep their
group. There's no problem with having multiple groups in a batch;
this is actually quite an efficient way of managing the drawing of
several textures.
Alex.
If a vertex list contains only tiles with the same texture, then you
don't need to remove/add it from/to the batch just to change texture;
just change the texture on the vertex list's group.
Forgive me if I misunderstand; I'm not entirely following your
description, and am a bit fuzzy headed after PyWeek to be reading
source code.
Alex.
> but since i want to fill the textures my own for this reason it would
> be nice to know how far it is reasonable to go for the texture size.
> the maximum on my hardware seems to be 2048x2048 but what is a save
> value on even five year old gfx cards?
I haven't seen a graphics card that couldn't handle a 2048x2048
texture (that wasn't also broken in many other ways), so that's a
pretty safe assumption. You can query the max texture size at runtime
with glGet(), and have your program adapt in any case.
Alex.
Nope, that's a bug. Post a complete (small?) example that reproduces
it, please.
is there an example of using
> migrate - couldnt find one: not in cocos and not in the pyglet
> examples or make me.
> isn't changing a vertex list's texture a good idea?
It's a bit hairy ;-)
Alex.
Thanks for the test case. Fixed in r2257 (in
branches/pyglet-1.1-maintenance) and r2258 (trunk).
>
> why is migrating vertex lists hairy?
>
> when i combine resource.image with animation then i HAVE to switch the
> group as i have no direct control over the texture my frames will be
> packed into and the new animation frame could be in a new texture.
>
> i also have to switch the group (respctively migrate the vertex list)
> when i want to move around the map by switching the tile texture in my
> vertex lists (which is what i have to do because there are more tile
> types than even a 2048x2048 texture can handle)
>
> so why is it hairy and not used very often? do i do it wrong?
It's just a complicated and error-prone (as you've discovered) method.
It's quite costly to do (though not as costly as delete+add), and if
you can think of other ways of organising your vertices, they'll
probably work better.
>
> this night a fourth method to do movement came to my mind and it's a
> variance of my second method:
> one could first delete tiles (respectively vertex lists) that will go
> off screen,
> then move the tiles that are still to be drawn
> and then adding new vertex lists with batch.add() but obviously only
> on the border
> with this method no group changing would be necessary
You don't need to move the tiles in this case: just glTranslate the
whole scene. This is similar to the method I used in Make Me, except
that I was adding several rows (about 2/3rds of a screenful) of tiles
at a time, rather than just the one that was required. Remember that
batches work fastest when you don't touch them, and there's usually
very little penalty for rendering off-screen (if the memory transfer
is fast, which is what batch gives you).
>
> i still do not know what method should be prefered.
> method 1 seems to be a little too much overhead because batch.add() is
> called to often - but is still acceptably fast
> method 2 is like 4% faster than method 1 but needs to change the
> texture of the vertex lists with migrate
> method 3 seems a bit complicated because of the texture management
> method 4 is also more complicated but combines method 1 and 2 to not
> need any texture change
>
> there is still the issue whether i should use resource.image() or not
> - is the checking or storing of the texture each tile or animation
> frame is packed into and the vertex list migration on animation worth
> the convenience?
resource.image doesn't give that much convenience. The image packing
procedure is available in the TextureBin and TextureAtlas classes, and
pyglet.resource.file gives you the path/zip-finding shortcut.
Creating your own resource function that packs into specific textures
shouldn't be difficult.
>
> http://pyglet.googlecode.com/svn/trunk/DESIGN <= i read about scene2d.
> how are you gonna do it? will your approach solve my problems?
That's a super-old document. (Richard: didn't we delete it?). I'm not
working on that area any more at all, but Richard's more or less taken
over (in the cocos2d project).
Alex.
It may start to make less sense... it's a judgement call you'll have
to make based on your application behaviour. Luke's suggestion about
only animating on-screen texture coordinates while keeping off-screen
vertices around seems reasonable as well.
Alex.
Thanks to josch and Alex I've learned a hell of a lot and I thank them
for the discourse. It makes me want to play along but I've just moved
house and no time from the wife to play until the house is sorted and
boxes unpacked.
Cheers for the convo guys,
PN
2008/9/26 Dany0 <men...@seznam.cz>:
It just means that (like anything with computers), the less you do,
the faster it happens. Changing the size of a vertex domain is more
expensive than just modifying one in place, and creating a new domain
(i.e., by creating a vertex list that uses a previously unseen vertex
format) is still more expensive.
>
> and is there a difference between different amounts of changes i can
> do to the batch? like whether i update one vertex list in the batch or
> two or whether i update a lot of texture coordinates in a given vertex
> list or only a few ones.
For this kind of information you're best off creating some simple test
cases and measuring the performance. Bear in mind that these
measurements will be dependent on your video driver, and whether or
not VBOs are enabled.
Alex.
In general, yes; though the logic in iterating past every second (or
third) float in a vertex list may outweigh the benefit. Note that
using a slice with a step effectively implements the same operation,
though pyglet handles the iteration for you (probably slower than you
could have done, due to generality).
Alex.
I don't know if this was suggested before (I skimmed the thread and
didn't see mention), but it is possible to animate the vertex
coordinate without changing the vertex list itself by using a shader.
A possible method would be to store the tile type for each vertex as
an additional vertex attribute and pass a varying "time" value from
your application to the shader program to control the animation. The
shader would then alter the texture coordinates (perhaps shifting them
along the Y or Z for a 3D texture) according to the vertex attribute
(for instance leaving the coords alone for non-animated tiles) and the
time value. The major downside to this approach is it requires opengl
2.0, which may be a non-starter. The upside is zero CPU cost for
shaders running in the hardware.
Alternately, you could divide your vertex list into animated and
non-animated tiles and draw them separately. In one pass you could lay
down the non-animated tiles, and in a second the animated ones. So
long as the latter does not overdraw the non-animated tiles, it will
look the same and allow you to have the non-animated tiles have a
static vertex list. Thus you update a significantly smaller vertex
list to animate. This assumes that a large fraction of the tiles are
not animated, however.
Yet another approach would be to switch the base texture rather than
the texture coordinates. Rather than putting every possible tile type
over time in a single texture, only put ones that will be shown at the
same time. Then switch which texture is active to animate. If many or
most tiles are not animated, you can combine this idea with the above
and keep the non-animated texture completely separate, thus removing
redundant tile textures to save space. The downside to this is that
all animated textures would have to operate in lock-step (have the
same texture at the same time), which may not look as good. But you
could get around this by having multiple water types at the cost of
larger textures.
Still another approach would be to abandon updating the vertex list in
python (assuming doing that is the bottleneck) and write some C code
to generate it on the fly. You'll probably find yourself limited by
transfer bandwidth to the video card with this approach, In
native-code you can build a very large vertex array in short order.
The shader solution is probably the most ideal. I would probably go
for that and either disable animations when shaders are not available
or fallback to one of the other compromise solutions above.
hth,
-Casey
-Casey
Hello all!I'm having a bit of an issue with drawing tilemap. See, I'm trying the same methods as josch to draw it, and it works pretty ok. The only thing is the tiles are not in sync (or something) and when I move the map around (with glTranslatef) I get black lines (or brown or blue, seems to depend on the tile next to it on tileset) between them. That is kinda weird. One fellow suggested indeces (batch.add_indexed()), but I dont know how to use them on this case. Not much talk about it on the internet.Here is an example shot of the issue: http://www.eeneku.net/upload/screen_for_fryer.pngHere is the source file for that test: https://github.com/eeneku/thor/blob/master/tilemap_test.pyeeneku--
You received this message because you are subscribed to the Google Groups "pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyglet-users...@googlegroups.com.
To post to this group, send email to pyglet...@googlegroups.com.
Visit this group at http://groups.google.com/group/pyglet-users?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.