What I am up to

Curse you, Riven, for the brain worm you left me with.

So… I’ve been working on Battledroid for quite some time now (and it’s just got a whole lot harder as I am now effectively a full-time housewife thanks to Mrs. Prince suddenly getting a full time job leaving me to wrangle the kids). Whilst Chaz is totally tied up making Basingstoke I’ve been able to slowly trundle away doing long-undone jobs in our game framework in some sort of attempt to make it all a bit more easy to make games etc. etc. blah blah refactor refactor.

A couple of weeks ago I finally had the full metagame in Battledroid implemented, which is the bit where you take over territory on a giant world atlas of the actual Earth, and win the resources contained therein. There was (is) a screen in Battledroid which you can zoom and scroll around the atlas and see who’s got what, and overlay various sorts of data over it such as the size of the armies or the value of the resources or the time since territory was last attacked. There are something like 7m territories in the atlas of which 2m are actually conquerable (the rest of course being ocean). They’re tinted blue if they’re your territories, red if they’re enemy territories, and purple if they’re friends’ territories (Steam friends); the brightness of the territory described the overlaid data value. Plus a few decals such as the location of custom resources, home bases, and such. Yay! There’s a debug mode which lets you click all over the place and “annex” territories and I had a fun little “battle” with a friend the other day watching them turn red and blue.

But I am dissatisfied, thanks to Riven.

See, he said that a hex map would be much nicer. And he is of course right, as a hex map would indeed be much nicer, especially given the significance in this game of contiguous territory clusters. And there’s also the tiny niggling irritation of the fact that no 2D atlas projection manages to get the land area right without looking pretty distorted or being rather difficult to manage or render.

Now here I am building an entirely new world topology based on a geodesic sphere. I am really only just at the start of this particular endeavour but I’ll try and describe where I’m at and who knows, maybe some bright spark in here knows some shortcuts that they might be able to show me.

So, a geodesic sphere is a based on an icosahedron (d20). What you can do is subdivide each face of the icosahedron into many, many smaller equilateral triangles; and then when you’ve got enough, you “balloon” the whole thing out so each point is at a constant radius, which gives you sphere. Though at this stage I am not so concerned about the rendering of the globe which is another problem entirely. Right now, I am concerned simply about the topology of the sphere.

That’s yer basic sphere. I need a subtly tweaked version: I want hexes, not triangles. This means you can’t just naively subdivide the faces of the icosahedron by powers-of-two because hexes don’t quite fit right in that; each hex requires a clump of 9 triangles in which to fit. But this again is simply an issue for rendering the globe later on - I don’t need to worry about it right now.

In order to use my topology in the game, I need a data structure for which I can ask, “Given a hexagon at index n, what are the indices of its six neighbours?”

Firstly, I’ve arbitrarily numbered the vertices, edges, and faces of the icosahedron to give everything some sort of frame of reference, with vertex 0 being the North Pole and vertex 11 being the South Pole. The Greenwhich Meridien will be down edge 0, and Blighty will be split over faces 0 and 4. I am describing the sphere in terms of an “order” (its “size”), which is the number of wholly contained hexes along an edge. For order 0, there will be a single wholly contained hex in each face of the icosahedron. As each vertex of the icosahedron joins five edges, there is a pentagon at each vertex - a special territory which just 5 neighbours. Each edge has a number of hexes which are shared by the two faces on that edge (for a sphere of order n, there are n shared hexes on each edge, and an additional n + 1 edges touching that edge).

In theory I should be able to mathematically calculate the indicies of every hex / pentagon on the sphere, and their neighbours, using a whole bunch of formulae, but this might take me an exceedingly long time to work out being a bear of little brain, so instead I’m going to precalculate the whole lot and stash it in a simple data structure.

Precalculating the whole lot is almost as hard.

What I’m attempting to do is, for each “Face”, construct the appropriate number of “Hexagons”, then iterate along them, joining them together in a two-way graph. After I’ve done all the wholly contained faces I will need to do the “Edges”, stitching the faces together. Then finally the 12 pentagons hold the whole thing together. After that, I should be able to validate the topology with two tests. Firstly, all territories should have exactly 6 unique neighbours (except the pentagons which must have 5). Secondly, I can do a flood fill of the topology, the end result of which should be that I’ve touched every single territory.

Turns out it’s quite a complex problem to solve and might take me a week or two :slight_smile:

Cas :slight_smile:

I’ve only quickly skimmed your post. My little one is waiting for his lunch. Look here: http://www.redblobgames.com/grids/hexagons/

Thats actually a really good article.

@Roquen: hex on a 2D map is fairly easy (I put that code in SPGL2 quite some time ago). Wrapping a 2D hexagram/pentagram grid over a sphere, while retaining uniform dimensions is slightly harder.

@Cas: my first instinct: don’t split in powers-of-2, split in powers-of-3.

There will be 22 12 pentagrams, regardless of how many times you sub-divide. They will get smaller with each iteration (reduced by factor 9 in surface area). Iterate often enough and you end up with a hex-sphere with 22 12 pentagrams with surface areas approaching zero.

At this stage the actual geometry of the thing is not what I’m trying to solve - I’m working just on the topology - what faces are adjacent to each other. So I’m working directly with hexes and pentagrams, rather than triangles and subdivisions.

