Multiple terrain

Hey I was wondering that is there any option to load multiple terrain in cesium for unreal.

I have a use case where I have high res terrain for small area. How can drape it over cesium world terrain without using cartographic polygon.

Cartographic Polygon is the way to do this. Why are you trying to avoid it?

Because I have to manually sit and cut section for each operation site.

If it is not possible without using cartographic polygon then can you please tell me how to set up automation to add multiple actor automatically (one actor for globe low res terrain and imagery and other K actors for each site with high res terrain and imagery)

Given that value of K is large using cartographic polygon is not feasible. Got it? @Kevin_Ring

You can use Blueprints or C++ to automate adding Actors.

Depending on just how large your K is, it might be feasible. Even a hundred simple cutouts is probably workable, using a single raster overlay with multiple polygons.

Another possibility is to use Cesium ion. If your source terrain data is heightmaps in TIFF or similar format, you can upload them all to Cesium ion as a single terrain and ask it to overlay them on top of Cesium World Terrain. That will give you a single unified terrain to import into Cesium for Unreal.

@Kevin_Ring

we are working with offline data using custom fileserver.
image
over folder structure look like this (Just for demonstration purpose)
C:\USERS\ADMIN\DESKTOP\DATA
├───high_res_data1
│ │ layer.json
│ │
│ ├───1
│ ├───10
│ ├───2
│ ├───3
│ ├───4
│ ├───5
│ ├───6
│ ├───7
│ ├───8
│ └───9
├───high_res_data2
│ │ layer.json
│ │
│ ├───1
│ ├───10
│ ├───2
│ ├───3
│ ├───4
│ ├───5
│ ├───6
│ ├───7
│ ├───8
│ └───9
└───low_res_glob_data
│ layer.json

├───1
├───2
├───3
└───4

we have created only one cesium3dTiles actor.

we have made a custom fileserve that intercept each request and check do we have high res terrain tiles for particular z, x, y if not then we fall back to globe low res terrain tiles.

problem I am encountering is that I am able to load only one site high res terrain despite having non overlapping area with other site.

see where over gizmo is there should also be a high res terrain. I have tried to go to close to the terrain where out gizmo is still engine is not sending request with higher zoom level for other site.

here is our custom fileserver code.

import glob
import json
import os
from http.server import SimpleHTTPRequestHandler, HTTPServer
import re

class CustomFileServer(SimpleHTTPRequestHandler):    
    def do_GET(self):

        if(self.path):
            # Use regular expressions to find the numbers in the path
            match = re.search(pattern, self.path)
            if match:
                # Extract the three numbers
                z = match.group(1)
                x = match.group(2)
                y = match.group(3)
                
                filter_data_dict ={}
                for key, value in all_data_folder_dict.items():
                    if str(z) in value['z']:
                        if str(x) in value['z'][str(z)]:
                            if str(y) in value['z'][str(z)][str(x)]:
                                filter_data_dict[key] = maxzoom

                if len(filter_data_dict) == 0:
                    return super().do_GET()

                print()
                max_zoom_data_key = max(filter_data_dict, key=filter_data_dict.get)
                self.path = f'{max_zoom_data_key}/{self.path}'
                print(f'** self.path : {self.path} :data {filter_data_dict}')
                return super().do_GET()

            else:
                return super().do_GET()
        else:
            return super().do_GET()


    def determine_file_to_serve(self, request_path):
        # Implement your custom logic here
        # For example, you can map certain requests to specific files
        if request_path == '/special':
            return 'special_file.txt'
        elif request_path.startswith('/data'):
            return 'data_file.csv'
        else:
            # Default to the requested file
            return request_path.strip('/')

# Step 2: Set up and run the server
if __name__ == "__main__":
    other_folder_path = "other"

    # def list_folders_recursively(directory):
    #     folders = glob.glob(os.path.join(directory, '**/'), recursive=True)
    #     return folders
    
    def list_folders_recursively(directory):
        folders = []
        for root, dirs, files in os.walk(directory):
            for dir_name in dirs:
                folders.append( dir_name)
        return folders
    
    def get_filenames_without_extension(directory):
        # List all items in the given directory
        items = os.listdir(directory)
        
        # Filter out files and remove their extensions
        filenames = [os.path.splitext(item)[0] for item in items if os.path.isfile(os.path.join(directory, item))]
        
        return filenames
    
    all_data_folder_dict = {}
    if os.path.exists(other_folder_path) and os.path.isdir(other_folder_path):
        # List all files and directories in the "other" folder
        contents = os.listdir(other_folder_path)
        current_directory_path = os.getcwd()


        print(f"Contents of the '{other_folder_path}' folder:")
        
        for folder in contents:
            layer_json_path = f"{current_directory_path}\\{other_folder_path}\\{folder}\\layer.json"

            folder_path = f"{current_directory_path}\\{other_folder_path}\\{folder}"
            print(folder_path)
            if os.path.exists(layer_json_path):
                with open(layer_json_path, 'r') as layer_json_file:
                    layer_json_file_content =json.load(layer_json_file)
                    maxzoom = layer_json_file_content['maxzoom']

                    z_folders = [item for item in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, item))]
                    out= {'maxzoom':maxzoom, 'z' :{}}
                    for z in z_folders:
                        z_directory_path = os.path.join(folder_path, z)
                        x_folders = [item for item in os.listdir(z_directory_path) if os.path.isdir(os.path.join(z_directory_path, item))]
                        x_dict ={}
                        for x in x_folders:
                            x_directory_path = os.path.join(z_directory_path, x)
                            y_files = get_filenames_without_extension(x_directory_path)
                            x_dict[x] = y_files
                        out['z'][z] = x_dict
                    print(out)
                    all_data_folder_dict[f'{other_folder_path}/{folder}']= out
            # check[folder] =  for y in x for x in z for z in folder 
            # print(os.path.exists(item+ "/layer.json"))
    else:
        print(f"The folder '{other_folder_path}' does not exist or is not a directory.")

    print(all_data_folder_dict.keys())


    port = 8000
    pattern = r"/(\d+)/(\d+)/(\d+)\.terrain"
    server_address = ("", port)
    httpd = HTTPServer(server_address, CustomFileServer)
    print(f"Serving on port {port}")
    httpd.serve_forever()


    

@Kevin_Ring can you please share me logic in the code where (plugin source or cesium native) its handling loading of two different terrain tiles with different resolution low and high based on some parameter like vertex count or triangle count

Unfortunately, that won’t work. The problem is “availability” information.

In layer.json/quantized-mesh, which you appear to be using, “availability” indicates which tiles actually exist and should be requested. This way we don’t waste time requesting tiles that won’t exist. This information comes from two possible places. The first is the layer.json itself. So in order for your scheme to work, you need to make sure that the available property in the top-level layer.json indicates that all of the more detailed tiles for all of the insets exist.

If you have a lot of insets, this available set may become quite large. That’s why quantized-mesh also supports a second way of indicating tile availability.

Individual tiles can also list further availability in the metadata extension. This has the same structure as the property in layer.json, but allows the overall availability for the tileset to be loaded incrementally because individual tiles can encode the availability of child / descendant tiles.

So your missing high-res terrain is likely caused by missing availability information.