• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

SDK Converting Heightmap XML data to displacement texture

Messages
134
Country
italy
Hello everyone. Got a bit of a techy question.
Does anybody know a way to convert an heightmap from MSFS to a usable displacement black/white image?

Once you apply an heightmap to a surface, it "wraps" around it so the single vertices of the heightmap should be the ones of the actual ingame terrain. That would be damn useful to model objects around existing terrain without needing to adapt the terrain to the models





Immagine



And this is the heightmap line in the XML

XML:
<Rectangle displayName="HEIGHT_CAMPANILE" groupIndex="1" width="73.499985" falloff="500.000000" surface="{47D48287-3ADE-4FC5-8BEC-B6B36901E612}" priority="0" precision="TRUE" latitude="46.16429628849714" longitude="8.32601261387702" altitude="482.65115316305310" latitude2="46.16495747442070" longitude2="8.32601261482440" altitude2="482.65155906789005">        <Heightmap width="64" data="528.354797 528.375000 528.371399 528.375000 528.400757 528.417419 528.445984 528.501770 528.593750 528.659790 528.704651 528.771851 528.856689 528.973022 529.104065 529.165894 529.215210 529.263672 529.328796 529.442017 529.539673 529.580750 529.603455 529.590332 529.544128 529.530396 529.490540 529.446716 529.417969 529.404968 529.370667 529.303284 529.230042 529.178833 529.128662 529.020203 528.897583 528.770081 528.656677 528.559143 528.464783 528.356995 528.216309 528.057312 527.857361 527.606995 527.349548 527.118958 526.919128 526.680176 526.411804 526.136353 525.885559 525.648804 525.432373 525.246094 525.106445 524.943298 524.728516 524.504883 524.246277 523.967651 523.709839 523.464111 528.697327 528.694397 528.695312 528.723633 528.774719 528.775574 528.791016 528.844666 528.923584 528.987610 529.029053 529.058472 529.126892 529.207886 529.279907 529.342285 529.382935 529.412476 529.452209 529.564331 529.675903 529.731262 529.739136 529.712891 529.668396 529.657898 529.625488 529.581116 529.544373 529.531372 529.490234 529.398560 529.344910 529.314575 529.276001 529.186279 529.092346 529.005310 528.925842 528.849854 528.767578 528.675476 528.555725 528.405640 528.223206 ETC ETC ETC </Rectangle>



Just a portion of that, due to charachters limit :D

Can't get around it so far, but this would be awesome to avoid "fixing" the terrain around the model, and start modeling around the terrain
 
That is definitely an option and if you have some python (or any other language for that matter) knowledge, you can quite easily do this, and vice versa you could also go from modelling software to MSFS rectangle.

The rectangle's structure is just one big array of data with a length of the heightmap width*height. If I recall correctly from my own experiments, it starts at the top left of the rectangle and scans each row like a television would do. Additionally, you'll have to make sure to correct the elevation data to a suitable data reference model. Since the elevation data in MSFS uses the EGM2008 geoid model which differs from an ellipsoid model which you often get from DEM sources.

So a rough outline of the process would be:

1) Read the "<Heightmap>...</Heightmap>" Data from left to right into an m*n array with m ="width" attribute and n = the length of data/m (or the height of the heightmap in your MSFS scenery editor, but this isn't how it's stored in XML).
2) By using the boundaries defined by the latitude, longitude and latitude2, longitude2 you can sample an EGM2008 geoid model to correct each data point and obtain the ellipsoid elevation for said point.
3) Assign the data for each point to a raster file using GDAL or equivalent geographic library. This is going to be the hard part but there's a lot of knowledge available on this area.
4) Import the raster into Blender using the Blender GIS addon or if you use another program import it using another plugin.

Alternatively, you could directly go from step 2) to a 3D by converting a mesh from those data points but I'd say it depends on one's comfort with doing some index math if that's really easier.

This is of course an oversimplification of the whole process, so feel free to shoot me a message if you have more questions!
 
That is definitely an option and if you have some python (or any other language for that matter) knowledge, you can quite easily do this, and vice versa you could also go from modelling software to MSFS rectangle.

The rectangle's structure is just one big array of data with a length of the heightmap width*height. If I recall correctly from my own experiments, it starts at the top left of the rectangle and scans each row like a television would do. Additionally, you'll have to make sure to correct the elevation data to a suitable data reference model. Since the elevation data in MSFS uses the EGM2008 geoid model which differs from an ellipsoid model which you often get from DEM sources.

So a rough outline of the process would be:

1) Read the "<Heightmap>...</Heightmap>" Data from left to right into an m*n array with m ="width" attribute and n = the length of data/m (or the height of the heightmap in your MSFS scenery editor, but this isn't how it's stored in XML).
2) By using the boundaries defined by the latitude, longitude and latitude2, longitude2 you can sample an EGM2008 geoid model to correct each data point and obtain the ellipsoid elevation for said point.
3) Assign the data for each point to a raster file using GDAL or equivalent geographic library. This is going to be the hard part but there's a lot of knowledge available on this area.
4) Import the raster into Blender using the Blender GIS addon or if you use another program import it using another plugin.

