The Intriguing Problem with Map Wrapping

Started by
12 comments, last by alvaro 11 years, 4 months ago
Thanks for the replies so far guys.

Anyway, I'm only making a game, not necessarily a simulation, so distortion, however much it may occur, won't really matter much; as the planet is randomly generated, the map will look nothing like earth and so no distortion could be detected in the representation of the map.

At this point, working with two squares seems to be my best bet, but here's the new question: how would I navigate east to west and represent that on the map?

I'll explain in greater detail later, but I'm quite busy at the moment.

Thanks again,

Selenaut
Advertisement
Humanity has been dealing with this problem for a long time, because we have a round planet and flat paper to make maps of it. You can get an overview of the subject from the Wikipedia page on map projections. The more general problem of describing mathematical objects using charts is dealt with in Differential Geometry.

I think the cleanest thing to do for planet-wide terrain generation in a game is to impose any mesh you want on the sphere (I would start with a cube map) and then use 3D noise to generate elevation.

I don't know why you want to rotate the map as the player moves. Moving the camera seems a lot easier.
Since I'm going to be having the view act similarly to Dwarf Fortress, kind of like bird's-eye, moving the player and moving the camera are essentially the same thing. The reason I'd like to rotate the land around the player is to prevent confusion at the wrapping edges.

So, to recap so far: Squares don't make spheres; I'm going to need a LOT more help trying to figure out how to even begin wrapping something like this; and 2D objects projected to 2D planes is nigh impossible.

Hm.

I have been thinking on and off about this all day; perhaps I could create two circles and wrap those, but the problem is loss of precision near the equator, and much more noise towards the poles. Is it possible to create an equidistant grid of sorts on a circle?

Also:

I think the cleanest thing to do for planet-wide terrain generation in a game is to impose any mesh you want on the sphere (I would start with a cube map)...

The provlem with making a cube UV is the wrapping mechanics of all of the edges. Also, I'd have to generate each face independently, and stitch them together, since the resulting UV shape isn't rectangular.

and then use 3D noise to generate elevation.

What do mean by 3D Perlin noise? I'm completely familiar with the concept, but I'm confused as to how I would use it in this circumstance.

For example, go look at the earth's atlas. When you said that wrapping around from the south pole gets you to the north pole, that's wrong.

I know this. That's why I'm having my problem. The land I'm generating looks like this. As you can see, it wraps in both the x and y direction; this may solve any confusion as to what exactly I meant by wrapping. If any of you are familiar with Java (I'm assuming so), here's the code for generating this:

import java.util.Random;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.*;
import javax.imageio.ImageIO;
public class Perlin {
static int p = 8;
static int size = (int)Math.pow(2,p)*4; //Multiply this by any power of 2 to get a good size image; you may be able to use other numbers, but I wouldn't reccomend it
static int h = 0;
public static void main(String[] args) throws IOException {
Random rand = new Random();
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
WritableRaster raster = image.getRaster();
int[][] height = new int[size][size];
int a = 0, b = 0, c = 0, d = 0, highest = 0, lowest = -1;
for(int i = p-1; i > 0; i--){
int range = (int)Math.pow(2,i);
int arraySize = size/range;
int[][] current = new int[arraySize][arraySize];
for(int pointx = 0; pointx < arraySize; pointx++){
for(int pointy = 0; pointy < arraySize; pointy++){
current[pointx][pointy] = rand.nextInt(range); //Getting interpolation points; array gets more precise but less influential with each iteration
}
}
for(int x = 0; x < size; x++){
for(int y = 0; y < size; y++){
int left = (x/range)%(size/range); //Getting corners; automatically wraps both horizontally and vertically using the modulus method
int right = (x/range + 1)%(size/range);
int up = (y/range)%(size/range);
int down = (y/range + 1)%(size/range);
a = current

[up]; //Setting corners
b = current

[up];
c = current

[down];
d = current

[down];
h = (int)cosineInterpolate(cosineInterpolate(a, b, ((x%range)*1.0)/range), cosineInterpolate(c, d, ((x%range)*1.0)/range), ((y%range)*1.0)/range); //interpolates between the closest boundaries by interpolating horizontally on the top and bottom, then interpolating vertically
height[x][y] += h;
}
}
for(int x = 0; x < size; x++){ //Used to correct the contrast
for(int y = 0; y < size; y++){
if(height[x][y] > highest){
highest = height[x][y];
}
if(height[x][y] < lowest || lowest < 0){
lowest = height[x][y];
}
}
}
}
highest -= lowest; //Used to correct the contrast
for(int x = 0; x < size; x++){
for(int y = 0; y < size; y++){
height[x][y] -= lowest; //Used to correct the contrast
}
}
lowest -= lowest; //Used to correct the contrast
for(int x = 0; x < size; x++){
for(int y = 0; y < size; y++){
int n = (int)(((height[x][y]*1.0)/highest)*255); //Correcting the contrast
int[] green = {0, n, 0};
int[] blue = {0, 0, n};
int[] yellow = {n, n, 0};
if(height[x][y] > 128){
raster.setPixel(x, y, green);
}
else if(height[x][y] < 128){
raster.setPixel(x, y, blue);
}
else{
raster.setPixel(x, y, yellow);
}
}
}
File file = new File("perlinTest.png");
ImageIO.write(image, "png", file);
}
public static double cosineInterpolate(double a, double b, double x){ //Interpolates between the two specified points at a fraction x from 0-1
double ft = x * Math.PI;
double f = (1 - Math.cos(ft)) * 0.5;
return a*(1-f) + b*f;
}
}



Run that yourself, see if that gives you any ideas; I'll keep working on how I want to wrap it.

Thanks guys,

Selenaut
If your map is supposed to be a map of elevation on a sphere, the top edge of the map should have a single constant value (elevation of the North pole), and similarly for the bottom edge. So your map is obviously not that of a function on the sphere.

What I meant by using 3D Perlin noise is something like this:
for (j=0; j<HEIGHT; ++j) {
for (i=0; i<WIDTH; ++i) {
longitude = 2*pi*(i/WIDTH-0.5);
latitude = pi*(j/HEIGHT-0.5);
x = sin(latitude)*cos(longitude);
y = sin(latitude)*sin(longitude);
z = cos(latitude);
elevation[j] = perlin_3D_noise(x, y, z);
}
}


That will generate a map that comes from a well-behaved noise function on the sphere, and it will have all the right features, including constant values for the poles.

This topic is closed to new replies.

Advertisement