So the goal is to have pretty unit selection rings, preferably using textures so that we can extend it later on to also add various fancy graphics and not only basic rectangles and circles. This graphic was cited as an example of what this system would ideally support:
Keeping that in mind, the plan was to use textured quads (rectangles in OpenGL-speak).
However, there are some issues with using textures:
- If you apply the same texture to display a selection ring of larger sizes, then the texture will get stretched and a 1px white / 2px player color / 1px white outline might become a 4px / 8px / 4px outline. So, to achieve a consistent visual outline width, you'd ideally have to create a separate texture for each selection outline size. This is what the proof of concept currently does, and it comes down to a total of 126 textures. This is a large amount of textures, and in practice there are another 126 mask textures involved to indicate where the player color should be applied, totalling 252 textures to be loaded in the worst-case scenario. I imagine it's unlikely that you'd ever actually need to load this many textures at the same time to render a scene, but I have no data to see how many unique unit footprint sizes there are on average in a scene.
To reduce this amount, a scheme was suggested where you'd use the same texture for footprint sizes that are very close, like circles of radius 0.75 and 1, and accept the (hopefully small) visual difference. The amount of textures that is eliminated depends on how much visual accuracy you're willing to sacrifice. I've yet to try out a full range of accuracies to determine roughly at which point things start becoming unacceptable, but some quick tests suggest a reduction to only maybe 90-ish unique footprints (not counting masks). That's still a lot of textures.
The current implementation sorts the rendering by texture so as to reduce swapping, but there are still potentially many textures that need to be loaded to render a scene, and I have no idea how many textures of which sizes the game can typically handle. I'm also unsure how to best manage these in code; I've noticed that ingame there is usually this short black-flicker when the texture is first used, i.e. while it's being loaded. I've noticed that textures can be prefetched which might be useful for this, but again, I have this feeling that prefetching like 50+ textures is not a very good idea. I'd love some input on how to best deal with this.
- Using this many textures may pose some issues for artists as well. The current implementation uses a python script to read out all possible footprint sizes and shapes from the templates and generate the textures accordingly. This is fine for basic geometric shapes like circles and rectangles, but if we ever want to apply fancy graphics like in the image above to units of various footprint sizes, then the same texture is gonna need to be replicated a bunch of times in various sizes, with the additional constraint that the texture canvas must be a power-of-two in width and height. To at least somewhat accomodate this, I've created a small python script that will resize an image to its next power of two dimensions and fill the excess space with transparent pixels.
Naming conventions and resolution is another issue: the engine needs a way to identify which version of a texture matches a particular footprint size from the texture name, and also the exact dimensions of the actual texture within its canvas (ie. what part of the image is transparent filler to reach pow2 dimensions and what part is actual texture). The latter depends on the resolution with which world-space units are mapped to texture pixels, i.e. how many pixels you, the artist, would like to use to achieve a texture that looks decent for that particular footprint size.
Currently it defines a resolution of 8px per world-space unit, so that a footprint that is defined in XML as having radius 1.5 will have a 12px radius and 24px diameter in the texture. The convention, then, is to name that texture circle_24x24.png, and the engine would use those dimensions to determine exactly where the actual texture is located within the surrounding canvas (in this case, 32x32). The problem with this is twofold:
- First of all, it forces a resolution of 8px upon you, and this is hardcoded into the engine and hence unchangeable (and you'd have to redo all textures if you ever change it).
- Second, you may want to use different resolutions for different footprint sizes, or for various textures. For instance, I've found that 8px is sufficient for large units, but it looks bad for small circles. Or maybe you're working on a fancy graphic like the one above but need a higher resolution to make it look decent.
Clearly, the 8px one looks bad, while the 12px one might tentatively be called acceptable. However, for larger units 8px is plenty, and it'd actually be preferable to not use anything higher for them since that may cause jumps to the next pow2 canvas sizes and further increase the texture load -- probably not a good idea if you have on the order of a hundred of them. All in all, I'm not sure how to best handle this in a manner that's friendly to artists (because whatever fancy rules you come up with, the artists and the naming convention will have to consistently apply them). I've seen some textures.xml files around; I don't know if those are intended for this kind of thing, but maybe a texture listing in XML would be a decent solution as those would easily allow you to specify things like the resolution and also allow for location independence of the actual texture files. But of course, the file would need to be kept synchronized.
- The current implementation tries to be as efficient as possible about rendering the quads, primarily by two means:
- It allocates a single VBO, and reuses this buffer each frame instead of constantly releasing and re-allocating buffers.
- As mentioned, it sorts the selection overlays to be rendered by texture, so as to prevent excessive texture swapping.
However, supporting multiple quads per overlay introduces substantial additional complexity. It implies that we must use a separate index buffer and also keep track of a lot of technical stuff like offsets and number of indices per overlay in the large, statically-allocated buffer. Actually computing these quad's vertex locations is very much non-trivial as well, since units may be arbitrarily rotated -- and keep in mind that these computations will have to be performed every frame (units can move) for every selectable unit, which quickly adds up.
If each overlay were to be only a single quad, then we could get rid of all this extra complexity, and we'd also have a guarantee that the single buffer supports exactly 4K quads and hence 4K selection rings, which is guaranteed to be enough if the hardcoded selection limit is indeed 300. However, this will look bad if the terrain under a large unit or building is somewhat hilly. For instance, if a wide unit is walking through a ravine, then with a single quad the selection ring will disappear into the sides instead of "crawling up" the sides (although I personally would find that to be acceptable given the advantages it brings).
Someone on IRC mentioned that the ground beneath buildings may need to be flattened when it gets built, in which case single quads should be no problem at all. Nobody I've asked could actually confirm whether this flattening of the ground under buildings is something that I can rely on though, so I'm hoping someone can confirm or deny this.
Whew, I think that's about it. Hope I made the choices that need to be made clear without getting too involved; if not, please let me know and I will try to explain more clearly. For reference, some screenshots of the current implementation (at 8px resolution, so circles may look a bit shoddy):
Edited by vts, 09 October 2011 - 10:19 PM.