Perlin-noise based renderer fully functional. perlin.py adjusted to produce fully dynamic Perlin noises, per resource. Scenery generated to produce proper ZL16 tiles. Correction for incorrectly rendered water bodies in photogen.py . Removed waterway:stream from rendering as it is no actual geographical feature.

This commit is contained in:
Marcus Str. 2025-02-01 16:39:15 +01:00
parent 9eddb97425
commit 88905ac509
7 changed files with 205 additions and 165 deletions

View File

@ -71,7 +71,7 @@ mstr_xp_genscenery = True
# Generate normal maps for X-Plane scenery? # Generate normal maps for X-Plane scenery?
# Strong recommendation: yes # Strong recommendation: yes
mstr_xp_scn_normalmaps = True mstr_xp_scn_normalmaps = False
# Paths to required X-Plane scenery tools # Paths to required X-Plane scenery tools
#mstr_xp_meshtool = "/home/marcus/Developer/Projects/orthographic/bin/MeshTool" #mstr_xp_meshtool = "/home/marcus/Developer/Projects/orthographic/bin/MeshTool"
@ -195,11 +195,11 @@ mstr_ortho_layers = [
("building", "commercial", "building", "commercial"), ("building", "commercial", "building", "commercial"),
("building", "warehouse", "building", "warehouse"), ("building", "warehouse", "building", "warehouse"),
("building", "yes", "building", "common"), ("building", "yes", "building", "common"),
#("waterway", "river", 3),
("water", "lake", "natural", "water"), ("water", "lake", "natural", "water"),
("water", "pond", "natural", "water"), ("water", "pond", "natural", "water"),
("water", "river", "natural", "water"), ("water", "river", "natural", "water"),
("natural", "water", "natural", "water"), ("natural", "water", "natural", "water"),
("waterway", "river", 3),
("place", "sea", "natural", "sea"), ("place", "sea", "natural", "sea"),
("place", "ocean", "natural", "sea") ("place", "ocean", "natural", "sea")
] ]
@ -244,11 +244,12 @@ mstr_mask_blur = [
("natural", "water", 1), ("natural", "water", 1),
("natural", "bay", 7), ("natural", "bay", 7),
("natural", "beach", 7), ("natural", "beach", 7),
("natural", "sand", 3),
("water", "lake", 3), ("water", "lake", 3),
("water", "pond", 3), ("water", "pond", 3),
("water", "river", 3), ("water", "river", 3),
("leisure", "swimming_pool", 3), ("leisure", "swimming_pool", 3),
("waterway", "river", 3), #("waterway", "river", 3),
("waterway", "stream", 2), ("waterway", "stream", 2),
("amenity", "parking", 1), ("amenity", "parking", 1),
("amenity", "school", 1), ("amenity", "school", 1),

View File

@ -361,8 +361,8 @@ class mstr_layergen:
for x in range(0, layer.width): for x in range(0, layer.width):
xp = prln_x + int(x/prln_pix_step) xp = prln_x + int(x/prln_pix_step)
yp = prln_y + int(y/prln_pix_step) yp = prln_y + int(y/prln_pix_step)
if xp == perlin_map.width: xp = perlin_map.width - 1 if xp >= perlin_map.width: xp = perlin_map.width - 1
if yp == perlin_map.height: yp = perlin_map.height - 1 if yp >= perlin_map.height: yp = perlin_map.height - 1
pc = perlin_pix[xp,yp] pc = perlin_pix[xp,yp]
df = randrange(0, 6) df = randrange(0, 6)
lc = (pc[0]+df, pc[1]+df, pc[2]+df, 255) lc = (pc[0]+df, pc[1]+df, pc[2]+df, 255)
@ -666,7 +666,6 @@ class mstr_layergen:
#layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" ) #layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
mstr_msg("layergen", "Layer image finalized and saved.") mstr_msg("layergen", "Layer image finalized and saved.")
# Return image # Return image
return layer_comp return layer_comp
@ -801,8 +800,6 @@ class mstr_layergen:
if mp[3] == 255 and tp[3] == 255: if mp[3] == 255 and tp[3] == 255:
tree_pix[x,y] = (0,0,0,0) tree_pix[x,y] = (0,0,0,0)
if mstr_shadow_enabled == True: if mstr_shadow_enabled == True:
tree_pix = trees.load() tree_pix = trees.load()
shadow_pix = tree_shadow.load() shadow_pix = tree_shadow.load()
@ -842,7 +839,6 @@ class mstr_layergen:
return bld_comp return bld_comp
# Find the next "by-ten" numbers for the current latitude and longitude # Find the next "by-ten" numbers for the current latitude and longitude
def find_earthnavdata_number(self): def find_earthnavdata_number(self):
earthnavdata = [] earthnavdata = []

10
og.py
View File

@ -19,6 +19,7 @@ from defines import *
# Print a welcome message # Print a welcome message
def _welcome():
print(r' ') print(r' ')
print(r' ____ __ __ __ _ ') print(r' ____ __ __ __ _ ')
print(r' / __ \_____/ /_/ /_ ____ ____ __________ _____ / /_ (_)____') print(r' / __ \_____/ /_/ /_ ____ ____ __________ _____ / /_ (_)____')
@ -32,7 +33,7 @@ print(r' for flight simulators.')
print(r' ----------------------------------------------------------------') print(r' ----------------------------------------------------------------')
print(r' Developed by and (c) Marcus Str.') print(r' Developed by and (c) Marcus Str.')
print(r' Website: https://marstr.online/orthographic') print(r' Website: https://marstr.online/orthographic')
print(r' Code repo: https://marstr.online/code/gitweb.cgi?p=orthographic') print(r' Code repo: http://marstr.online:3000/marstr/orthographic')
print(r' ----------------------------------------------------------------') print(r' ----------------------------------------------------------------')
print(r' If you paid for this software, you have been scammed. The source') print(r' If you paid for this software, you have been scammed. The source')
print(r' code and always available free of charge.') print(r' code and always available free of charge.')
@ -61,20 +62,17 @@ if __name__ == '__main__':
# Create the class and init values # Create the class and init values
og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd(), prep) og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd(), prep)
# Prepare a tile
if sys.argv[3] == "prepare":
og._prepareTile()
# Generate orthos # Generate orthos
if sys.argv[3] != "prepare" and sys.argv[3] != "xpscenery": if sys.argv[3] != "prepare" and sys.argv[3] != "xpscenery":
_welcome()
og._generateOrthos_mt(int(sys.argv[3])) og._generateOrthos_mt(int(sys.argv[3]))
# Build the terrain mesh and assign ground textures # Build the terrain mesh and assign ground textures
if sys.argv[3] == "xpscenery": if sys.argv[3] == "xpscenery":
_welcome()
og.generate_xp_scenery() og.generate_xp_scenery()
if cli == False and pbf == False: if cli == False and pbf == False:
mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.") mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.")
print ("") print ("")

