OpenXcom Forum
Contributions => Programming => Topic started by: SupSuper on June 18, 2010, 07:19:22 am
-
The X-Com game world consists of a flat map with a series of polygons representing the land masses, like so:
(https://img534.imageshack.us/img534/2180/400pxxcomrender2.gif)
(image from Ufopaedia.org)
Every point on the map has a latitude and a longitude, just like the real thing. To turn the map into a pseudo-globe, every point is converted from lat/lon to X/Y, like so:
x = radius * sin(lon) * cos(lat)
y = radius * (sin(lat) * cos(rotLat) + cos(lat) * sin(rotLat) * cos(lon))
(radius = globe radius, lon = longitude, lat = latitude, rotLat = rotation on the latitude, sin = sine, cos = cosine)
Now I need to invert these formulas to reverse the process (turn points on the globe into points on the flat map), so they become something like:
lat = ...
lon = ...
So... anyone have any ideas how to solve this?
-
Untested, but I think you need something like
z = sqrt( radius2 - x2 - y2 )
lon = arccos( z / radius )
lat = atan2( y, x )
Used quotes, cos I couldn't superscript in code ;D
Reference: https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
Edit: Hmmm, just thinking, this might actually be expecting an (X, Y, Z) in 3D cartesian space, not on the 2D map...
Edit 2: But then again, your original equations were from the same page, and you don't calculate z (hence why I added the reverse calculation)
-
So... anyone have any ideas how to solve this?
I've thought about this before and I'm not sure if my idea is going to make any sense after typing it out, but here goes:
The geoscape screen is always going to be 320x200, and you already (obviously) have done the math to project the globe into screen coordinates. What I am thinking is do a projection with (0,0,0) rotation and then save a "mapping" of which lat/long gets drawn at each location in the 320x200 screen space. Then when the mouse interacts with it, you have the latitude and longitude as if the globe were at (0,0,0) and you can "rotate" the mouse click based on how the globe is being displayed.
Actually if you update the mapping every frame that you display then you don't even have to "rotate" the lat/long you get from the mouse interaction
edit: ummm I just read your OP again and are you converting to a sphere before you do any drawing? because maybe you can build the map i've described by doing the polar coordinate -> sphere -> screen pixel calculation for each pixel with a test program and then just make the mapping a struct
edit: haha I just read someone elses solution to your polygon problem and that solution is similar.. store meta-info for screen coords
-
x = radius * sin(lon) * cos(lat)
y = radius * (sin(lat) * cos(rotLat) + cos(lat) * sin(rotLat) * cos(lon))
(radius = globe radius, lon = longitude, lat = latitude, rotLat = rotation on the latitude, sin = sine, cos = cosine)
Now I need to invert these formulas to reverse the process (turn points on the globe into points on the flat map), so they become something like:
lat = ...
lon = ...
So... anyone have any ideas how to solve this?
Obviously, solving these equations should give zero, one or two distinct lat,lon pairs as a result for each x,y on input. Don't know if it's easy to solve these equations analytically (my math skills got a bit rusty nowadays). But being lazy, one can easily solve the problem numerically, by using Newton's method (https://en.wikipedia.org/wiki/Newton%27s_method) for example ;)
-
I can't say math is exactly my forte either, so so far all the solutions are scaring the crap out of me. :P But I'll give in and try them if nothing better comes along.
For the curious, this is the current formula:
lat = asin((y - cenY) / r);
lon = asin((x - cenX) / (r * cos(lat))) - rotLon;
Which works fine... as long as you don't rotate the globe vertically (no rotLat in there), that's the real problem. ;)
Typically spherical coordinates (r, lat, lon) convert into cartesian (x, y, z) easily (Wikipedia has the formulas as posted earlier), but in this case the sphere can be rotated which changes the axis, and they need to be flattened for the screen into (x, y). Also the radius is constant since we always want the sphere's surface. But if it's easier for you, you can think of the process as:
(r, lat, lon) -> (x, y, z) -> (x, y)
(r, lat, lon) <- (x, y, z) <- (x, y)
Early on I did try drawing the globe with (x, y, z) through 3D rendering but that just caused a whole lot more problems, so... here we are. :P
-
SupSuper: Longitude is thrown out wildly with that - _rotLon
If anybody else is looking at this, my code segment so far is
void Globe::cartToPolar(Sint16 x, Sint16 y, double *lon, double *lat)
{
double dr = (_radius * _zoom);
double dx = (double)(x - _cenX);
double dy = (double)(y - _cenY);
double dz = sqrt( (dr * dr) - (dx * dx) - (dy * dy) );
*lon = asin( dx / (dr * dz) );
*lat = asin( dy / dr ) - _rotLat;
https:// Check for errors
if (!(*lat == *lat && *lon == *lon))
{
*lat = COORD_OUT_OF_BOUNDS;
*lon = COORD_OUT_OF_BOUNDS;
}
else
{
*lon = longitudeLoop(*lon - _rotLon);
}
https:// Debug
https:// cout << x << ", " << y << ": " << _rotLon << ", " << _rotLat << endl;
}
With this, you can rotate it any way you wish and the selection will remain central (X axis) to the planet.
-
I found the solution! https://en.wikipedia.org/wiki/Orthographic_projection_(cartography) (https://en.wikipedia.org/wiki/Orthographic_projection_(cartography))
The functions are frightening but, they work ;D, and now that I got these issues out of the way I can finally work on giving the globe more functionality. Case closed!
-
Excellent news!