5

I am currently working on a game in Godot which involves rendering countries on a globe. I have very little prior experience with Godot, but have experimented with it in the past.

I am using this data from Natural Earth for country borders, and have successfully gotten it to display on the globe using a line mesh. The data was originally in shapefile format, but I converted it to GeoJSON using mapshaper.org.

Picture

The data basically boils down to a list of points given in latitude and longitude, which I then converted into 3d points and created a mesh using SurfaceTool.

I am having trouble generating an actual surface for the mesh, however. Firstly, I am unable to find a built-in function to generate a triangle mesh from this data. I have looked into numerous solutions, including using the built-in Mesh.PRIMITIVE_TRIANGLE_FAN format, which doesn't work on concave shapes.

I have looked into triangulation algorithms such as delaunay triangulation, but have had little success implementing them.

My current plan is to generate a triangle mesh using the 2d data (x,y = longitude,latitude), and project it onto the surface of the sphere. In order to produce a curved surface, I will include the vertices of the sphere itself in the mesh (example).

I would like to know how to go about constructing a triangle mesh from this data. In essence, I need an algorithm that can do the following things:

  1. Create a triangle mesh from a concave polygon (country border)
  2. Connect the mesh to a series of points within this polygon
  3. Allow for holes within the polygon (for lakes, etc.)

Here is an example of the result I am looking for.

Again, I am quite new to Godot and I am probably over-complicating things. If there is an easier way to go about rendering countries on a globe, please let me know.

This is my current code:

extends Node

export var radius = 1
export var path = "res://data/countries.json"

func coords(uv):
    return (uv - Vector2(0.5, 0.5)) * 360

func uv(coords):
    return (coords / 360) + Vector2(0.5, 0.5)

func sphere(coords, r):
    var angles = coords / 180 * 3.14159
    return Vector3(
        r * cos(angles.y) * sin(angles.x),
        r * sin(angles.y),
        r * cos(angles.y) * cos(angles.x)
    )

func generate_mesh(c):
    var mesh = MeshInstance.new()
    var material = SpatialMaterial.new()
    material.albedo_color = Color(randf(), randf(), randf(), 1.0)

    var st = SurfaceTool.new()
    st.begin(Mesh.PRIMITIVE_LINE_STRIP)

    for h in c:
        var k = sphere(h, radius)
        st.add_normal(k / radius)
        st.add_uv(uv(h))
        st.add_vertex(k)

    st.index()
    mesh.mesh = st.commit()
    mesh.set_material_override(material)
    return mesh

func load_data():
    var file = File.new()
    file.open(path, file.READ)
    var data = JSON.parse(file.get_as_text()).result
    file.close()

    for feature in data.features:
        var geometry = feature.geometry
        var properties = feature.properties

        if geometry.type == "Polygon":
            for body in geometry.coordinates:
                var coordinates = []
                for coordinate in body:
                    coordinates.append(Vector2(coordinate[0], coordinate[1]))
                add_child(generate_mesh(coordinates))
        if geometry.type == "MultiPolygon":
            for polygon in geometry.coordinates:
                for body in polygon:
                    var coordinates = []
                    for coordinate in body:
                        coordinates.append(Vector2(coordinate[0], coordinate[1]))
                    add_child(generate_mesh(coordinates))

func _ready():
    load_data()
TXP Ghost
  • 51
  • 1
  • I am working on the very same problem. The closest I got so far is by tessellating the GeoJSON into sufficiently small cells (e.g. with QGIS) before importing it into my project... curious if someone will post a better approach! – bob Dec 06 '19 at 12:26

0 Answers0