Alternatively, you could directly go from step 2) to a 3D by converting a mesh from those data points but I'd say it depends on one's comfort with doing some index math if that's really easier.

This is of course an oversimplification of the whole process, so feel free to shoot me a message if you have more questions!
I have a ton of questions, unfortunately I have no knowledge of python or any other programming language, my IT culture is pretty much artistical (3D modeling, music and so on). I though thank you for your input, I might really want one day to start learning Python but that's a task that's really off the boundaries of my knowledge so far, but I know your effort in explaining and your clearness are priceless here! Maybe one day someone more skilled than me (or more correctly with a more suitable and different skillset) could come up with a tool which is usable ;)
Thank you mate, you've been more than exhaustin :3

Chris
 
No problems Chris and good luck with your project. Right now I have a small side project to do some stuff like exporting elevation to rectangles so I might as well look into the reverse option too. If I'm at that point, I'll make sure to share it! However, for now it's more a side project than my priority.
 
I came up with this using chatGPT, but I know that's gonna be a rough approach and probably not even usable. Still might be worth taking a look from someone more expertised than me:p
Might this work?
Sorry if it's sounds dumb but with my lack of knowledge that's the very first thing I wanted to give a try to, but I doubt it could be working out of the box :p

------------------------------------------------------------------------

Step 1: Parsing the Heightmap Data
---------------------------------------
You can use Python's `xml.etree.ElementTree` library to parse the XML data and extract the heightmap data. Here's a code snippet to get you started:


Python:
import xml.etree.ElementTree as ET


# Your XML data as a string
xml_data = """<Rectangle displayName="HEIGHT_CAMPANILE" ...> ... </Rectangle>"""


# Parse the XML data
root = ET.fromstring(xml_data)


# Extract the heightmap data
heightmap_data = root.find(".//Heightmap").attrib.get("data")
heightmap_data = list(map(float, heightmap_data.split()))


# Extract width attribute
width = int(root.attrib.get("width"))


# Calculate the heightmap rows (n) based on width
n = len(heightmap_data) // width


# Reshape the data into an m*n array
heightmap_array = [heightmap_data[i:i+width] for i in range(0, len(heightmap_data), width)]

Step 2: Sampling EGM2008 Geoid Model
---------------------------------------
You can use a geospatial library like `pyproj` to perform the geoid correction. Here's a code snippet to get you started:

Python:
import pyproj


# Define the EGM2008 geoid model
egm2008 = pyproj.Geod(ellps="WGS84", hgrid="egm2008_1")


# Define the latitude and longitude coordinates
lat1 = float(root.attrib.get("latitude"))
lon1 = float(root.attrib.get("longitude"))
lat2 = float(root.attrib.get("latitude2"))
lon2 = float(root.attrib.get("longitude2"))


# Create a list of corrected elevations
corrected_elevations = []


for row in heightmap_array:
    for lon in row:
        _, _, corrected_elevation = egm2008.fwd(lon1, lat1, 0, lon)
        corrected_elevations.append(corrected_elevation)


Step 3: Writing to a Raster File Using GDAL
--------------------------------------------
To write the data to a raster file using GDAL, you'll need to install the `gdal` library. Here's a code snippet to get you started:

Python:
from osgeo import gdal, osr, gdal_array


# Set up the raster dataset
driver = gdal.GetDriverByName("GTiff")
output_filename = "output_raster.tif"


# Create a new raster dataset
dst_ds = driver.Create(output_filename, width, n, 1, gdal.GDT_Float32)


# Define the spatial reference (assuming WGS84)
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)  # WGS84 EPSG code


# Set the dataset's projection
dst_ds.SetProjection(srs.ExportToWkt())


# Write the heightmap data to the raster
dst_ds.GetRasterBand(1).WriteArray(heightmap_array)


# Close the raster dataset
dst_ds = None
 
Failure on my try. The elevations are ellipsoid elevations. I didn't convert them (I just subtracted a 51+ meters to each elevation to adjust from the center of the heightmap to sea level). But they do seem to need earth curvature for each point.

What I did was to make a grid in Blender the dimensions of the rectangle, then duplicate that and delete (X) the faces and edges only. That gave me a grid of points as well. I saved each as OBJ. I then made a list of the rectangle elevations, and turned it into a spreadsheet column. I entered the grid of points OBJ as a spreadsheet, and overwrote the Z values with my elevation list. Turned the mess back into a point grid OBJ.
In blender, I imported the rectangle OBJ and the Point grid OBJ, then used a shrinkwrap modifier to wrap the rectangle to the point grid. That all works well. But importing the wrapped rectangle as an object shows the ends of the shrinkwrap well above the ground level of the heightmap... the whole grid of points needs to be warped to the earth.
 

Attachments

  • lich.zip
    1.5 MB · Views: 64
Alright, I've taken a look at your heightmap data and by rewriting some of my existing code I can get the vertices exported as an obj file, albeit in a somewhat brute force manner.