View File

@ -241,15 +241,16 @@ class mstr_orthographic:
# Step 2 # Step 2
# Create folders and generate all perlin noise maps # Create folders and generate all perlin noise maps
self._createFolders() self._createFolders()
""" #"""
for l in mstr_ortho_layers: for l in mstr_ortho_layers:
if l[0] != "highway" and l[0] != "building": if l[0] != "highway" and l[0] != "building":
fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/" + l[0] + "_" + l[1] + ".png"
if os.path.isfile(fn) == False:
mstr_important_msg("orthographic", "Generating Perlin map for " + l[0] + ":" + l[1]) mstr_important_msg("orthographic", "Generating Perlin map for " + l[0] + ":" + l[1])
prln = mstr_perlin(l[0], l[1], mlat, mlng, 16) prln = mstr_perlin(l[0], l[1], mlat, mlng, 16)
pmap = prln._generatePerlinMap() pmap = prln._generatePerlinMap()
fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/" + l[0] + "_" + l[1] + ".png"
pmap.save(fn) pmap.save(fn)
""" #"""
# For completion layers # For completion layers
numbers = list(range(1, 16)) numbers = list(range(1, 16))
@ -285,9 +286,15 @@ class mstr_orthographic:
mlng = 1 mlng = 1
while bb_lat < self._lat + 1: while bb_lat < self._lat + 1:
bb_lat = bb_lat + self._vstep bb_lat = bb_lat + self._vstep
if bb_lat >= self._lat + 1:
bb_lat = bb_lat - self._zoomlevel
break
mlat = mlat+1 mlat = mlat+1
while bb_lng < self._long + 1: while bb_lng < self._long + 1:
bb_lng = bb_lng + self._zoomlevel bb_lng = bb_lng + self._zoomlevel
if bb_lng >= self._long + 1:
bb_lng = bb_lng - self._zoomlevel
break
mlng = mlng+1 mlng = mlng+1
mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng)) mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng))
maxlatlng = [ mlat, mlng ] maxlatlng = [ mlat, mlng ]
@ -631,7 +638,7 @@ class mstr_orthographic:
# - the finished orthos # - the finished orthos
# - a current LIDAR scan of the terrain # - a current LIDAR scan of the terrain
def generate_xp_scenery(self): def generate_xp_scenery(self):
mstr_msg("orthographic", "[X-Plane] Generation of scenery started") mstr_important_msg("orthographic", "[X-Plane] Generation of scenery started")
# This call appears quite often... surely this can be done better # This call appears quite often... surely this can be done better
mlat = 1 mlat = 1
@ -654,8 +661,8 @@ class mstr_orthographic:
mstr_msg("orthographic", "[X-Plane] Scenery object instantiated") mstr_msg("orthographic", "[X-Plane] Scenery object instantiated")
# Download LIDAR scan from our endpoint # Download LIDAR scan from our endpoint
xpscn.acquire_elevation_data() #xpscn.acquire_elevation_data()
mstr_msg("orthographic", "[X-Plane] Elevation data acquired") #mstr_msg("orthographic", "[X-Plane] Elevation data acquired")
# Generate the .ter files # Generate the .ter files
xpscn.build_ter_files() xpscn.build_ter_files()

