Limit to buffer images loaded?

I’m using bge.texture.ImageFFmpeg to load some brushes for painting on a terrain.

At first I tried using a small number of brushes and edit their alpha values in real time to get the full selection I need.
But this takes way too long.

So I used PIL to make a full set of brushes, which I load as a dictionary and then get the dictionary entries to paint with.
But the loading process seems to be failing around 130 brushes. The brushes are small, just 64x64 tiles but there are a lot of them. Maybe it’s a memory issue?

Or is there a limit to the number of buffer objects you can load?

On the up side it now takes around 2.3 seconds to paint a map that’s 4096x4096 in size.

I’d be grateful if someone could test it too;
Does it load at all?
How long does it take to load?
If you change “self.brush_number” to 3 or 4 does that make it work or stop it from working?

Here’s what it should look like:

Right now it’s just painting random terrain values, but later I’ll be setting it up to paint a more complex map.

Works, with 2, 3, 4.
Latter had printed:

Thanks. Thats 240 brushes loaded and a time of 2.3 seconds to load the map.
Wonder why mine is failing to load…

I would suggest instead of using a massive 4096/4096 texture map, use the dynamic texture to create a mask for several smaller repeating textures.

Took 2.16 seconds to load “120”.
Clicking and drawing drops the framerate down to 40FPS.

Just a note on the code:
You have some cryptic stuff going on and some rather long functions. Even if things aren’t used in multiple places, the functions should be broken down, particularly ones like ‘fill_areas’ which is 150 lines long and has great stacks like this:

        for i in range(3):
            for key in paper:
                area_color = paper[key][1]    
                if area_color != 0:                      
                    search = [[-1,0],[0,1],[1,0],[0,-1]]                                    
                    for n in search:
                        n_key = (key[0] + n[0],key[1] + n[1])   
                        neighbor_tile = paper.get(n_key,None)
                        if neighbor_tile:                        
                            if neighbor_tile[1] == 0:
                                roads[n_key] = 1
                                paper[n_key][1] = area_color   

Also, I have discovered in workin in CaveX16 that you need to separate the map into various parts. Initially I had a map class that contained everything about the map, and hit some issues with that becoming unmanagable when I tried to implement pathfinding on top of resource collection on top of … It ended up with me re-writing the entire map system.
What I ended up doing was to create a separate ‘node’ class representing each logical section. Then that node could be manipulated.
Unfortunately I didn’t fully avoid a map superclass, but if I were to do it again I would have each node know about it’s neighbour nodes, have each node have a plot function that will draw on it’s section of texture etc.

You may also want to consider breaking out constants to the top of the script, such as:

MAP_SIZE = 4096
TILE_PATH = bge.logic.expandPath('//brushes//')

There are also some tricks, like to join sections of a path in a cross-platform manner, use:

os.path.join(path1, path2, path3, path4)

Things like loading the brushes don’t need to be part of the map class. Perhaps part of a brush handling module.

Also, I’d reccomend using the text in an external editor, and firing up the dreaded pylint and pep008 checkers. Your code looks good stylistically, but those two will force you to maintain the style (and tell you when functions are getting too long and complex).

Thanks, this is just a test script so it’s a bit messy. There’s stuff in there that isnt getting used right this moment and other things which haven’t been broken up in to proper functions yet. I usually use pycharm for working on the real project but being able to type and run in the same window is really great for prototypes like this.

The main aim right now is to test if the principle of painted maps works, and if it can be made reliable. I also want to see potential upper limits on size and other things. When generating the map most of what you see is going in to the visual representation only. A few things will be extracted for game play such as location of roads, areas marked as objectives, where trees will be placed etc… the map will be working on the principle of being an almost empty dictionary. It will store only the location of things that block movement. Calling a custom get on the map will return either a reference to the object located there or an empty tile object if there is no dictionary entry. It’s not like an underground map where 70 percent of the map will be blocking walls. 80 percent of the map is going to be empty space. The gameplay revolves around how you move your units around that space to get in to the best position and how you keep track of enemies who you might not be able to see.

Found a work around today, just went through a sub list of brush types “road_{}” for example in one pass, loaded all the road brushes painted and then moved on. So only a few brushes are loaded at one time.

I halved the size of the texture and that also helped, dropping painting time to a total of 0.5 seconds. But I do like the idea of a large grid map, at least 64x64 tiles is needed. Maybe I can reduce the size of the tiles, down to 32x32 or 16x16. That should get rid of some of the more tiled look that is going on too, but may make the ground look a little pixelated.

I tried loading up a big base image to add some macro detail before painting the tiles with a slight transparency, but couldn’t get the large image to load. Same issue as before with non-loading brushes.

Also, whilst advisable to avoid prematurely optimising any code you write, there are certain tips to consider:

Avoid performing multiple attribute lookups inside loops. For example, instead of writing “mathutils.noise.voronoi.XXX”, import from voronoi “from mathutils.noise.voronoi import XXX”, and if optimising later, you can locally alias these to reduce lookup in the global namespace (and for even better performance, assign them as default args to a function, so they’re only evaluated once).

Pre-calculate constants outside of loops, and use itertools.product (in some cases) instead of nested for loops.

Thanks, I’ve seen here that a few changes and pre calculating things can make a big difference.
When prototyping I usually dont worry that much about optimal performance, but rather being able to quickly write new methods for testing. Having constants as properties of self makes that easy since I can make a change there and see results all through the code. I suppose declaring them at the class root is good, but what about subclassing later?
I’ve never considered whether “from x import x” would improve performance. I usually avoid it for the sake of clarity, otherwise later I’m not always sure where there are dependencies in my code. But I’ll give it a try. As long as I avoid “from x import *” it shouldn’t be hard to track.