2024-09-12 22:51:39 +02:00
|
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
|
|
# 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")
|