As you can see on picture 1, we're about 283m above the geoid, similarly to what the z size in blender indicates. The value is not exact because I'm not sure at what fidelity Asobo samples the geoid model so there is going to be some minor variation. Additionally, I have done very little in terms of earth curvature correction, not even checked if it's needed. The second picture shows the full rectangle and the third the corresponding vertices in blender. There is still some tweaks required in the math as I have some offsets that I am not accounting for but as a proof of concept, I think your idea might work!

rectangle_to_obj.jpg


Furthermore, your ideas do raise a lot more ideas and possibilities. Since we can easily create rectangles inside MSFS using an automated process, we can also extract default terrain data from MSFS. When adding a heightmap for the first time, the heightmap takes on the height values of the underlying terrain which can then be interpreted by any type of automated process like the one I use right now. So this means we can export terrain into our software of choice, be it GIS or 3D applications, edit the terrain (e.g. blending our custom terrain into the default terrain) and export back to the sim without having to iterate endlessly and sculpting inside the sim itself. This basically opens up another world of editing terrain which I feel could help a lot of users out there.
 
Sorry for using the Runway heightmap, I just grabbed the first one that came out of the XML :D glad you found a working method to do that, if and when you feel like sharing, I'd personally pay for that kind of service. I dunno if you're planning on making some terrain edition import/export suite (that's what it seems from what you've been explaining just above xD) but this sounds like the most valuable and useful / powerful tool I can think of about Airport making.
Whenever you feel like doing, I'm all down for that. Of course again, I'd pay for it, consider I'm using it for payware airports and that's just a fair trade, and the minimum I can do for the effort you've put in even just testing this on your end :)
 
It's definitely on my planning to release some kind of tool to edit elevation data inside and outside the sim. However, I'm not to keen on making it a paid service since this comes with a lot more responsibility for support and adding features etc. and since I am not a software developer, I feel like I cannot guarantee that desired standard. So my plan is to release the tool for free without any limitations on use but with the ability to buy me a coffee (or a beer, depending on the time ;)) if the person downloading desires to do so.

That being said, I still want to keep my focus on my main scenery project but since I am currently using my tools for that project as well, it is being worked on in parallel!
 
It's definitely on my planning to release some kind of tool to edit elevation data inside and outside the sim. However, I'm not to keen on making it a paid service since this comes with a lot more responsibility for support and adding features etc. and since I am not a software developer, I feel like I cannot guarantee that desired standard. So my plan is to release the tool for free without any limitations on use but with the ability to buy me a coffee (or a beer, depending on the time ;)) if the person downloading desires to do so.

That being said, I still want to keep my focus on my main scenery project but since I am currently using my tools for that project as well, it is being worked on in parallel!
Couldn't hear any better news regarding this, you're a life saver man. And surely I'm gonna buy you a coffee :D
 
When adding a heightmap for the first time, the heightmap takes on the height values of the underlying terrain which can then be interpreted by any type of automated process like the one I use right now
I hadn't thought of that! I can designate any rectangle and get a model out of Blender in half an hour manually. A tool would be best as it might generate an OBJ (for example) in a few seconds... You can get a lot of info in the DevMode:
Untitled.png


The sim can give you the ellipsoid elevation and the AMSL as well (note that you need to subtract the AGL --1.0m in this case-- )

The gizmo gives you the heading, which defaults to 180. In Blender you can adjust for the heading, lower the elevation to 0, if you want, or you can adjust the heading and elevation of the model in the sim. The end result is pretty close... probably within 3cm.
 
What I did was to make a grid in Blender the dimensions of the rectangle, then duplicate that and delete (X) the faces and edges only. That gave me a grid of points as well. I saved each as OBJ. I then made a list of the rectangle elevations, and turned it into a spreadsheet column. I entered the grid of points OBJ as a spreadsheet, and overwrote the Z values with my elevation list. Turned the mess back into a point grid OBJ.
In blender, I imported the rectangle OBJ and the Point grid OBJ, then used a shrinkwrap modifier to wrap the rectangle to the point grid. That all works well. But importing the wrapped rectangle as an object shows the ends of the shrinkwrap well above the ground level of the heightmap... the whole grid of points needs to be warped to the earth.
An easier method:
Make a mesh grid in blender to the dimensions. Save as OBJ. Copy the rectangle data to a text editor and replace the spaces with regular expression \n to put the data in a column. In another text editor instance copy the lines of vertices (start with v) and paste that to a 3rd text editor instance and save as "grid.cvs". Load "grid.cvs" into a spreadsheet, copy all the rectangle column data you made, and paste over the 0.0000s vertex column. Save that spreadsheet as "grid.cvs". Open "grid.cvs" in a text editor and change any commas to spaces, and copy all. Go back to the original OBJ, open in a text editor again, and copy the new vertex lines (that now have the elevations) over the vertex lines and save. Now you have an OBJ with the correct elevations.

Blender mesh -> OBJ -> text editor manipulation. No need for using Blender for two meshes and shrink-wrapping.
 
Back
Top