Rendering large tile-based ships

Started by
2 comments, last by Ravyne 7 years, 7 months ago

I'm working on a top-down 2D space RTS that features large tile-based ships.

Each ship has a complete functioning interior, with dozens of crewmen who man the various stations. The player can zoom in to manage the crewmen and zoom out to manage the fleet as a whole.

I'm having trouble coming up with a method of rendering these ships. There's a few stumbling blocks in my way:

1. Ships must be able to rotate.

This makes me think that trying to calculate tile positions and rotations wouldn't work and would result in a lot of visual artifacts.

2. Ships need detail when zooming in.

I would like ships to be fairly detailed when you zoom in to look at the interior. In my prototyping I had it rendering 16x16 tiles, but I'd like to render even higher (32x32, 64x64).

3. Ships need to be destructible.

Tiles will be removed when taking damage.

4. Some ship tiles should be animated.

I need a rendering method that can take into account the above 4 requirements. So far I have considered the following:

1. Rendering tiles individually every draw call and recalculating their positions/rotations based on the ships position/rotation.

2. Rendering the tiles flat to a RenderTarget2D and drawing that using the parents position/rotation.

I'm looking for any other advice or ideas about how to render multiple, large, tile-based ships without killing the performance or any of the requirements.

Example pics - zoomed out.

Ship Exterior:

ShipExterior.PNG

Ship Interior:

ShipInterior.PNG

Advertisement

Most probably you don't want to render individual tiles, but a mesh of quads, like a grid, where those inner vertices are shared by the six adjacent triangles of the touching quads. Since the vertices are shared, there's no risk of them diverging to great gaps. You can assign texture coordinates per triangle instead of per-vertex to give each quad a different tile; animation can be achieved by updating those coordinates each frame. Destruction can be achieved by re-assigning those coordinates to an empty/transparent tile, or damaged tile -- Debris and such would be drawn separately.

Drawing the ships to a 2D render target and then drawing that to the screen in one go is also viable, but remember that you have to animate it and change tile images there -- and the most complex part of drawing this large ship with potentially-high zoom factors will be to determine the sub-section of the ship that you need to render to that 2D render target -- then transforming it a bit differently because you no longer have a fixed origin. I guess 64x64 tile ships at 64x64 pixel tiles is 4096x4096 pixels, and modern GPUs should be able to handle textures that size, but its still a 90MB texture per-ship assuming 32bit color and mip-mapping -- You're approaching 1GB of texture memory with only 8 ships, and that's not an uncommon amount of memory on a low/mid-tier GPU from recent years (in fact, the Steam hardware survey says that just over 1/3rd of their users have 1GB or less VRAM).

throw table_exception("(? ???)? ? ???");

I agree, drawing it as a mesh of quads seems like the correct way to go. I haven't done much in the way of drawing 3D primitives, can you give me an example of how I would go about this?

I get the idea and I understand the basics of effects and meshes but how exactly would I be able to assign texture coordinates per triangle? With a custom shader?

A good search term is PTEX (Per-face TEXture mapping).

However, I spoke without my coffee this morning, and if you're pretty green with 3D its more straight-forward to just duplicate the vertices -- as long as the vertices are identical going in, and as long as they always get transformed by the exact same matrix (if you regenerate it, you have to build it by composing the component matrices in the same order), then the vertices won't diverge, and the filling rules ought to prevent pixels from fighting in a steady scene -- you might perceive some fighting as the ship rotates though -- MSAA/FSAA should help smooth that out.

On a separate note, if your very large ships become very small on-screen (say, where a tile occupies maybe < 4x4 pixels, you might then want to switch to rendering it as a single textured quad that's generated via means similar to the 2D render target method; once you can't see the detail in the tiles anyways, its a lot of work to go through for no real benefit, and rendering very small triangles is a huge performance drain. I would choose the transition point to be where the animation of the tiles gets lost in the smallness of them on-screen, then you don't have to worry about animating this small ship texture.

I assumed this whole time that you're rendering moving things like crewmembers separately, so you'd do the same for them when the ship takes only a small portion of the screen (assuming the position of crew members has meaning to the player when zoomed out this far), so you could transition them at some point to being rendered as single pixels. Likewise, you could do the same for an important indicators that would normally be shown with a tile animation.

throw table_exception("(? ???)? ? ???");

This topic is closed to new replies.

Advertisement