# What I want to achieve

I want to generate sphere planet world, seamless of course, for RTS game like Planetary Annihilation: TITANS, which means I want:

• place walking agents on sphere, move them on a sphere surface, be able to do A* pathfinding
• generate polygonal regions on sphere like Voronoi
• have world divided by chunks (possibly reduced to 1D array) to effectively perform calculations like local avoidance.
• generate seamless surface heightmap based on noise functions AND not to store this height map as a texture but just store height overrides in chunks. This way I can have just uint seed and overrides instead of keeping huge heightmap texture.

# How would I do it in seamless 2D world

In 2D space things are really simple even if your world one axis seamless: 2D map can be imagined as cylinder, or both axis are seamless: 2D map can be imagined as torus.

## Coordinates

They are just float x, y. Moving is just adjusting coordinates. Distance between 2 points is just distance between 2D vectors. Seamlessness of moving is achieved by % operation with coordinates, like x = x % x_max.

## World as 2D grid

2D world can be represented as 2D grid where each cell is a chunk with which I can effectively store and access data, because 2D grid is reduced to 1D array. 2D position can be easily converted to chunk index and back.

## Heightmap noise generation

Seamless noise can be tricky, but here we have golden treasure of map noise generation from red blob games.

## Presenting world and LOD

I would use simple plane mesh with rectangular connection of triangles to represent terrain of 2D world. LOD then could be implemented by recursively add vertices into mesh cells like tree. Again with 2D world it is simple because mesh / data / LOD are all grid based.

# Problems of doing same with sphere world

Because claimed / x-seamless / xy-seamless 2D worlds are all not really the sphere (they are plane / cylinder / torus in terms of wrapping) there can't be 1:1 transformation between 2D and spherical 2D world.

When trying to wrap rectangular around sphere something should be disturbed. It could be that we actually run all logic in 2D but project coordinates on sphere, but we will get disturbed positions near poles.

Or we can run logic on sphere and project positions back to 2D then we will get disturbed 2D representation like what happens with earth map when it represented in 2D, which is what called Equirectangular projection.

One way or another I need some "grid" representation of chunks, because in the end I need to test what chunks of sphere camera see and effectively cull objects which are not in visible chunk.

# Cubemap solution

Representing sphere as cubemap kills all problems working with sphere, because sphere now represented as 6 2D grids.

• Constructing mesh is just get cube (or six plane faces) with vertices adjusted in a way they lay on unit sphere. LOD is now also possible because we can work with faces separately.
• Store and access needed chunk now isn't a problem also because now we are back on 2D grids.

## Problem with cubemap coordinate system

Many thanks again to red blob games and this article in particular. Here we can find a way to implement coordinate system for cubemap world. But moving across faces is a huge mess, not only because it require to map direction for each face but also because when both x and y have to be wrapped to another face it becomes ambiguous which face to choose. So moving on cube map becomes problematic for corner cases.

## Use cubemap only for data

I think it is possible to use spherical coordinate system latitude and longitude and reimplement common operations like moving, getting distance, etc. but on sphere instead of 2D surface and use cubemap representation only for storing data in and read from chunks.

# Where to live finally?

• Living on sphere means to deal with spherical calculations which are more complex to understand and expensive because require trigonometrical functions. But then coordinate system becomes natural because we actually live on sphere and just map it on cubemap when need to work with rectangular chunks.
• Living on cubemap means to deal with cubemap coordinate system which is complex core of all this approach also producing prohibited moving cases. But in return we get nice and simple 2D calculations for everything with natural storing data in rectangular chunks and only use sphere to represent our world in 3D as a planet.

# What I want as an answer

Maybe I already answered my question and there is no place to answer anymore and I just need to chose one way or another comparing pros and cons. But maybe I miss something, maybe there is another smart way to do what I need without overcomplicating everything. I will appreciate any advice.

• Another option is to use an underlying icosahedral structure, which means less distortion when inflating it to a sphere. This means more edges/seams to deal with, but they follow a reasonably neat pattern you can exploit. Commented Aug 4 at 11:05
• "I'm" tethered to the center of the sphere. Having not thought about it before, but thinking of it now, I would be thinking in term of poles, the equator, latitudes and longtitudes. Some jurisdictions "square" things out with "land systems" (creating "townships"; etc.) that one can emulate. Commented Aug 4 at 19:30

I'm not a fan of using angles as a source of truth, very often you then need to deal with wrapping and in just about every case you need to feed it through a trig function to get the actual values you care to do math with.

Any angle can be stored as a 2D normalized vector from the origin, the components of which will be the sin and cos of the angle. Using the various trig identities you can perform most calculations on that representation without needing to get the actual angle.

Extending from that for a lat/long position on a sphere (which are angles relative to the equator and prime meridian) you can store the locations as normalized 3D directions from the center of the planet. This gives you the coordinates to lookup in a cube map, and doesn't do anything weird on any seams (because there are none).

Also if you spend a bit of time working out the math required you'll find they might be simpler than you expect: The shortest distance between 2 points on the unit sphere for example is the arccos of the dot product. Getting the great circle direction from one point to another is 2 cross products, or you get the direction by projecting the other point onto the tangent plane of the first point (the imaginary place that touches the surface of the sphere at that point) and get the direction from that.

Of course those 3 components doesn't gives you the heading of the unit (the way they are facing) So we could add a 4th component to... wait a minute quaternions? What are you doing here?

Joking aside quaternions would simplify some things while complicating others depending on whether you only needed the position and the embedded heading just complicated matters. So you'll have to judge for yourself which strategy you go with.

• +1 to the suggestion to prefer vectors over angles. I use this advice whenever I can in my code, and it frequently leads to more concise, clearer code with fewer edge cases, and often better performance too because I don't need to compute transcendental functions. Yes, you're juggling 3 coordinates instead of 2, but they're often going through SIMD-friendly operations. Commented Aug 4 at 10:58
• First of all thank you for great answer! I have few questions: * I understand part of shortest distance which called "GreatCirlce", this is how we get actual distance between two points on sphere * What you mean is 2 cross products? I understand part with calculating point projection on tangent plane but what is 2 cross products * To Lerp pos between points we use Slerp, right? * How can I rotate normal vector to represent moving to some direction with some 2D vector velocity? Can't find related info about it. Commented Aug 4 at 17:22
• @TonyMax a cross product is a 3D operation that gives you a vector perpendicular to 2 given vectors, In this case the first cross(self, other) will give you a vector that goes to the right, then you do cross(self, right) to get the forward vector. Commented Aug 4 at 19:26