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 = []

46
og.py
View File

@ -19,25 +19,26 @@ from defines import *
# Print a welcome message # Print a welcome message
print(r' ') def _welcome():
print(r' ____ __ __ __ _ ') print(r' ')
print(r' / __ \_____/ /_/ /_ ____ ____ __________ _____ / /_ (_)____') print(r' ____ __ __ __ _ ')
print(r' / / / / ___/ __/ __ \/ __ \/ __ `/ ___/ __ `/ __ \/ __ \/ / ___/') print(r' / __ \_____/ /_/ /_ ____ ____ __________ _____ / /_ (_)____')
print(r'/ /_/ / / / /_/ / / / /_/ / /_/ / / / /_/ / /_/ / / / / / /__ ') print(r' / / / / ___/ __/ __ \/ __ \/ __ `/ ___/ __ `/ __ \/ __ \/ / ___/')
print(r'\____/_/ \__/_/ /_/\____/\__, /_/ \__,_/ .___/_/ /_/_/\___/ ') print(r'/ /_/ / / / /_/ / / / /_/ / /_/ / / / /_/ / /_/ / / / / / /__ ')
print(r' /____/ /_/ ') print(r'\____/_/ \__/_/ /_/\____/\__, /_/ \__,_/ .___/_/ /_/_/\___/ ')
print(r' ----------------------------------------------------------------') print(r' /____/ /_/ ')
print(r' A ground texture generator, using photo realistic resources,') print(r' ----------------------------------------------------------------')
print(r' for flight simulators.') print(r' A ground texture generator, using photo realistic resources,')
print(r' ----------------------------------------------------------------') print(r' for flight simulators.')
print(r' Developed by and (c) Marcus Str.') print(r' ----------------------------------------------------------------')
print(r' Website: https://marstr.online/orthographic') print(r' Developed by and (c) Marcus Str.')
print(r' Code repo: https://marstr.online/code/gitweb.cgi?p=orthographic') print(r' Website: https://marstr.online/orthographic')
print(r' ----------------------------------------------------------------') print(r' Code repo: http://marstr.online:3000/marstr/orthographic')
print(r' If you paid for this software, you have been scammed. The source') print(r' ----------------------------------------------------------------')
print(r' code and always available free of charge.') print(r' If you paid for this software, you have been scammed. The source')
print(r' ----------------------------------------------------------------') print(r' code and always available free of charge.')
print(r' ') print(r' ----------------------------------------------------------------')
print(r' ')
# Evaluate CLI arguments and process tile. # Evaluate CLI arguments and process tile.
@ -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