I can plug the new topology straight into the back-end of the game with virtually no changes which is nice (mostly just replacing X and Y coordinates with a single index). Then I can get to the much more complicated problem of rendering it!

Riven… 22 pentagrams?

Cas :slight_smile:

You said you calculated/stored the data ahead of time. You can bruteforce the vertex-indices for each face, and the neighbouring face-indices for each face.

To make the AoT process slightly faster, create a 3D lookup table (or any space partitioning datastructure) and gather the 6 nearest vertex-indices for each vertex-index.

Argh: 12. There is a pentagram at every vertex of the icosahedron: 1+(2*5)+1

Heh, made similar mistake in my own post too when I went back to look :smiley:

Cas :slight_smile:

But but but… topology depends on the geometry. :’(

I think it’s fairly simple, but maybe I’m missing something:


// fill the lookup-table
for(Face face: faces) {
   Vec3 midpoint = new Vec3();
   for(int index: face.indices()) {
      midpoint.add(vertexArray.get(index));
   midpoint.div(face.indices().length);

   fancyDatastructure.put(midpoint, face);
}

// query the lookup-table
for(Face face: faces) {
   Vec3 midpoint = new Vec3();
   for(int index: face.indices()) {
      midpoint.add(vertexArray.get(index));
   midpoint.div(face.indices().length);

   float halfDim = ...; // depends on recursion count
   List<Face> result;
   do {
      result = fancyDatastructure.queryCube(midpoint.sub(halfDim), midpoint.add(halfDim));
      halfDim *= Math.cbrt(2.0); // double the search volume, in case we need to query again
   }
   while(result.size() < 1+6); // self + neighbours

   Collections.sort(result, fancyDistanceComparator);
   if(result.remove(0) != face) throw new Fit();
   List<Face> neighbours = result.subList(0, 6);
   Collections.sort(neighbours, fancyOrientationComparator); // N, NE, SE, S, SW, NW
}

I did that quickly…I thought there were some 3d links in that. Simply searching for hexagon, tiling, sphere should be a win.

Here’s some examples (and yeah you gotta cheap somewhere):



http://sourceforge.net/projects/hexmapsphere/ (java)

I want to think you can approximate good enough by: start with an icosahedron, then perform some number of sqrt(3) subdivisions. hexagons only.

http://kiwi.atmos.colostate.edu/BUGS/geodesic/text.html might be of some use with the indexing.

It’s also pretty easy to just use the dual graph to convert from triangles to hexagons.

Maybe you need a truncated icosahedron http://mathworld.wolfram.com/TruncatedIcosahedron.html, wikipedia has the general set of coordinates for the points you might be able to use in your pre computed set https://en.wikipedia.org/wiki/Truncated_icosahedron

This shape is the shape they make footballs (soccer) in.

I’ve started with a truncated icosahedron. Step 2… subdivision. So far as I can tell, each edge needs to be turned into another hexagon, whilst each existing hexagon “shrinks” to accommodate the new hexagons… and I should be able to do that as often as I like until I get the required number of hexagons.

Cas :slight_smile:

I’m not sure whether you’re asking a geometry question this time, or just stating what you’re doing… :point:

An edge is a line (in 3D space, in this case), so I’m not sure what you mean by turning an edge into a hexagon. The basic principle is to subdivide each triangle into 9 triangles, of which the outer edges of the 6 inner triangles define the boundaries of your inner hexagon. The same edges define hexagons that share edges with neighbouring subdivided triangles.

To subdivide: turn every hexagon and pentagon into 6 resp. 5 triangles, then subdivide those in 9 triangles again. See first picture I posted in this topic.

Last but not least: in your first post you state that normalization of all vertices happens as the very last operation. Your vertices will be more uniformly distributed if you normalize your vertices after each subdivision iteration.

The initial thing I have, an icosahedron, is nice and easy to define, if I just consider the vertices… sorta like this:


	link(v[0], v[1], v[2], v[3], v[4], v[5]);
	link(v[1], v[2], v[5], v[6], v[10]);
	link(v[2], v[3], v[6], v[7]);
	link(v[3], v[4], v[7], v[8]);
	link(v[4], v[5], v[8], v[9]);
	link(v[5], v[9], v[10]);
	link(v[6], v[7], v[10], v[11]);
	link(v[7], v[8], v[11]);
	link(v[8], v[9], v[11]);
	link(v[9], v[10], v[11]);
	link(v[10], v[11]);

Where each vertex maintains a list of its neighbours and link makes the connection both ways. So far so good. The bit that is proving hard for me to get my brain around is subdivision. I’m not doing any geometry at this stage, or spheres, or anything at all of that sort - just topology.

Cas :slight_smile:

I’m not sure why your focus is solely on topology…

If you focus on geometry, create that tessellated sphere, and calculate (‘bruteforce’) your topology from there (with the code I posted in this thread), you’d have had all the vertices, faces and all the ‘links’ within an hour of coding.

Anyway, it’s your party :slight_smile: I feel like I’m pushing my view a bit too much, so I guess I’ll let others jump in, that are willing to explore this problem purely from a topology point of view. :slight_smile:

Solving this using topology without any geometry to back it up, is not trivial, and certainly not a simple case of recursion (like with quadtrees or octtrees), because it involves shapes that go outside the boundaries of their parent (and sharing multiple parents) more often than that they stay within.

Cracked it. Will post some code tomoz.

Cas :slight_smile:

Can’t wait :slight_smile: