├── LICENSE ├── README.md ├── applymaterials.py ├── blueprints ├── ImportLines.txt ├── ImportLines.zip ├── README.md ├── UTMCoordinates.jpg ├── UTMCoordinates.txt └── UTMCoordinates.zip ├── importlines.py ├── materialsgen.py ├── readmeassets ├── blueprint.jpg ├── insertlinesmesh.jpg ├── insertlinesoffsetexample.jpg ├── insertlineszoffset.jpg ├── origin.JPG └── ueimportscreen.jpg └── srtm2heightmap.py /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Rodrigo Nascimento Hernandez 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UE4HmapsGenerator (heightmaps generator) 2 | ## Unreal Engine 4 scripts to: 3 | - Prepare and tile from SRTM (and others) DEM to UE heightmaps while keeping real world X,Y,Z proportions and metrics (UE u.u.) (>srtm2heightmap.py<) 4 | - Create landscape textures and materials. (>materialsgen.py<) 5 | - Assign landscape materials to tiles. (>applymaterials.py<) 6 | - Import line features from shapefile. (>importlines.py<) 7 | 8 | Output example: https://www.youtube.com/watch?v=91U2XWXpHJk 9 | 10 | ## >srtm2heightmap.py< 11 | Script that takes GeoTiff (and other GDAL valid file drivers, SRTM .hgt images e.g.) and generates UE4 hightmaps tiles and texture files. Script needs GDAL installed (No need to have GDAL python binds installed). On Windows the easiest way to install GDAL is [osgeo4w] 12 | Also on Windows make sure you have appropriated sys environment sets, e.g.: 13 | 14 | - "GDAL_DATA C:\Program Files\GDAL\gdal-data" 15 | - "GDAL_DRIVER_PATH C:\Program Files\GDAL\gdalplugins" 16 | - "GDAL_VERSION 3.1.1" 17 | 18 | Script will ask for all necessary parameters needed to take the input images, generate the tiles and generate/inform the necessary importing X,Y,Z UE4 scales. 19 | It will generate a 'customtiles' directory and auxiliary files in the same directory as origin files are located. The image file to be used as landscape texture/materials is optional but if you use it, needs to be a georeferenced file as well in the same projection/EPSG as the DEM file. 20 | Look the code for the 'METER_BY_DEGREE_FACTOR' variable, adjust as necessary depending the part of the globe you're working. This conversion is necessary if we need convert spatial resolution (pixel size) in decimal degrees to a metric reference (meter) as UE4 workspace is a orthogonal world. It may be better using a DEM image already in a ortoghonal projection (e.g. UTM) as you don't need to adjust 'METER_BY_DEGREE_FACTOR', accordingly your final objectives. 21 | This script was tested with [SRTM] DEMs but will accept other valid GeoTiff images. 22 | Starting from raw SRTM images if you don't know how to answer the Y/N questions, answer (Y)es for all of them to get started. 23 | [How to download SRTM] 24 | 25 | The conventions used by the script are the following: 26 | 27 | ![Scene origin](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/origin.JPG) 28 | 29 | The script will detect the most top left coordinates as the origin coordinates for the tiles extraction. Script will ask if user wants to use the detected origin coordinates or indicate alternative coordinates somewhere in the scene to be used as origin for tiles extraction. 30 | 31 | ![UE import screen](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/ueimportscreen.jpg) 32 | 33 | You can use the script to produce a single tile and import directly in landscape mode. If importing tiles in world composition mode, make sure option 'Flip Tile Y Coordinate' is not selected. Either way, use the X,Y,Z scales calculated by the script in the importing screen. Take care to not select texture 'texture_' files but only the heightmaps png files; 34 | 35 | ## >materialsgen.py< 36 | 37 | This script will import the texture files, create and set the materials to be used for each tile. Copy this script under your UE project '/content' folder, edit and change the 'inputdir' and 'UE4_TILE_TEXTURE_SCALE' variables accordingly and run in the UE4 project instance (e.g. File->Execute Python Script). 38 | After importing all textures the resulting blueprint for each tile material will be like: 39 | 40 | ![UE blueprint](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/blueprint.jpg) 41 | 42 | ## >applymaterials.py< 43 | 44 | This script simply apply the texture materials in the corresponding tiles/levels. 45 | Take note that for this to work the levels/tiles must be loaded in the viewport/project. This way you can load textures in parts or the whole thing at once. 46 | 47 | ## >importlines.py< 48 | This script will import lines from a given shapefile. Intended to be used with orthogonal origin workspace (UTM e.g.). It will read (optional) elevation/height from line features. 'Snap to ground' and 'Render mesh' functionality in the accompanying Blueprint. This script needs gdal python bindings installed. 49 | - Copy [ImportLines] bluerpint from blueprints directory to UE project's folder 50 | - Copy the script inside your project (content folder) 51 | - Edit the script to set the necessary references. You will need information generated by >srtm2heightmap.py< like X,Y Origin. Also edit the path of your installed gdal python bindings. 52 | - Run the script from UE project instance to import the lines. 53 | - After importing you can use blueprint's 'SnapToGround' function to snap splines points to ground (and other surfaces in it's vertical range) and use 'RenderMesh' to render a selected mesh along the splines. You can select all the lines in World Outliner and snap to ground or render mesh in one go. 54 | - You need to set the mesh to be rendered in the blueprint. 55 | ![importlinemesh](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/insertlinesmesh.jpg) 56 | - You can set a 'Z offset' for the splines in the blueprint 57 | ![importlineoffset](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/insertlineszoffset.jpg) 58 | ![importlineoffsetexample](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/readmeassets/insertlinesoffsetexample.jpg) 59 | Example lines with a Z offset 60 | 61 | [How to download SRTM]: https://www.youtube.com/watch?v=0YPFegTcL4w 62 | [SRTM]: https://www2.jpl.nasa.gov/srtm/ 63 | [osgeo4w]: https://trac.osgeo.org/osgeo4w/ 64 | [importlines]: https://github.com/Rodrigo-NH/UE4HmapsGenerator/blob/main/blueprints/ImportLines.zip -------------------------------------------------------------------------------- /applymaterials.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # Rodrigo Nascimento Hernandez 3 | 4 | import unreal 5 | 6 | asset_path = '/Game' 7 | material_dir = '/landsmaterials' 8 | 9 | actors = unreal.EditorLevelLibrary.get_all_level_actors() 10 | for each in actors: 11 | label = each.get_actor_label() 12 | if 'Landscape' in label: 13 | try: 14 | matname = asset_path + material_dir + '/M_' + each.get_path_name().split(':')[0].split('.')[1] 15 | mat = unreal.EditorAssetLibrary.load_asset(matname) 16 | each.set_editor_property('landscapematerial', mat) 17 | except: 18 | pass 19 | -------------------------------------------------------------------------------- /blueprints/ImportLines.txt: -------------------------------------------------------------------------------- 1 | UE version: 2 | 4.26.1 -------------------------------------------------------------------------------- /blueprints/ImportLines.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/blueprints/ImportLines.zip -------------------------------------------------------------------------------- /blueprints/README.md: -------------------------------------------------------------------------------- 1 | ## >UTMCoordinates.zip< (Testing) 2 | This blueprint is an actor that reports it's current map coordinates and height. It just apllies an offset to ActorLocation vector position based on your real world X,Y origin coordinates. Edit the blueprint and change 'UTM X origin', 'UTM Y origin' and 'Height point reference (m)' informed by >srtm2heightmap.py<. Works for UTM and possibly any other orthogonal projection. 3 | ![UE blueprint](https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/main/blueprints/UTMCoordinates.jpg) 4 | ## >ImportLines.zip< 5 | Blueprint to be used in conjunction with >importlines.py< -------------------------------------------------------------------------------- /blueprints/UTMCoordinates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/blueprints/UTMCoordinates.jpg -------------------------------------------------------------------------------- /blueprints/UTMCoordinates.txt: -------------------------------------------------------------------------------- 1 | UE version: 2 | 4.26.1 -------------------------------------------------------------------------------- /blueprints/UTMCoordinates.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/blueprints/UTMCoordinates.zip -------------------------------------------------------------------------------- /importlines.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # Rodrigo Nascimento Hernandez 3 | # Tested UE versions: 4 | # 4.26.1 5 | # Blueprint: ImportLines 6 | 7 | import sys 8 | import json 9 | import unreal 10 | 11 | # ========================================= user inputs =============================================================== 12 | # Change this path pointing to your gdal python bindings install dir 13 | sys.path.append(r'C:\OSGeo4W64\apps\Python37\Lib\site-packages') 14 | from osgeo import ogr 15 | # Set project's relative path of the importlines BluePrint (e.g. in content folder-> 16 | importlines = unreal.EditorAssetLibrary().load_blueprint_class('/Game/ImportLines') 17 | # Set shapefile path 18 | shapefile = r'D:\UnrealEngine\curves\contour.shp' 19 | # Set layer's feature name to read lines vertices Z values (height). Leave empty to not import any (Can use blueprint's 'Snap to Ground' custom event instead) 20 | # If set to read Z values so you need to set 'Height point reference (m)' value also, calculated by 'srtm2heightmap.py' 21 | featurename = '' 22 | hpointreference = 572 23 | # Set real world's UTM X,Y Origin (used in srtm2heightmap.py e.g.) 24 | UTMx = 596441.4895549538 25 | UTMy = 7918815.581869906 26 | # Choose one of SplinePointType to be used on import 27 | SplinePointType = unreal.SplinePointType.CURVE_CLAMPED 28 | # SplinePointType = unreal.SplinePointType.CURVE_CUSTOM_TANGENT 29 | # SplinePointType = unreal.SplinePointType.LINEAR 30 | # SplinePointType = unreal.SplinePointType.CURVE 31 | # SplinePointType = unreal.SplinePointType.CONSTANT 32 | # ========================================= script work =============================================================== 33 | 34 | ogr = ogr.Open(shapefile) 35 | layer = ogr.GetLayer(0) 36 | jsond = '{' 37 | LINE = 0 38 | for feat in layer: 39 | if featurename is '': 40 | zvalue = "0" 41 | else: 42 | zvalue = feat.GetField(featurename) 43 | geometry = feat.GetGeometryRef() 44 | dd = geometry.ExportToJson() 45 | jsond = jsond + '"LINE' + str(LINE) + '": [ { "zvalue": ' + str(zvalue) + ' }, ' + dd + '],' 46 | LINE += 1 47 | jsond = jsond[:-1] + '}' 48 | ldata = json.loads(jsond) 49 | ed = unreal.EditorLevelLibrary 50 | for key in ldata: 51 | zvalue = ldata[key][0]['zvalue'] 52 | print(zvalue) 53 | line = ldata[key][1]['coordinates'] 54 | if zvalue != 0: 55 | zvalue = (float(zvalue) - float(hpointreference)) * 100.0 56 | pi = 0 57 | for coord in line: 58 | xx = (float(coord[0]) - UTMx) * 100.0 59 | yy = (UTMy - float(coord[1])) * 100.0 60 | nv = unreal.Vector(x=xx, y=yy, z=zvalue) 61 | if pi == 0: 62 | rt = unreal.Rotator(roll=0.0, pitch=0.0, yaw=0.0) 63 | actorObject = ed.spawn_actor_from_class(importlines, nv, rt, transient=False) 64 | SceneComponent = actorObject.root_component 65 | SplineComponent = SceneComponent.get_child_component(1) 66 | SplineComponent.clear_spline_points() 67 | SplineComponent.add_spline_point_at_index(nv, pi, unreal.SplineCoordinateSpace.WORLD) 68 | SplineComponent.set_spline_point_type(pi, SplinePointType, update_spline=True) 69 | pi += 1 70 | -------------------------------------------------------------------------------- /materialsgen.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # Rodrigo Nascimento Hernandez 3 | 4 | import unreal 5 | import os 6 | import pathlib 7 | 8 | inputdir = r'D:\MA\GIS\UE4\kingsvalley\customtiles' # Enter directory where tiled texture files are stored 9 | asset_path = '/Game' 10 | material_dir = '/landsmaterials' 11 | # Change to your Texture scale calculated by srtm2heightmap.py 12 | UE4_TILE_TEXTURE_SCALE = 0.003952569169960475 13 | 14 | # Scan for texture files 15 | texturepaths = [] 16 | for file in os.listdir(inputdir): 17 | if file.startswith("texture_") and file[-4:].upper() == '.PNG': 18 | texturepaths.append(os.path.join(inputdir, file)) 19 | 20 | # import textures and set in memory textures reference 21 | data = unreal.AutomatedAssetImportData() 22 | data.set_editor_property('destination_path', asset_path + material_dir) 23 | data.set_editor_property('filenames', texturepaths) 24 | textures = unreal.AssetToolsHelpers.get_asset_tools().import_assets_automated(data) 25 | 26 | # set used tools 27 | assetTools = unreal.AssetToolsHelpers.get_asset_tools() 28 | mf = unreal.MaterialFactoryNew() 29 | 30 | for texture in textures: 31 | texturename = texture.get_name() 32 | matname = 'M_' + texturename.split('texture_')[1] 33 | 34 | # Create the new material and set property 35 | matobj = assetTools.create_asset(matname, asset_path + material_dir, unreal.Material, mf) 36 | matobj.set_editor_property('shading_model', unreal.MaterialShadingModel.MSM_DEFAULT_LIT) 37 | 38 | # Create texturesample expression, constant expression, make connections and set texture asset origin 39 | texturesample = unreal.MaterialEditingLibrary.create_material_expression(matobj,unreal.MaterialExpressionTextureSample,-384,-200) 40 | specularzero = unreal.MaterialEditingLibrary.create_material_expression(matobj,unreal.MaterialExpressionConstant,-200,-100) 41 | unreal.MaterialEditingLibrary.connect_material_property(texturesample, 'rgb', unreal.MaterialProperty.MP_BASE_COLOR) 42 | unreal.MaterialEditingLibrary.connect_material_property(specularzero, '', unreal.MaterialProperty.MP_SPECULAR) 43 | texturesample.texture = texture 44 | 45 | # Create TextureCoordinate expression, set scales and connect 46 | coordexpression = unreal.MaterialEditingLibrary.create_material_expression(matobj,unreal.MaterialExpressionTextureCoordinate,-550,-200) 47 | coordexpression.set_editor_property('utiling', UE4_TILE_TEXTURE_SCALE) 48 | coordexpression.set_editor_property('vtiling', UE4_TILE_TEXTURE_SCALE) 49 | unreal.MaterialEditingLibrary.connect_material_expressions(coordexpression, '', texturesample, 'uvs') 50 | 51 | # Optional save the assets as soon as possible. Shaders must be compiled etc. 52 | # May be more comfortable leave this commented, wait all shaders compiles in editor and just press 'save all' in editor 53 | # textureref = asset_path + material_dir + '/' + texturename 54 | # matref = asset_path + material_dir + '/' + matname 55 | # unreal.EditorAssetLibrary.save_asset(textureref) 56 | # unreal.EditorAssetLibrary.save_asset(matref) 57 | 58 | -------------------------------------------------------------------------------- /readmeassets/blueprint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/blueprint.jpg -------------------------------------------------------------------------------- /readmeassets/insertlinesmesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/insertlinesmesh.jpg -------------------------------------------------------------------------------- /readmeassets/insertlinesoffsetexample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/insertlinesoffsetexample.jpg -------------------------------------------------------------------------------- /readmeassets/insertlineszoffset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/insertlineszoffset.jpg -------------------------------------------------------------------------------- /readmeassets/origin.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/origin.JPG -------------------------------------------------------------------------------- /readmeassets/ueimportscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rodrigo-NH/UE4HmapsGenerator/e69f96be09ecc3b566f31fef668ed60ca01c4a22/readmeassets/ueimportscreen.jpg -------------------------------------------------------------------------------- /srtm2heightmap.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # Rodrigo Nascimento Hernandez 3 | # Tested UE versions: 4 | # 4.26.1 5 | 6 | import os 7 | from PIL import Image 8 | import pathlib 9 | 10 | # Adjust this factor relative to the real projection figure on the region of the globe you're working in. 11 | # https://www.usna.edu/Users/oceano/pguth/md_help/html/approx_equivalents.htm 12 | # You DONT need to set this factor (not used in calculations) if your input is a regular GeoTiff in a orthogonal projection (e.g. UTM) 13 | METER_BY_DEGREE_FACTOR = 111000 14 | 15 | def main(): 16 | inputfile = input("Enter GeoTIFF file's full path (including file name and extension)\n") 17 | DIR_PATH = pathlib.Path(inputfile).parent.absolute() 18 | userchoicebackgroundimage = YN("Want to tile a background image too? (To be used as UE4 textures") 19 | if userchoicebackgroundimage is True: 20 | backgroundimagepath = input("Enter background GeoTIFF file's full path (to be used as UE4 textures)\n") 21 | print("Computing image ...\n") 22 | gdalinfo = os.popen("gdalinfo -stats " + inputfile).read().splitlines() 23 | 24 | START_LAT = None 25 | START_LONG = None 26 | PIXEL_SIZE = None 27 | for each in gdalinfo: 28 | line = each.strip().upper() 29 | if "PIXEL SIZE" in line: 30 | PIXEL_SIZE = float(line.split("(")[1].split(",")[0]) 31 | if "ORIGIN =" in line: 32 | START_LONG = float(line.split("(")[1].split(",")[0]) 33 | START_LAT = float(line.split("(")[1].split(",")[1].split(")")[0]) 34 | 35 | userchoice = False 36 | if START_LAT is not None: 37 | userchoice = YN("Use Lat (" + str(START_LAT) + ") from image statistics origin coordinates for Level starting point?") 38 | if userchoice is False: 39 | START_LAT = float(input("Enter Latitude coordinate of Level starting point (e.g. -20.32453)\n")) 40 | 41 | userchoice = False 42 | if START_LONG is not None: 43 | userchoice = YN( 44 | "Use Long (" + str(START_LONG) + ") from image statistics origin coordinates for Level starting point?") 45 | if userchoice is False: 46 | START_LONG = float(input("Enter Longitude coordinate of Level starting point (e.g. -45.54623)\n")) 47 | 48 | userchoice = False 49 | if PIXEL_SIZE is not None: 50 | userchoice = YN( 51 | "Use Spatial Resolution (" + str(PIXEL_SIZE) + ") from image statistics for output X,Y scale calculation?") 52 | if userchoice is False: 53 | PIXEL_SIZE = float(input("Enter Spatial Resolution (pixel size) of the input image\n")) 54 | 55 | PIXEL_SIZE_IS_DEGREE = YN("Is above Spatial Resolution in decimal degrees? (Choose 'N' if in meters)") 56 | TILE_SIZE = float(input("Enter UE4 (tile) Level size aspect (Recommended sizes: 127, 253, 505, 1009, 2017, 4033, 8129)\n")) 57 | X_TILES_NUMBER = float(input("Enter desired X number of Level Tiles \n")) 58 | Y_TILES_NUMBER = float(input("Enter desired Y number of Level Tiles\n")) 59 | 60 | zspace = YN("Use entire UE4 Z range (-256 to 255.992) for Level/tiles?" 61 | " Will use only positive range otherwise ( 0 to 255.992)") 62 | if zspace is False: 63 | startrange = 32767 64 | else: 65 | startrange = 0 66 | 67 | print("Computing parameters ...\n") 68 | 69 | overallfile = os.path.join(DIR_PATH, "overall.tif") 70 | totalX = X_TILES_NUMBER * TILE_SIZE * PIXEL_SIZE 71 | totalY = Y_TILES_NUMBER * TILE_SIZE * PIXEL_SIZE 72 | xrange = START_LONG + totalX 73 | yrange = START_LAT - totalY 74 | command = "gdal_translate -projwin " + str(START_LONG) + " " + str(START_LAT) + " " + str(xrange) + " " + str(yrange) + " " \ 75 | + inputfile + " " + overallfile 76 | 77 | print("Computing image ...\n") 78 | result = os.popen(command).read().splitlines() 79 | print(result) 80 | 81 | maxminvals = getmaxminheight(overallfile) 82 | STATISTICS_MAXIMUM = float(maxminvals[0]) 83 | STATISTICS_MINIMUM = float(maxminvals[1]) 84 | 85 | userchoice = False 86 | if STATISTICS_MAXIMUM is not 0: 87 | userchoice = YN( 88 | "Use source Max pixel value (height = " + str(STATISTICS_MAXIMUM) + ") from selected region statistics " 89 | "for output Z scale calculation?") 90 | if userchoice is False: 91 | STATISTICS_MAXIMUM = input("Enter image's real world Max height in meters\n") 92 | 93 | userchoice = False 94 | if STATISTICS_MINIMUM is not 0: 95 | userchoice = YN( 96 | "Use source Min pixel value (height = " + str(STATISTICS_MINIMUM) + ") from selected region statistics " 97 | "for output Z scale calculation?") 98 | if userchoice is False: 99 | STATISTICS_MINIMUM = input("Enter image's real world Min height in meters\n") 100 | 101 | overallfilescaled = os.path.join(DIR_PATH, "overallScaled.tif") 102 | print("Computing image ...\n") 103 | scalecommand = "gdal_translate -ot UInt16 -scale " + str(STATISTICS_MINIMUM) + " " + str(STATISTICS_MAXIMUM) + \ 104 | " " + str(startrange) + " 65535" + " " + overallfile + " " + overallfilescaled 105 | print(scalecommand) 106 | scaleproc = os.popen(scalecommand).read() 107 | print(scaleproc) 108 | 109 | tilespath = os.path.join(DIR_PATH, "customtiles") 110 | os.mkdir(tilespath) 111 | 112 | tilelist = [] 113 | stepX = START_LONG 114 | stepY = START_LAT 115 | tilerange = TILE_SIZE * PIXEL_SIZE 116 | for eachY in range(0, int(Y_TILES_NUMBER)): 117 | if eachY < 10: 118 | eachY = "0" + str(eachY) 119 | for eachX in range(0, int(X_TILES_NUMBER)): 120 | if eachX < 10: 121 | eachX = "0" + str(eachX) 122 | coordsname = "tile_x" + str(eachX) + "_y" + str(eachY) 123 | tilename = coordsname + ".tif" 124 | texturename = "texture_" + coordsname + ".tif" 125 | tilelist.append(tilename) 126 | tilepath = os.path.join(tilespath, tilename) 127 | texturepath = os.path.join(tilespath, texturename) 128 | tilerangeX = stepX + tilerange 129 | tilerangeY = stepY - tilerange 130 | command = "gdal_translate -projwin " + str(stepX) + " " + str(stepY) + " " + str(tilerangeX) + " " + str( 131 | tilerangeY) + " " + overallfilescaled + " " + tilepath 132 | print(command) 133 | tileproc = os.popen(command).read() 134 | print(tileproc) 135 | if userchoicebackgroundimage: 136 | command2 = "gdal_translate -ot Byte -scale 0 255 -projwin " + str(stepX) + " " + str(stepY) + " " + str( 137 | tilerangeX) + " " + str( 138 | tilerangeY) + " " + backgroundimagepath + " " + texturepath 139 | print(command2) 140 | textureproc = os.popen(command2).read() 141 | print(textureproc) 142 | stepX = stepX + tilerange 143 | stepX = START_LONG 144 | stepY = stepY - tilerange 145 | 146 | print("Computing images ...\n") 147 | 148 | isresizeimage = False 149 | at = False 150 | rsize = 0 151 | for image in tilelist: 152 | terraintiffpath = os.path.join(tilespath, image) 153 | texturetiffpath = os.path.join(tilespath, "texture_" + image) 154 | terrainpngfile = image.split(".")[0]+".png" 155 | texturepngfile = "texture_" + image.split(".")[0] + ".png" 156 | terrainpngpath = os.path.join(tilespath, terrainpngfile) 157 | texturepngpath = os.path.join(tilespath, texturepngfile) 158 | if userchoicebackgroundimage: 159 | im = Image.open(texturetiffpath) 160 | width, height = im.size 161 | if not ((width != 0) and (width & (width-1) == 0)): # Test for (is number a power of two)? 162 | if not at: 163 | at = True 164 | isresizeimage = YN("Texture images are (" + str(width) + "x" + str(height) + "), not power of two size. " 165 | "Want to resize texture files?\n" ) 166 | if isresizeimage: 167 | rs = int(input("Enter a number to use for width and height. (power of two recommended e.g." 168 | " 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192)\n")) 169 | rsize = (rs, rs) 170 | if isresizeimage: 171 | im = im.resize(rsize, resample=Image.BILINEAR) 172 | im.save(texturepngpath) 173 | os.remove(texturetiffpath) 174 | im = Image.open(terraintiffpath) 175 | im.save(terrainpngpath) 176 | os.remove(terraintiffpath) 177 | print("Computing image ...\n") 178 | 179 | if PIXEL_SIZE_IS_DEGREE: 180 | PIXEL_SIZE = PIXEL_SIZE * METER_BY_DEGREE_FACTOR 181 | 182 | REAL_WORLD_XY_TILE_LENGHT = TILE_SIZE * PIXEL_SIZE 183 | UE4_XY_SCALE = (REAL_WORLD_XY_TILE_LENGHT / (TILE_SIZE - 1)) * 100 184 | REAL_WORLD_HEIGHT_DIFFERENCE = float(STATISTICS_MAXIMUM) - float(STATISTICS_MINIMUM) 185 | 186 | if zspace: 187 | zfactorrange = 512 188 | HEIGHT_MID_POINT = STATISTICS_MINIMUM + (REAL_WORLD_HEIGHT_DIFFERENCE / 2.0) 189 | else: 190 | zfactorrange = 256 191 | HEIGHT_MID_POINT = STATISTICS_MINIMUM 192 | 193 | UE4_Z_SCALE = (REAL_WORLD_HEIGHT_DIFFERENCE * 100.0) / zfactorrange #height in centimeters 194 | UE4_TILE_TEXTURE_SCALE = UE4_XY_SCALE / (REAL_WORLD_XY_TILE_LENGHT*100.0) 195 | 196 | 197 | print("=================================== OUTPUTS ====================================\n") 198 | print("XY_TILE_LENGHT: " + str(REAL_WORLD_XY_TILE_LENGHT)) 199 | print("XY_TILE_LENGHT: " + str(REAL_WORLD_XY_TILE_LENGHT)) 200 | print("STATISTICS_MAXIMUM (height): " + str(STATISTICS_MAXIMUM)) 201 | print("STATISTICS_MINIMUM (height): " + str(STATISTICS_MINIMUM)) 202 | print("Real World height difference: " + str(REAL_WORLD_HEIGHT_DIFFERENCE)) 203 | print("Origin X: " + str(START_LONG)) 204 | print("Origin Y: " + str(START_LAT)) 205 | print("\n") 206 | print("=========================== UE4 importing parameters ===========================\n") 207 | print("UE4 X,Y Scale: " + str(UE4_XY_SCALE)) 208 | print("UE4 Z Scale: " + str(UE4_Z_SCALE)) 209 | print("Tile texture scale: " + str(UE4_TILE_TEXTURE_SCALE)) 210 | print("Height point reference (m): " + str(HEIGHT_MID_POINT)) 211 | print("\n") 212 | 213 | def getmaxminheight(inputfile): 214 | gdalinfo = os.popen("gdalinfo -stats " + inputfile).read().splitlines() 215 | vals = [0,0] 216 | for each in gdalinfo: 217 | line = each.strip().upper() 218 | if "STATISTICS_MAXIMUM" in line: 219 | vals[0] = line.split("=")[1].strip() 220 | if "STATISTICS_MINIMUM" in line: 221 | vals[1] = line.split("=")[1].strip() 222 | return vals 223 | 224 | def YN(question): 225 | startlatoption = False 226 | while startlatoption is False: 227 | userinput = input(question + "(Y/N)\n") 228 | if "N" in userinput.upper(): 229 | startlatoption = True 230 | return False 231 | if "Y" in userinput.upper(): 232 | startlatoption = True 233 | return True 234 | 235 | 236 | if __name__=="__main__": 237 | main() 238 | --------------------------------------------------------------------------------