107
perlin.py
View File

@ -15,6 +15,8 @@ from PIL import Image # Depends on the Pillow lib
from random import randrange from random import randrange
import random import random
import numpy as np import numpy as np
import glob
from defines import *
from perlin_numpy import ( from perlin_numpy import (
generate_perlin_noise_2d, generate_fractal_noise_2d generate_perlin_noise_2d, generate_fractal_noise_2d
) )
@ -32,85 +34,42 @@ class mstr_perlin:
if zl == 16: self._mapscale = 80 if zl == 16: self._mapscale = 80
if zl == 18: self._mapscale = 20 if zl == 18: self._mapscale = 20
# Define some base colors
# tag, value, base color, perlin noise color palette
self._bc = [
# Amenity
["amenity", "parking", (31,32,34,255), [ (31,32,34), (74,74,73), (56,55,55), (34,40,44) ]],
["amenity", "school", (26,26,26,255), [ (26,26,26), (78,78,78), (28,29,25), (86,74,64) ]],
# Barrier
["barrier", "hedge", (27,37,25,255), [ (27,37,25), (8,14,13), (15,20,23), (40,40,38) ]],
# Boundary
["boundary", "administrative", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]],
# Landuse
["landuse", "cemetery", (22,22,20,255), [ (22,22,20), (44,43,40), (31,33,29), (39,37,34) ]],
["landuse", "construction", (65,51,32,255), [ (65,51,32), (77,73,67), (45,49,43), (74,63,48) ]],
["landuse", "farmland", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["landuse", "farmyard", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["landuse", "forest", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]],
["landuse", "grass", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]],
["landuse", "greenfield", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]],
["landuse", "meadow", (28,29,27,255), [ (28,29,27), (36,42,40), (45,44,41), (21,22,20) ]],
["landuse", "military", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]],
["landuse", "orchard", (27,38,27,255), [ (27,38,27), (19,22,16), (40,42,33), (40,50,45) ]],
["landuse", "recreation_ground", (27,38,27,255), [ (27,38,27), (19,22,16), (40,42,33), (40,50,45) ]],
["landuse", "residential", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]],
["landuse", "residential-boundary", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]],
["landuse", "vineyard", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
# Leisure
["leisure", "dog_park", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["leisure", "garden", (39,39,32,255), [ (39,39,32), (38,47,43), (35,41,31), (21,25,18) ]],
["leisure", "golf_course", (29,39,28,255), [ (29,39,28), (86,81,76), (60,55,45), (13,18,14) ]],
["leisure", "green", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["leisure", "nature_reserve", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]],
["leisure", "park", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]],
["leisure", "pitch", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["leisure", "playground", (65,51,32,255), [ (65,51,32), (77,73,67), (45,49,43), (74,63,48) ]],
["leisure", "sports_centre", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]],
["leisure", "swimming_pool", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]],
# Natural
["natural", "bare_rock", (47,54,56,255), [ (47,54,56), (79,75,71), (84,51,43), (26,33,45) ]],
["natural", "beach", (79,76,69,255), [ (79,76,69), (82,83,80), (71,61,48), (69,68,66) ]],
["natural", "desert", (79,64,42,255), [ (79,64,42), (79,47,24), (37,41,27), (51,39,23) ]],
["natural", "grassland", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]],
["natural", "heath", (26,28,23,255), [ (26,28,23), (45,44,42), (30,34,29), (45,45,42) ]],
["natural", "sand", (79,76,69,255), [ (79,76,69), (82,83,80), (71,61,48), (69,68,66) ]],
["natural", "scree", (38,36,33,255), [ (38,36,33), (81,80,76), (43,42,31), (37,34,27) ]],
["natural", "scrub", (41,39,35,255), [ (41,39,35), (44,38,34), (30,32,39), (20,27,22) ]],
["natural", "water", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]],
["natural", "wetland", (32,34,33,255), [ (32,34,33), (5, 8, 8), (35,36,35), (17,19,17) ]],
["natural", "wood", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]],
# Water
["water", "lake", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]],
["water", "pond", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]],
["water", "river", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]]
]
def _generateColorIndex(self):
idx = []
colorsrc = glob.glob(mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/*.png")
if len(colorsrc) >= 4:
picknum = list(range(0, len(colorsrc)))
picksel = random.sample(picknum, 4)
for p in range(0, 4):
ptc = Image.open(colorsrc[picksel[p]])
ptc = ptc.resize((1,1), resample=Image.Resampling.BILINEAR)
clr = ptc.getpixel((0,0))
res = (clr[0], clr[1], clr[2])
idx.append(res)
# Find base color depending on tag/value
def _findBaseColor(self):
idx = -1
for b in range(len(self._bc)):
if self._bc[b][0] == self._tag and self._bc[b][1] == self._value:
idx = b
break
return idx return idx
# Generates a Perlin map depending on what we need # Generates a Perlin map depending on what we need
def _generatePerlinMap(self): def _generatePerlinMap(self):
idx = self._findBaseColor() #idx = self._findBaseColor()
clr = self._generateColorIndex()
bw = self._mlng * self._mapscale bw = self._mlng * self._mapscale
bh = self._mlat * self._mapscale bh = self._mlat * self._mapscale
wh = 2048 base = Image.new("RGBA", (bw,bh))
base = Image.new("RGBA", (bw,bh), self._bc[idx][2])
for c in range(len(self._bc[idx][3])): if len(clr) == 4:
wh = 2048
base = Image.new("RGBA", (bw,bh), clr[0])
for c in range(len(clr)):
np.random.seed(randrange(10000000, 100000000)) np.random.seed(randrange(10000000, 100000000))
noise1 = generate_fractal_noise_2d((wh,wh), (16,16), 5) noise1 = generate_fractal_noise_2d((wh,wh), (64,64), 5)
np.random.seed(randrange(10000000, 100000000)) np.random.seed(randrange(10000000, 100000000))
noise2 = generate_fractal_noise_2d((wh,wh), (8,8), 4) noise2 = generate_fractal_noise_2d((wh,wh), (32,32), 4)
im1 = Image.new("RGBA", (wh,wh)) im1 = Image.new("RGBA", (wh,wh))
im2 = Image.new("RGBA", (wh,wh)) im2 = Image.new("RGBA", (wh,wh))
@ -119,9 +78,9 @@ class mstr_perlin:
for x in range(0, wh): for x in range(0, wh):
n = (noise1[y][x] + 1) / 2 n = (noise1[y][x] + 1) / 2
v = ( v = (
self._bc[idx][3][c][0], clr[c][0],
self._bc[idx][3][c][1], clr[c][1],
self._bc[idx][3][c][2], clr[c][2],
int(n*255) int(n*255)
) )
im1.putpixel((x,y), v) im1.putpixel((x,y), v)
@ -130,14 +89,14 @@ class mstr_perlin:
for x in range(0, wh): for x in range(0, wh):
n = (noise2[y][x] + 1) / 2 n = (noise2[y][x] + 1) / 2
v = ( v = (
self._bc[idx][3][c][0], clr[c][0],
self._bc[idx][3][c][1], clr[c][1],
self._bc[idx][3][c][2], clr[c][2],
int(n*255) int(n*255)
) )
im2.putpixel((x,y), v) im2.putpixel((x,y), v)
im = Image.blend(im1,im2, alpha=0.5) im = Image.blend(im1,im2, alpha=0.675)
base.alpha_composite(im) base.alpha_composite(im)
# Provide the perlin map # Provide the perlin map

View File

@ -210,8 +210,6 @@ class mstr_photogen:
#self._tile = ImageEnhance.Contrast(self._tile).enhance(0.1) #self._tile = ImageEnhance.Contrast(self._tile).enhance(0.1)
# This we can save accordingly. # This we can save accordingly.
self._tile.show()
exit()
self._tile.save(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png") self._tile.save(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png")
# Now we convert this into a DDS # Now we convert this into a DDS
@ -489,13 +487,13 @@ class mstr_photogen:
# First the residential/forest dilemma # First the residential/forest dilemma
residential = -1 residential = -1
forest = -1 forest = -1
reserve = -1
curlyr = 0 curlyr = 0
for lyr in self._lyrnames: for lyr in self._lyrnames:
if lyr[0] == "landuse" and lyr[1] == "residential": if lyr[0] == "landuse" and lyr[1] == "residential": residential=curlyr
residential=curlyr if lyr[0] == "landuse" and lyr[1] == "forest": forest = curlyr
if lyr[0] == "landuse" and lyr[1] == "forest": if lyr[0] == "leisure" and lyr[1] == "nature_reserve": reserve = curlyr
forest = curlyr
curlyr = curlyr+1 curlyr = curlyr+1
# Make sure we hit the correct layers with correct content! # Make sure we hit the correct layers with correct content!
@ -515,11 +513,82 @@ class mstr_photogen:
layers[forest].alpha_composite(rsd_adj) layers[forest].alpha_composite(rsd_adj)
if residential != -1 and reserve != -1:
rsd_pix = layers[residential].load()
frs_pix = layers[reserve].load()
rsd_adj = Image.new("RGBA", (self._imgsize, self._imgsize))
adj_pix = rsd_adj.load()
for y in range(0, self._tile.height):
for x in range(0, self._tile.width):
fp = frs_pix[x,y]
rp = rsd_pix[x,y]
if rp[3] > 0 and fp[3] > 0:
adj_pix[x,y] = (rp[0], rp[1], rp[2], rp[3])
layers[reserve].alpha_composite(rsd_adj)
# At ZL16 there seems to be an issue with water bodies, especially rivers (natural:water).
# Let's see if we can correct that
# natural:water
ntrl_water = False
ntrl_idx = 0
for lyr in self._lyrnames:
if lyr[0] == "natural" and lyr[1] == "water":
ntrl_water = True
break
ntrl_idx = ntrl_idx + 1
if ntrl_water == True:
# We'll go through all layers and "shavve off" excess.
# This way we can hopefully correct incorrect line and polygon renders.
for l in range(0, len(layers)):
if l < ntrl_idx: # <- We don't want to load the target layer itself
lyrpix = layers[l].load()
wtr_pix = layers[ntrl_idx].load()
for y in range(0, self._tile.height):
for x in range(0, self._tile.width):
lp = lyrpix[x,y]
wp = wtr_pix[x,y]
if lp[3] > 0:
a = 255 - lp[3]
if a < wp[3]:
c = (wp[0], wp[1], wp[2], a)
wtr_pix[x,y] = c
# water:river
river_water = False
river_idx = 0
for lyr in self._lyrnames:
if lyr[0] == "water" and lyr[1] == "river":
river_water = True
break
river_idx = river_idx + 1
if river_water == True:
# We'll go through all layers and "shavve off" excess.
# This way we can hopefully correct incorrect line and polygon renders.
for l in range(0, len(layers)):
if l < river_idx and l != ntrl_idx: # <- We don't want to load the target layer itself
lyrpix = layers[l].load()
wtr_pix = layers[river_idx].load()
for y in range(0, self._tile.height):
for x in range(0, self._tile.width):
lp = lyrpix[x,y]
wp = wtr_pix[x,y]
if lp[3] > 0:
a = 255 - lp[3]
if a < wp[3]:
c = (wp[0], wp[1], wp[2], a)
wtr_pix[x,y] = c
return layers return layers
# This checks the final image for empty patches. Should one be # This checks the final image for empty patches. Should one be
# found, we will generate something to fill the gap. If this is # found, we will generate something to fill the gap. If this is
# the case, we will also note this in the database for the tile, # the case, we will also note this in the database for the tile,

View File

@ -92,8 +92,13 @@ class mstr_xp_scenery:
end = self.find_earthnavdata_number() end = self.find_earthnavdata_number()
llf = self.xplane_latlng_folder(end) llf = self.xplane_latlng_folder(end)
meshtxt = mstr_datafolder + "_cache/mesh_"+self._latlngfld+".txt" meshtxt = mstr_datafolder + "_cache/mesh_"+self._latlngfld+".txt"
cmd = mstr_xp_dsftool + " --text2dsf " + meshtxt + " '" + mstr_datafolder + "z_orthographic/Earth nav data/" + llf + "/" + self._latlngfld + ".dsf'" meshtxt = meshtxt.replace("/", "\\")
cmdpath = mstr_datafolder + "_cache/" + self._latlngfld + ".dsf"
#cmdpath = mstr_datafolder + "z_orthographic/Earth nav data/" + llf + "/" + self._latlngfld + ".dsf"
cmdpath = cmdpath.replace("/", "\\")
cmd = mstr_xp_dsftool + " --text2dsf " + meshtxt + " " + cmdpath
os.system(cmd) os.system(cmd)
os.replace(cmdpath, mstr_datafolder + "z_orthographic/Earth nav data/" + llf + "/" + self._latlngfld + ".dsf")
# Find exact with of longitude # Find exact with of longitude
@ -165,14 +170,14 @@ class mstr_xp_scenery:
cur_lat = self._lat cur_lat = self._lat
cur_lng = self._lng cur_lng = self._lng
xp_folder = self.xplane_latlng_folder([self._lat, self._lng]) xp_folder = self.xplane_latlng_folder([self._lat, self._lng])
for lat in range(1, self._mlat+1): for lat in range(1, self._mlat):
for lng in range(1, self._mlng+1): for lng in range(1, self._mlng):
wdt = self.find_width_of_longitude(cur_lat) wdt = self.find_width_of_longitude(cur_lat)
dmt = wdt * mstr_zl_18 dmt = wdt * mstr_zl_16
cnt_x = cur_lat + (self._vstep/2) cnt_x = cur_lat + (self._vstep/2)
cnt_y = cur_lng + (mstr_zl_18/2) cnt_y = cur_lng + (mstr_zl_16/2)
terstr = "" terstr = ""
terstr = terstr + "A\r\n" terstr = terstr + "A\r\n"
@ -181,15 +186,15 @@ class mstr_xp_scenery:
terstr = terstr + "\r\n" terstr = terstr + "\r\n"
terstr = terstr + "LOAD_CENTER " + str(cnt_x) + " " + str(cnt_y) + " " + str(dmt) + " 2048\r\n" terstr = terstr + "LOAD_CENTER " + str(cnt_x) + " " + str(cnt_y) + " " + str(dmt) + " 2048\r\n"
terstr = terstr + "BASE_TEX_NOWRAP ../../orthos/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".dds\r\n" terstr = terstr + "BASE_TEX_NOWRAP ../../orthos/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".dds\r\n"
if mstr_xp_scn_normalmaps: #if mstr_xp_scn_normalmaps:
terstr = terstr + "NORMAL_TEX 1.0 ../../normals/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".png\r\n" # terstr = terstr + "NORMAL_TEX 1.0 ../../normals/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".png\r\n"
terfln = mstr_datafolder + "z_orthographic/terrain/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".ter" terfln = mstr_datafolder + "z_orthographic/terrain/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".ter"
with open(terfln, 'w') as textfile: with open(terfln, 'w') as textfile:
textfile.write(terstr) textfile.write(terstr)
cur_lng = round(cur_lng + mstr_zl_18, 6) cur_lng = round(cur_lng + mstr_zl_16, 6)
cur_lng = self._lng cur_lng = self._lng
cur_lat = round(cur_lat + self._vstep, 6) cur_lat = round(cur_lat + self._vstep, 6)
@ -200,7 +205,7 @@ class mstr_xp_scenery:
# This generates the entire terrain mesh # This generates the entire terrain mesh
def generate_terrain_mesh(self): def generate_terrain_mesh(self):
# Get the DEM model file name, and acquire important info about the data # Get the DEM model file name, and acquire important info about the data
meshfn = mstr_datafolder + "_cache/" + self.build_dem_filename() meshfn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + self.build_dem_filename()
siz = os.path.getsize(meshfn) siz = os.path.getsize(meshfn)
dim = int(math.sqrt(siz/2)) dim = int(math.sqrt(siz/2))
assert dim*dim*2 == siz, 'Invalid file size' assert dim*dim*2 == siz, 'Invalid file size'
@ -245,8 +250,8 @@ class mstr_xp_scenery:
dsf_str = "" dsf_str = ""
# Orthos # Orthos
for lat in range(1, self._mlat+1): for lat in range(1, self._mlat):
for lng in range(1, self._mlng+1): for lng in range(1, self._mlng):
# Write the line only if an ortho exists of course. # Write the line only if an ortho exists of course.
ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".dds" ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".dds"
if os.path.isfile(ddsf): if os.path.isfile(ddsf):
@ -261,8 +266,8 @@ class mstr_xp_scenery:
# Current patch # Current patch
curpatch = 0 curpatch = 0
for lat in range(1, self._mlat+1): for lat in range(1, self._mlat):
for lng in range(1, self._mlng+1): for lng in range(1, self._mlng):
# Create the patch only if the matching ortho exists. # Create the patch only if the matching ortho exists.
# This way we make sure that we hit the same order as the .ter files. # This way we make sure that we hit the same order as the .ter files.
@ -273,7 +278,7 @@ class mstr_xp_scenery:
# Base coords for this ortho # Base coords for this ortho
base_lat = self._lat + ((lat-1) * self._vstep) base_lat = self._lat + ((lat-1) * self._vstep)
base_lng = self._lng + ((lng-1) * mstr_zl_18) base_lng = self._lng + ((lng-1) * mstr_zl_16)
# Begin a new patch # Begin a new patch
mstr_msg("xp_scenery", "[X-Plane] Processing ortho patch " + str(curpatch)) mstr_msg("xp_scenery", "[X-Plane] Processing ortho patch " + str(curpatch))
@ -281,9 +286,10 @@ class mstr_xp_scenery:
textfile.write("BEGIN_PATCH " + str(curpatch) + " 0.000000 -1.000000 1 7\r\n") textfile.write("BEGIN_PATCH " + str(curpatch) + " 0.000000 -1.000000 1 7\r\n")
# Step for each ortho vertex # Step for each ortho vertex
odiv = 4 #odiv = 4
odiv = 16
latstep = self._vstep/odiv latstep = self._vstep/odiv
lngstep = mstr_zl_18 /odiv lngstep = mstr_zl_16 /odiv
uv_step = 1 / odiv uv_step = 1 / odiv
# Generate the ortho tile # Generate the ortho tile
@ -300,10 +306,14 @@ class mstr_xp_scenery:
lng_l = base_lng lng_l = base_lng
if y == 0: if y == 0:
lat_b = base_lat lat_b = base_lat
if y == 3: if y == 15:
#if y == 3:
lat_t = base_lat + self._vstep lat_t = base_lat + self._vstep
if x == 3: if x == 15:
lng_r = base_lng + mstr_zl_18 lng_r = base_lng + mstr_zl_16
#if x == 3:
#lng_r = base_lng + mstr_zl_18
# Corrections, just in case # Corrections, just in case
if lat_b > self._lat + 1: lat_b = self._lat+1 if lat_b > self._lat + 1: lat_b = self._lat+1