Project Topographic map MFD mode (Experimental for Orbiter 2015 Beta only)

Topper

Addon Developer
Addon Developer
Donator
Joined
Mar 28, 2008
Messages
666
Reaction score
20
Points
33
The plan is to develope a MFD mode for Orbiter 2015+ to display a topographic map. To remove the discussion from the "Orbiter Beta" thread I created this developement thread.

The Situation:
The MFD is working, but still now it has some performance issues:

1. The used method "oapiSurfaceElevation" is a bit slow to render the complete map on each "MFD Update". But therefore Enjo had the Idea to
"discretize the map, fill it with calculations, cache them, and then once enough movement has been made in a given direction, (using floor / ceil), shift the map, but query only for the changed part (incoming from the border), and query the cache for the remaining points?"

2. I used the function "skp->LineTo(x...,y...);" to paint a pixel. It's also a bit slow even if I want to paint each pixcel just with the same color. So I need a better way to "render a picture" on the MFD display.

3. Still now, the representation of the elevation is black (low) and white (high) only. So I'm looking for an alogrithm which can give me a colorized picture like this:


PIA19974-Ceres-Dawn-GlobalMap-Annotated-20150930.jpg

And this is how it looks right now:
topoMFD.png


The very simple experimental method to create this picture is the following which can be called from the update method of the MFD mode:

Code:
void SurfaceSpeedMFD::drawTopoMap(oapi::Sketchpad *skp)
{        
    static double highest = 5000;
    static double lowest = -5000;
    VESSEL*v = oapiGetFocusInterface();
    double lng_Vessel, lat_Vessel, rad;
    v->GetEquPos(lng_Vessel,lat_Vessel,rad);
    double heading = ORBITERTOOLS::getFlightVectorHeading(v);            
    const double zoom = 500;
    for (int x=-width/2; x<width/2; x++)
    {
        double lng_left, lat_left, rad;
        ORBITERTOOLS::pointRadialDistance(lat_Vessel,lng_Vessel,heading+PI05,x*zoom,v,&lat_left,&lng_left);
        skp->MoveTo(x+width/2,0);
        for (int y=-height/2; y<height/2; y++)
        {            
            double lng_pixel, lat_pixel;
            ORBITERTOOLS::pointRadialDistance(lat_left,lng_left,heading,y*zoom,v,&lat_pixel,&lng_pixel);
            double elevation = oapiSurfaceElevation(v->GetGravityRef(),lng_pixel,lat_pixel);    
            oapi::Pen *elevationPen;
            if (elevation > highest) highest = elevation;
            if (elevation < lowest) lowest = elevation;
            double elevation255 = ((elevation + (0 - lowest)) / ( highest - lowest)) * 255;            
            elevationPen = oapiCreatePen(1, 1, RGB(elevation255,elevation255,elevation255));    
            skp->SetPen(elevationPen);
            skp->LineTo(x+width/2, height-(y+height/2));            
            oapiReleasePen(elevationPen);
        }
    }    
}
And the supporting methods from "ORBITERTOOLS":
Code:
        static double getFlightVectorHeading(VESSEL *v)
        {
            VECTOR3 shipAirspeedVector;
            v->GetHorizonAirspeedVector(shipAirspeedVector);
            normalise(shipAirspeedVector);
            double vector = acos(shipAirspeedVector.z/sqrt((shipAirspeedVector.x*shipAirspeedVector.x)+(shipAirspeedVector.z*shipAirspeedVector.z)));
            vector = atan2(shipAirspeedVector.x, shipAirspeedVector.z);
            //if (vector > 2*PI) vector -= 2*PI;
            return vector;
        }

        static void pointRadialDistance(double lat1, double lon1, double bearing, double distance, VESSEL * v, double *lat2, double *lon2)
        {
            double rdistance = distance / oapiGetSize(v->GetGravityRef());
            * lat2 = asin(sin(lat1) * cos(rdistance) + cos(lat1) * sin (rdistance) * cos(bearing) );
            * lon2 = lon1 + atan2( sin (bearing) * sin (rdistance) * cos (lat1), cos (rdistance) - sin(lat1) * sin (* lat2 ));
        }
 
