Jump to content
Sign in to follow this  
Guest

Terrain Slope Lens - Need Help

Recommended Posts

Guest

Hi!

In my mod i want to do that: Terrain Slope Lens on Map.

What is it?

It consist in 160x90 dot markers distributed equaly along the map. Those dot markers are fixed in relation to the screen and cover all the screen. The markers changes it color acordingly to the slope of the terrain bellow it. User can zoom and move the map, but the dots dont change it position in relation to the screen.

Problem: How to make a map marker that is always on the center of the screen, even if you move and zoom the map?

Sorry if its complicated, i can try to explain more.

Thankyou a lot!

 

PS: The slope of a position on the map can be get with:

_tSlope =  acos((surfaceNormal _mapPosition) vectorCos [0,0,1]);

_tSlope goes from 0 (plain) to 90 (maximum slope). I believe it cant be 90 exactly, to be right.

Share this post


Link to post
Share on other sites

Look into

https://community.bistudio.com/wiki/ctrlMapScreenToWorld

Feed your desired screen position ([0.5,0.5] for screen center) to that function, and you should get the map position coordinates.

In your case, you'd have to update the positions every time the mapview is moved/zoomed. As there's no particular eventhandler for that, you probably have to rely on the "Draw" eventhandler to update the markerpos. Map markers are always bound to map positions, so you will always have to convert screenspace to worldspace.

Make sure to run the "Draw" cycle only when the map is shown to save some FPS when not on the map view

_eh = ((findDisplay 12) displayCtrl 51) ctrlAddEventHandler ["Draw", '
    if (!visibleMap and !visibleGPS) exitWith {}; // Only go through all the hazzle if the map is actually open
How to process positions? Save the "current" positions of your markers at the end of each Draw cycle (oldpositions). Then get the new map center coordinates (above function) and manually calculate the new "should be" screen positions for all markers as offsets in screenspace. Then, simply convert screenspace coords to worldspace using the reverse of above functions. If you use the default main map, remember that this map is NOT fullscreen, so you should retrieve the actual map size using ctrlPosition on the map (finddisplay 12 displayctrl 51).

Also remember that that display 12 ctrl 51 is also used for the GPS / Minimap in Vanilla, it's just smaller. Using ctrlPosition should make it work for that as well, as it returns both position (x,y) and size (width, height).

A good additional read to better understand how Arma's screenspace works is KK's GUI tutorial on it: http://killzonekid.com/arma-scripting-tutorials-gui-part-3/

EDIT: Had some time, so I did a quick debug console example:

markerx = createMarkerLocal ["test", position player];
markerx setMarkershape "ICON";
markerx setMarkerType "hd_objective";
markerx setMarkerText "Test";
ehtest = ((findDisplay 12) displayCtrl 51) ctrlAddEventHandler ["Draw", '
    if (!visibleMap and !visibleGPS) exitWith {};
    _screencenter = (finddisplay 12 displayctrl 51) ctrlMapScreenToWorld[0.5,0.5];
    markerx setMarkerPos _screencenter;
'];
In this case, it doesn't matter where the marker is created, as I always move it to map center as soon as I open the map. In your case, you proabably want to set up a marker matrix with positions in screenspace(!). Then you can use a foreach loop inside the eventhandler to update each marker:

// On mission init
slopemarkers = [];
_mapcontrolsize = ctrlPosition(finddisplay 12 displayctrl 51);
for "_i" from 0 to 159 do  {
    for "_j" from 0 to 89 do { _marker = createMarkerLocal [("slope" + str(_i) + "_" + str(_j)), [0, 0]];
        _marker setMarkershape "ICON";_marker setMarkerType "hd_dot";
        slopemarkers pushBack [_marker, [(_mapcontrolsize select 0) + (_i / 160 * (_mapcontrolsize select 2)), (_mapcontrolsize select 1) + _j / 90 * (_mapcontrolsize select 3)]];
    }
};
ehtest = ((findDisplay 12) displayCtrl 51) ctrlAddEventHandler ["Draw", '
    {
        _marker = _x select 0;
        _screenpos = _x select 1;
        _worldpos = (finddisplay 12 displayctrl 51) ctrlMapScreenToWorld _screenpos;
        _marker setMarkerPos _worldpos;
    } foreach slopemarkers;
'];
However, you will notice your large number of markers will cripple your FPS when in mapview. All this converting from screen to world space and setMarkerPos is pretty costly in performance. You can add an additional check to see if the map has been moved or zoomed(changes in ctrlMapScale for zoom, changes in mapcenter for move) to only update the map then, but it still will cause severely lagginess while panning&zooming. Maybe add a reasonable delay on the updates (simply add a sleep 3 for "update only once every 3 seconds") in addition to that. I'll leave that to you to figure out :) .

Share this post


Link to post
Share on other sites

Instead of using "normal" markers could you not just use a display to draw the dots straight in screen-space. Of course you could still use the same texture as the map markers does.

My dialog control is a little bit rusty but it should be possible to design a display that loads when the map is loaded, fills the screen with the "grid" and then updates each dot every frame (or so).

Pros:
Only marker color needs to be updated. Markers will remain fixed in screen space.

Marker color can be anything in the [r,g,b,a] format. Not just preset colors.
Feels the most professional (read "least hacky") in my book anyway.

Cons:
Requires a bit of work for spacing to be same on all screens, if that is considered important.

 

To do:
1. Design a display to work with the map. Main map control can be retrieved using findDisplay 12 displayCtrl 51.  

2. In the display's onLoad eventhandler spawn a loop to update the marker colors. The cool thing is that since you are working with a UI controls you can with a little bit of math do some nice color-transitions e.g. _color = [x, 1, 0, 1] where is a function of the slope.
3. In the loop iterate over each dot and input the screen-space coordinates into ctrlMapScreenToWorld to get the matching world position. 
4. When the map is closed simply close the display with closeDisplay or something similiar.

Share this post


Link to post
Share on other sites

Yup, a display solution probably would be much better for FPS. You still will have to do a lot of map-to-screen calls to get each "cell's" position, but at least you don't have to move things around all the time.

Share this post


Link to post
Share on other sites
Guest

Thankyou for all the help.

 

160X90 dots may be too much. I believe less can be used, and if the user want a more detailed info of some area, he just need to zoom in that area, so the "dots/meters" will increase.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×