# ------------------------------------------------------------------- # ORTHOGRAPHIC # Your personal aerial satellite. Always on. At any altitude.* # Developed by MarStrMind # License: Open Software License 3.0 # Up to date version always on marstr.online # ------------------------------------------------------------------- # xp_dsfgen.py # This class is coming into play at the very end of the tile # generation process, and builds the DSF (Distributable Scenery # Format) file for X-Plane. # # For this, you will need DSFTool which I cannot re-distribute. # # You can download it for free from X-Plane's website. # Place them somewhere convenient, and point to them in the # xp_ variables in defines.py # ------------------------------------------------------------------- import os import glob import math from random import randrange from log import * class mstr_xp_dsfgen: # Instantiate with Lat/Lng, as usual def __init__(self, lat, lng, mlat, mlng, vstep): self._latitude = lat self._longitude = lng self._maxlat = mlat self._maxlng = mlng self._tmpdsf = mstr_datafolder + "_cache/tiledsf.txt" self._vstep = vstep self._dsfstring = "" mstr_msg("xp_dsfgen", "[X-Plane] DSFgen initialized") self.build_header() # Construct header of DSF txt def build_header(self): self._dsfstring = self._dsfstring + "PROPERTY sim/west " + str(int(self._longitude)) + "\n" self._dsfstring = self._dsfstring + "PROPERTY sim/east " + str(int(self._longitude + 1)) + "\n" self._dsfstring = self._dsfstring + "PROPERTY sim/south " + str(int(self._latitude)) + "\n" self._dsfstring = self._dsfstring + "PROPERTY sim/north " + str(int(self._latitude+1)) + "\n" self._dsfstring = self._dsfstring + "PROPERTY sim/planet earth\n" self._dsfstring = self._dsfstring + "PROPERTY sim/creation_agent Orthographic\n" self._dsfstring = self._dsfstring + "PROPERTY sim/overlay 1\n" # <- DO NOT REMOVE THIS!! self._dsfstring = self._dsfstring + "PROPERTY sim/require_facade 6/0\n" mstr_msg("xp_dsfgen", "[X-Plane] DSF header built") # Write the text file def write_dsf_txt(self): mstr_msg("xp_dsfgen", "[X-Plane] Writing DSF txt file") with open(self._tmpdsf, 'w') as textfile: textfile.write(self._dsfstring) # Convert the DSF into actual, usable data for X-Plane def convert_dsf_text(self): mstr_msg("xp_dsfgen", "[X-Plane] Converting DSF information into X-Plane DSF file") # Find separator sep = "" if os.name == "nt": sep = "\\" if os.name == "posix": sep = "/" datafolder = mstr_datafolder.replace("/", sep) # First, create the Earth nav data folder should it not exist end_base = datafolder + "Tiles/z_orthographic_" + self.xplane_latlng_folder([self._latitude, self._longitude]) + sep + "Earth nav data" # Create the appropriate rounded folder end_round = self.xplane_latlng_folder(self.find_earthnavdata_number()) if not os.path.exists(end_base): os.makedirs(end_base) if not os.path.exists(end_base + sep + end_round): os.makedirs(end_base + sep + end_round) # Get the file name for the DSF end_latlng = self.xplane_latlng_folder([self._latitude, self._longitude]) # Perform conversion os.system(mstr_xp_dsftool + " --text2dsf " + datafolder + "_cache" + sep + "tiledsf.txt \"" + end_base + sep + end_round + sep + end_latlng + ".dsf\"") mstr_msg("xp_dsfgen", "[X-Plane] DSF conversion complete") # Find the next "by-ten" numbers for the current latitude and longitude def find_earthnavdata_number(self): earthnavdata = [] lat = abs(int(self._latitude / 10) * 10) lng = abs(int(self._longitude / 10) * 10) earthnavdata.append(lat) earthnavdata.append(lng) return earthnavdata # Find with of a longitude (needed for DSF and .pol files) def _findWidthOfLongitude(self, lat): dm = math.cos(math.radians(lat)) * 111.321 # <- 1 deg width at equator in km return round(dm * 1000, 3) # Find diameter of an ortho def find_ortho_diameter(self, side): sq = side * side sqsum = sq + sq dm = math.sqrt(sqsum) # Construct an X-Plane compatible folder name for latitude and longitude def xplane_latlng_folder(self, numbers): fstr = "" if numbers[0] >= 0: fstr = "+" if numbers[0] < 0: fstr = "-" if abs(numbers[0]) < 10: fstr = fstr + "0" + str(numbers[0]) if abs(numbers[0]) >= 10 and numbers[0] <= 90: fstr = fstr + str(numbers[0]) if numbers[1] >= 0: fstr = fstr + "+" if numbers[1] < 0: fstr = fstr + "-" if abs(numbers[1]) < 10: fstr = fstr + "00" + str(numbers[1]) if abs(numbers[1]) >= 10 and numbers[0] <= 99: fstr = fstr + "0" + str(numbers[1]) if abs(numbers[1]) >= 100 : fstr = fstr + str(numbers[1]) return fstr # Build the complete DSF. # This is the main function to call. def build_dsf_for_tile(self): mstr_msg("xp_dsfgen", "[X-Plane] Building DSF file") # Add the polygon definition file entries for v in range(1, self._maxlat+1): for h in range(1, self._maxlng+1): self._dsfstring = self._dsfstring + "POLYGON_DEF terrain/"+str(v)+"_"+str(h)+".pol\n" # Add the definitions for each ortho tile curpol = 0 cur_lat = self._latitude cur_lng = self._longitude for v in range(1, self._maxlat+1): for h in range(1, self._maxlng+1): bbox = [ cur_lat, cur_lng, cur_lat+self._vstep-0.00001, cur_lng + mstr_zl_18-0.00001 ] self._dsfstring = self._dsfstring + "BEGIN_POLYGON "+str(curpol)+" 65535 4\n" self._dsfstring = self._dsfstring + "BEGIN_WINDING\n" self._dsfstring = self._dsfstring + "POLYGON_POINT " + str(bbox[1]) + " " + str(bbox[0]) + " 0.000000000 0.000000000\n" self._dsfstring = self._dsfstring + "POLYGON_POINT " + str(bbox[3]) + " " + str(bbox[0]) + " 1.000000000 0.000000000\n" self._dsfstring = self._dsfstring + "POLYGON_POINT " + str(bbox[3]) + " " + str(bbox[2]) + " 1.000000000 1.000000000\n" self._dsfstring = self._dsfstring + "POLYGON_POINT " + str(bbox[1]) + " " + str(bbox[2]) + " 0.000000000 1.000000000\n" self._dsfstring = self._dsfstring + "END_WINDING\n" self._dsfstring = self._dsfstring + "END_POLYGON\n" # Adjust forward cur_lng = cur_lng + mstr_zl_18 curpol = curpol + 1 # Adjust up, reset longitude cur_lng = self._longitude cur_lat = cur_lat + self._vstep # OK... we can now save this self.write_dsf_txt() # Generate the single .pol files now mstr_msg("xp_dsfgen", "[X-Plane] Beginning generation of terrain/*.pol files") cur_lat = self._latitude cur_lng = self._longitude lclat = cur_lat + (self._vstep/2) lclng = cur_lng + (mstr_zl_18/2) for v in range(1, self._maxlat+1): for h in range(1, self._maxlng+1): dm = self._findWidthOfLongitude(cur_lng) * mstr_zl_18 dg = self.find_ortho_diameter(dm) polstr = "" polstr = polstr + "A\n" polstr = polstr + "850\n" polstr = polstr + "DRAPED_POLYGON\n" polstr = polstr + "\n" polstr = polstr + "TEXTURE_NOWRAP ../orthos/"+str(v)+"_"+str(h)+".dds\n" # Check for existence of a normal map # If there is one, we will add that too end_latlng = self.xplane_latlng_folder([self._latitude, self._longitude]) if os.path.isfile(mstr_datafolder + "Tiles/z_orthographic_"+end_latlng+"/normals/" + str(v) + "_" + str(h) + ".png") == True: polstr = polstr + "TEXTURE_NORMAL 1 ../normals/"+str(v)+"_"+str(h)+".png\n" polstr = polstr + "SCALE "+str(dm)+" "+str(dm)+"\n" polstr = polstr + "LOAD_CENTER "+str(lclat)+" " +str(lclng)+" " +str(dg)+ " " +str(mstr_photores)+"\n" polstr = polstr + "LAYER_GROUP TERRAIN 1\n" # Save this content with open(mstr_datafolder + "Tiles/z_orthographic_"+end_latlng+"/terrain/"+str(v)+"_"+str(h)+".pol", 'w') as textfile: textfile.write(polstr) # Adjust forward cur_lng = cur_lng + mstr_zl_18 lclng = cur_lng + (mstr_zl_18/2) cur_lng = self._longitude lclng = cur_lng + (mstr_zl_18/2) cur_lat = cur_lat + self._vstep lclat = cur_lat + (self._vstep/2) mstr_msg("xp_dsfgen", "[X-Plane] Converting DSF txt") self.convert_dsf_text() mstr_msg("xp_dsfgen", "[X-Plane] DSF for tile completed") # Testing dsf = mstr_xp_dsfgen(51, 7, 101, 63, 0.01010) dsf.build_dsf_for_tile()