Last edited:

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Great add-on!

Orthographic projection would be more suitable IMHO, because orthographic projection is what you would see from the spacecraft looking down.

ortho.gif


The downside is, you have to re-project the map as the spacecraft moves which may prove to be too expensive...

On the other hand, if you split the planet into, say, 10 deg X 10 deg tiles and precalculated projection for each tile (assuming spacecraft in the center of the tile) then maybe you could get acceptable combination of performance and distortion...

---------- Post added at 12:38 AM ---------- Previous post was at 12:19 AM ----------

3. Still now, the representation of the elevation is black (low) and white (high) only. So I'm looking for an alogrithm which can give me a colorized picture like this:

http://stackoverflow.com/questions/7706339/grayscale-to-red-green-blue-matlab-jet-color-scale
http://stackoverflow.com/questions/7251872/is-there-a-better-color-scale-than-the-rainbow-colormap
http://cresspahl.blogspot.com/2012/03/expanded-control-of-octaves-colormap.html
 
Last edited:

Enjo

Mostly harmless
Addon Developer
Tutorial Publisher
Donator
Joined
Nov 25, 2007
Messages
1,665
Reaction score
13
Points
38
Location
Germany
Website
www.enderspace.de
Preferred Pronouns
Can't you smell my T levels?
I also need to improve the drawing of the map:
Now I just use Sketchpad->LineTo (x,y) and I need to create and release the "Sketchpad Pen" for each pixel just to change the color.
Maybe there are better solutions but I guess thats stuff for the sdk section later on...

Creating and releasing a pen in an O(N^2) algorithm could lead to slowdowns indeed. I would do the following:
Iterate the in-memory picture and add to an std::multimap a structure containing the coordinates, with elevation255 being the key (as integer!).
PHP:
struct Coords
{
   Coords(int x, int y) : x(x), y(y) {}
   int x, y;
};

typedef std::multimap<int, Coords> ElevMap;
ElevMap CalcMap()
{
   ElevMap yoMap;
   for (int x=-width/2; x<width/2; x++)
   {
      for (int y=-height/2; y<height/2; y++)
      {
         // calculate elevation
         yoMap[elevation255] = Coords(x,y);
      }
   }
   return yoMap;
}

Now for the drawing routine. The assumption is, that since you put everything into a multimap, the elements are sorted by the elevation. Until the elevation changes (hence integer elevation for easier verification), the Pen isn't changed at all. This gives pessimistically at most 255 pen switches, instead of guaranteed N*N (290*290 = 84100!).
PHP:
void Draw(const ElevMap & yoMap, oapi::Sketchpad *skp)
{
int elevation255Prev = -1;
oapi::Pen * elevationPen = NULL;
for (ElevMap::const_iterator it = yoMap.begin(); it != yoMap.end(); ++it)
{
   const int elevation255Curr = it->first;
   if (elevation255Curr != elevation255Prev || elevation255Prev == -1)
   {
      // Time to change or initlialize Pen
      if (elevationPen)
      {
          oapiReleasePen(elevationPen);
      }
      elevationPen = oapiCreatePen(1, 1, RGB(elevation255Curr,elevation255Curr,elevation255Curr));  
      skp->SetPen(elevationPen);
      elevation255Prev = elevation255Curr;
   }
   // Obtain x and y from the Coords structure
   const int x = it->second.x;
   const int y = it->second.y;
   skp->MoveTo(x,y)
   skp->LineTo(x,y+1)); // Note here *
}
if (elevationPen) // In case the multimap had no elements, the Pen remains NULL
{
     oapiReleasePen(elevationPen);
}
}
* Another thing to note is that I only draw only one pixel instead of a whole line from top to the ordinate y in question, as in your example. You could experiment if this gives any speed up at all, by drawing from 0 instead of y. It wouldn't make much difference if the drawing core used double buffering, but we can't assume it, and it may be graphical client dependent.

Topper said:
3. Still now, the representation of the elevation is black (low) and white (high) only.
Please make it an option, switchable via an MFD button. I think I prefer grey scale, as it doesn't hurt my eyes so badly.

---------- Post added at 08:23 AM ---------- Previous post was at 07:07 AM ----------

The last thing is - only after you've separated the calculation function from the drawing function, you can say with a higher probability which subroutine slows your application down. Comment out (or dynamically switch off) one of them or the other, and observe the framedrop.
 
Last edited:

Enjo

Mostly harmless
Addon Developer
Tutorial Publisher
Donator
Joined
Nov 25, 2007
Messages
1,665
Reaction score
13
Points
38
Location
Germany
Website
www.enderspace.de
Preferred Pronouns
Can't you smell my T levels?
Errata:
Since the ElevMap will be a relatively large object, maybe it will be somewhat faster not to return it by value, but pass it by reference for initialization:

PHP:
void CalcMap(ElevMap & yoMap) 
{ 
   for (int x=-width/2; x<width/2; x++) 
   { 
      for (int y=-height/2; y<height/2; y++) 
      { 
         // calculate elevation 
         yoMap[elevation255] = Coords(x,y); 
      } 
   } 
}
 

Enjo

Mostly harmless
Addon Developer
Tutorial Publisher
Donator
Joined
Nov 25, 2007
Messages
1,665
Reaction score
13
Points
38
Location
Germany
Website
www.enderspace.de
Preferred Pronouns
Can't you smell my T levels?
Topper, are you planning to return to the project? If not in an forseen future, than I'd gladly pick it up.
 

Topper

Addon Developer
Addon Developer
Donator
Joined
Mar 28, 2008
Messages
666
Reaction score
20
Points
33
Topper, are you planning to return to the project? If not in an forseen future, than I'd gladly pick it up.

I'm a bit busy at the moment so if you want to pick it up just do it.
 

Enjo

Mostly harmless
Addon Developer
Tutorial Publisher
Donator
Joined
Nov 25, 2007
Messages
1,665
Reaction score
13
Points
38
Location
Germany
Website
www.enderspace.de
Preferred Pronouns
Can't you smell my T levels?
I did it! Check this out:
[ame="http://www.orbithangar.com/searchid.php?ID=6853"]Topographic Map MFD for Orbiter BETA[/ame]

It turned out that not only the Pen reinitialization was the bottleneck, but also drawing individual pixels on the Sketchpad. Third bottleneck in the line was the actual calculation.

The trick was to create a temporary Surface, put pixels there and blit the surface onto Sketchpad's surface in the Update method. Putting the pixels onto the Surface had to be done in oapiPreStep() (not in Update()) in small steps (only n-lines per refresh), not to take too much CPU time per refresh, which would otherwise make the simulation jerky.

There exists however a big problem currently. As written in the documentation: "The only setting that make the MFD possible to work is Jarmonik's DirectX9 client in windowed mode."

Topper: I realize that now you want to use it for Surface Speed MFD. However, notice that I polluted the code with my libraries a little bit :)
 
Last edited:

Topper

Addon Developer
Addon Developer
Donator
Joined
Mar 28, 2008
Messages
666
Reaction score
20
Points
33
Hi why is the link not working anymore?

Topper
 

Enjo

Mostly harmless
Addon Developer
Tutorial Publisher
Donator
Joined
Nov 25, 2007
Messages
1,665
Reaction score
13
Points
38
Location
Germany
Website
www.enderspace.de
Preferred Pronouns
Can't you smell my T levels?
Hi
I cannot talk about it publicly right now (however creepy it may sound).
 

Ripley

Tutorial translator
Donator
Joined
Sep 12, 2010
Messages
3,133
Reaction score
407
Points
123
Location
Rome
Website
www.tuttovola.org
I'm guessing something "big" here... :thumbup:
Let's wait and see if it'll turn true.
 
Top