diff --git a/defines.py b/defines.py index b437c17..638d86b 100644 --- a/defines.py +++ b/defines.py @@ -132,6 +132,7 @@ mstr_ortho_layers = [ ("natural", "bare_rock", "natural", "bare_rock"), ("highway", "track", 3), ("highway", "path", 3), + ("highway", "footway", 4), ("leisure", "park", "leisure", "green"), ("leisure", "dog_park", "leisure", "green"), ("leisure", "garden", "leisure", "green"), @@ -144,7 +145,6 @@ mstr_ortho_layers = [ # Z-Order 2 ("highway", "service", 6), ("highway", "residential", 12), - ("highway", "footway", 4), ("highway", "primary", 25), ("highway", "secondary", 13), ("highway", "tertiary", 20), @@ -191,6 +191,7 @@ mstr_ortho_layers = [ ("building", "kindergarten", "building", "kindergarten"), ("building", "public", "building", "public"), ("building", "commercial", "building", "commercial"), + ("building", "warehouse", "building", "warehouse"), ("building", "yes", "building", "common"), ("water", "lake", "natural", "water"), ("water", "pond", "natural", "water"), @@ -281,9 +282,10 @@ mstr_mask_blur = [ ("building", "terrace", 1), ("building", "hangar", 1), ("building", "school", 1), - ("building", "kindergarten", 1), - ("building", "public", 1), - ("building", "commercial", 1), + ("building", "kindergarten", 1), + ("building", "public", 1), + ("building", "commercial", 1), + ("building", "warehouse", 1), ("building", "yes", 0), ("place", "sea", 1), ("place", "ocean", 1) @@ -396,7 +398,13 @@ mstr_building_base_colors = [ "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" ] - ) + ), + ("warehouse", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), ] diff --git a/functions.py b/functions.py index c1d89b4..0e1cc44 100644 --- a/functions.py +++ b/functions.py @@ -107,4 +107,5 @@ def xplane_latlng_folder(numbers): 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 \ No newline at end of file + return fstr + diff --git a/layergen.py b/layergen.py index adf6451..ff3dd65 100644 --- a/layergen.py +++ b/layergen.py @@ -14,14 +14,20 @@ import glob import os +import time from random import randrange import random -from PIL import Image, ImageFilter, ImageDraw + +import PIL.ImageOps +from PIL import Image, ImageFilter, ImageDraw, ImageOps, ImageFile from defines import * from log import * from tileinfo import * from osmxml import * from functions import * +from resourcegen import * + +ImageFile.LOAD_TRUNCATED_IMAGES = True class mstr_layergen: @@ -116,7 +122,7 @@ class mstr_layergen: # Find the source to use pre-determined in phase one def findLayerSource(self): # The source number - src = -1 + src = [] # The already existing source data srcfile = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(self._lat_number) + "_" + str(self._lng_number) @@ -125,26 +131,125 @@ class mstr_layergen: with open(srcfile) as file: for line in file: linedata = line.split(" ") - if linedata[2] == self._tag and linedata[3] == self._value: - src = int(linedata[4]) + if linedata[0] == self._tag and linedata[1] == self._value: + src = linedata[2].split(",") break - # Should we encounter a -1 at this point, we can choose something + # Should we encounter a 0 length at this point, we can choose something # It means it touches no border as it was not found in the file - if src == -1: - root_folder = mstr_datafolder + "textures/" - for s in mstr_ortho_layers: - if s[0] == self._tag and s[1] == self._value: - fld_main = len(s)-2 - fld_sub = len(s)-1 - root_folder = root_folder + s[fld_main] + "/" + s[fld_sub] - - brd = glob.glob(root_folder + "/brd/b*.png") - src = randrange(1, len(brd)+1) + if len(src) == 0: + while len(src) < 6: + pick = randrange(1, 16) + if pick not in src: src.append(pick) return src + # Find layer contrast to apply, if any + def findLayerContrast(self): + + contrast = 0 + + # The already existing source data + srcfile = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(self._lat_number) + "_" + str( + self._lng_number) + + # Let's open the file and find our entry + with open(srcfile) as file: + for line in file: + linedata = line.split(" ") + if linedata[0] == self._tag and linedata[1] == self._value: + src = linedata[2].split(",") + contrast = src[4] + + return contrast + + + # Generates some random tree. + # We will now move away from using pre-made trees... + # they didn't look so great + def generate_tree(self): + sx = randrange(18, 31) + sy = randrange(18, 31) + + treepoly = Image.new("RGBA", (sx, sy)) + draw = ImageDraw.Draw(treepoly) + + draw.ellipse((4, 4, sx - 4, sy - 4), fill="black") + + tree = Image.new("RGBA", (sx, sy)) + treepx = tree.load() + maskpx = treepoly.load() + + # How many tree points do we want? + treepts = 75 + # How many of those have been drawn? + ptsdrawn = 0 + + bc = [ + (36, 50, 52), + (30, 41, 39), + (32, 45, 37), + (32, 39, 49), + (33, 34, 40), + (44, 50, 53), + (40, 46, 48), + (14, 31, 38), + (17, 41, 33), + (39, 56, 35), + (51, 51, 42), + (12, 27, 31), + (45, 59, 48), + (37, 54, 29), + (59, 50, 34), + (59, 59, 35), + (59, 51, 35), + (70, 72, 45), + (48, 59, 44), + (29, 47, 23), + (47, 61, 43), + (29, 68, 15), + (53, 77, 63), + (20, 68, 40) + ] + + bcp = randrange(0, len(bc)) + + treedraw = ImageDraw.Draw(tree) + while ptsdrawn < treepts + 1: + rx = randrange(0, sx) + ry = randrange(0, sy) + mp = maskpx[rx, ry] + if mp[3] > 0: + d = randrange(0, 51) + r = bc[bcp][0] + g = bc[bcp][1] + d + b = bc[bcp][2] + (d // 2) + a = randrange(170, 256) + c = (r, g, b, a) + ex = randrange(2, 5) + ey = randrange(2, 5) + treedraw.ellipse((rx - (ex // 2), ry - (ey // 2), rx + (ey // 2), ry + (ey // 2)), fill=c) + ptsdrawn = ptsdrawn + 1 + + for y in range(0, tree.height): + for x in range(0, tree.width): + tp = treepx[x, y] + diff = randrange(0, 31) + nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) + treepx[x, y] = nc + + tree = tree.filter(ImageFilter.GaussianBlur(radius=0.5)) + + for y in range(0, tree.height): + for x in range(0, tree.width): + tp = treepx[x, y] + diff = randrange(0, 51) + nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) + treepx[x, y] = nc + + return tree + # This generates the layer from the defined mask def genlayer(self, mask, xml): @@ -163,12 +268,6 @@ class mstr_layergen: # If we find an airport, make a note ... if icao != None: if len(icao) >= 1 and self._is_completion == False: - #for i in icao: - # ... but only, if this airport is not already noted - #iccheck = self._tiledb.perform_query("SELECT * FROM airports WHERE icao='" + i +"';") - #if len(iccheck) == 0: - #self._tiledb.insert_icao(i, self._lat_number, self._lng_number, self._latitude, self._longitude) - # mstr_msg("layergen", "Airport/s noted in data file") rw_surface = xml.find_runway_surface() # The image for the layer itself @@ -179,7 +278,7 @@ class mstr_layergen: # We need to differentiate that. if (self._isline == False and self._tag != "building") or (self._is_completion == True): - # Determine where we get the our source material from + # Determine where we get the source material from root_folder = mstr_datafolder + "textures/" for s in mstr_ortho_layers: if s[0] == self._tag and s[1] == self._value: @@ -189,14 +288,33 @@ class mstr_layergen: # Determine which sources to use. src = self.findLayerSource() - - ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png") - - # Load in the sources to work with - brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png") + + # Patch and border sources. There can only be one for each. + brd_src = None ptc_src = [] - for p in ptc: - ptc_src.append(Image.open(p)) + + # Find out if the generated image already exists. + # If not, both border and patch need to be generated first. + gensrc_ptc = mstr_datafolder + "_cache/_pool/ptc_" + self._tag + "_" + self._value + "_" + for s in range(len(src)): + gensrc_ptc = gensrc_ptc + str(src[s]) + if s < len(src) - 1: + gensrc_ptc = gensrc_ptc + "_" + gensrc_ptc = gensrc_ptc + ".png" + + # Find this layer's predetermined contrast + lyr_contrast = self.findLayerContrast() + + # Should this not exist yet, we need to create it + #if os.path.isfile(gensrc_ptc) == False: + rg = mstr_resourcegen(self._tag, self._value, src) + rg.setLayerContrast(int(lyr_contrast)) + lyr_res = rg.gensource() + + # Open the images + ptc_src.append(lyr_res[0]) # Used to be an array, so let's keep it + brd_src = lyr_res[1] + mstr_msg("layergen", "Layer sources selected") # Generate an edge mask from the original @@ -260,83 +378,53 @@ class mstr_layergen: layer.alpha_composite( brd_src ) # Here we need to do some magic to make some features look more natural - if (self._tag == "landuse" and self._value == "meadow") or (self._tag == "natural" and self._value == "grassland") or (self._tag == "natural" and self._value == "heath") or (self._tag == "landuse" and self._value == "cemetery") or (self._tag == "landuse" and self._value == "residential"): - if self._is_completion == False: - amt = randrange(2, 9) - for i in range(1, amt+1): - ptc = randrange(1, 14) - img = Image.open(mstr_datafolder + "textures/tile/completion/p" + str(ptc)+".png") - img = img.rotate(randrange(0, 360), expand=True) - a = img.getchannel("A") - bbox = a.getbbox() - img = img.crop(bbox) - lx = randrange( self._imgsize - img.width ) - ly = randrange( self._imgsize - img.height ) - layer.alpha_composite( img, (lx, ly) ) - if self._is_completion == True: - mp = mask.load() - edn = self.xplane_latlng_folder(self.find_earthnavdata_number()) - idx = 0 - for r in mstr_completion_colors: - if r[0] == edn: - break - else: - idx = idx+1 - for y in range(self._imgsize): - for x in range(self._imgsize): - if mp[x,y][3] > 0: - # Pick a color - a = mp[x,y] - cidx = randrange(len(mstr_completion_colors[idx][1])) - clr = mstr_completion_colors[idx][1][cidx] - layer_pix[x,y] = (clr[0], clr[1], clr[2], a[3]) - amt = randrange(1,51) - for i in range(1, amt+1): - ptc = randrange(1, 14) - img = Image.open(mstr_datafolder + "textures/tile/completion/p" + str(ptc)+".png") - img = img.rotate(randrange(0, 360), expand=True) - a = img.getchannel("A") - bbox = a.getbbox() - img = img.crop(bbox) - imgp = img.load() - for y in range(img.height): - for x in range(img.width): - c = imgp[x,y] - nc = (c[0], c[1], c[2], int(imgp[x,y][3]*0.4)) - imgp[x,y] = nc - lx = randrange( self._imgsize - img.width ) - ly = randrange( self._imgsize - img.height ) - layer.alpha_composite( img, (lx, ly)) - layer = layer.filter(ImageFilter.GaussianBlur(radius=1)) - + if (self._tag == "landuse" and self._value == "meadow") or ( + self._tag == "natural" and self._value == "grassland") or ( + self._tag == "natural" and self._value == "heath") or ( + self._tag == "landuse" and self._value == "cemetery") or ( + self._tag == "landuse" and self._value == "residential"): + amt = randrange(2, 9) + masks = glob.glob(mstr_datafolder + "textures/tile/completion/*.png") + for i in range(1, amt + 1): + pick = randrange(0, len(masks)) + patchmask = Image.open(masks[pick]) + patchpix = patchmask.load() + # Pick from possible tags and values for the patches + patchtags = [ + ["landuse", "meadow"], + ["landuse", "grass"], + ["natural", "heath"], + ["natural", "scrub"] + ] - # Add trees only in some features - if (self._tag == "landuse" and self._value == "cemetery") or (self._tag == "landuse" and self._value == "residential") or (self._tag == "leisure" and self._value == "park"): - trees = Image.new("RGBA", (self._imgsize, self._imgsize)) - amt = 3500 - for i in range(1, amt+1): - p = randrange(1, 16) - tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png") - lx = randrange( self._imgsize - tree.width ) - ly = randrange( self._imgsize - tree.height ) - trees.alpha_composite(tree, (lx, ly)) - - tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) - tree_pix = trees.load() - shadow_pix = tree_shadow.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - tp = tree_pix[x,y] - if tp[3] > 0: - rndshd = randrange(5, 210) - sc = (0,0,0,rndshd) - if x+8 < self._imgsize and y+5 < self._imgsize: - shadow_pix[x+8,y+5] = sc - tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2)) - tree_shadow.alpha_composite(trees) - layer.alpha_composite(tree_shadow) + numbers = list(range(1, 16)) + src = random.sample(numbers, 5) - mstr_msg("layergen", "Layer image completed") + patchpick = randrange(0, len(patchtags)) + ctr = randrange(1, 4) + rg = mstr_resourcegen(patchtags[patchpick][0], patchtags[patchpick][1], src) + rg.setLayerContrast(ctr) + ptch = rg.gensource() + + rg_img = ptch[0] + rg_pix = rg_img.load() + + # The patch to be used in the layer + layerpatch = Image.new("RGBA", (patchmask.width, patchmask.height)) + lp_pix = layerpatch.load() + for y in range(0, patchmask.height): + for x in range(0, patchmask.width): + ptc_msk = patchpix[x,y] + if ptc_msk[3] > 0: + oc = rg_pix[x,y] + nc = ( oc[0], oc[1], oc[2], ptc_msk[3] ) + lp_pix[x,y] = nc + + layerpatch = layerpatch.rotate(randrange(0, 360), expand=True) + + lx = randrange(self._imgsize - layerpatch.width) + ly = randrange(self._imgsize - layerpatch.height) + layer.alpha_composite(layerpatch, (lx, ly)) # And now for the Big Mac. @@ -352,74 +440,24 @@ class mstr_layergen: a=mask_pix[x,y] layer_comp_pix[x, y] = ( rgb[0], rgb[1], rgb[2], a[3]) - # For some things, we will need to add a border and then add this to the layer. - layer_border = None - if self._tag == "landuse": - if self._value == "forest" or self._value == "farmland": - osm_edge = osm_edge.filter(ImageFilter.ModeFilter(size=15)) - osm_edge = osm_edge.filter(ImageFilter.BoxBlur(radius=2)) - layer_border = self.genborder(osm_edge, "landuse", "meadow") - layer_comp.alpha_composite(layer_border) - - - # Here we want to make sure that the generated image fits well with others, so - # let's do that. - mstr_msg("layergen", "Generating adjacent fades") - adjfade = self.generate_adjacent_fades(mask) - - layer_comp.alpha_composite(adjfade) - mstr_msg("layergen", "Adjacent fading completed") - # Add a white-ish border around pitches if self._tag == "leisure" and self._value == "pitch": - epx = osm_edge.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - ep = epx[x,y] - if ep[3] > 0: - d = randrange(10,101) - nw = (200-d,200-d,200-d,255) - layer_comp_pix[x,y] = nw + pitch_edge = osm_edge + pitch_edge = pitch_edge.filter(ImageFilter.GaussianBlur(radius=0.5)) + pitch_mask = pitch_edge.load() - # I need to put this special sub-call here to solve an otherwise unsolvable - # conflict with layer order - if self._tag == "landuse" and self._value == "forest": - # The residential layer MUST exist before we reach the forest part. - fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_landuse-residential_layer.png" - if os.path.isfile(fn): - rsd = Image.open(fn) - rsd_pix = rsd.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - rpix = rsd_pix[x,y] - lpix = layer_comp_pix[x,y] - if rpix[3] > 0 and lpix[3] > 0: - layer_comp_pix[x,y] = (lpix[0], lpix[1], lpix[2], 255-rpix[3]) - - # Store layer - #if self._is_completion == False: - # 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" ) - #if self._is_completion == True: - # layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_tile-completion_layer.png" ) - #layer_final.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.") + # ImageOps.invert does not like RGBA images for inversion. So I need to do it. + for y in range(0, self._imgsize): + for x in range(0, self._imgsize): + pm = pitch_mask[x,y] + if pm[3] > 0: + d = randrange(0, 21) + layer_comp_pix[x,y] = ( 110-pm[0]-d, 110-pm[1]-d, 110-pm[2]-d, pm[3]-(d*2) ) - # Depending on if scenery for XP should be made, AND if normal maps should be made, we would - # need to make them at this exact point - """ - if mstr_xp_genscenery == True: - if mstr_xp_scn_normalmaps == True and self._is_completion == False: - nm = False - for n in mstr_xp_normal_maps: - if n[0] == self._tag and (n[1] == self._value or n[1] == "*"): - nm = True - break - if nm == True: - nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) - nrm.build_normalmap(layer_comp) - """ + # Layer complete + mstr_msg("layergen", "Layer image completed.") # Let's try our hand at pseudo shadows @@ -456,35 +494,6 @@ class mstr_layergen: layer_comp = shadow mstr_msg("layergen", "Shadow layer completed") - - # Create a water mask we need to remove from the DDS later - """ - if (self._tag == "natural" and self._value == "water") or (self._tag == "water" and self._value == "lake") or (self._tag == "water" and self._value == "pond") or (self._tag == "water" and self._value == "river") or (self._tag == "leisure" and self._value == "swimming_pool"): - mstr_msg("layergen", "Generating inland water mask") - water_file = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._lat_number) + "_" + str(self._lng_number) + "_water.png" - inl_mask = None - if os.path.isfile(water_file): - inl_mask = Image.open(water_file) - else: - inl_mask = Image.new("L", (self._imgsize, self._imgsize), (255)) - lyr_pix = layer_comp.load() - inl_pix = inl_mask.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - l = lyr_pix[x,y] - if l[3] > 50: - clr = 255-l[3] - c = (clr) - inl_pix[x,y] = c - inl_mask.save(water_file) - #if l[3] > 65: - # b = 255 - l[3] - # inl_pix[x,y] = (255,0,255,255) - #inl_mask.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_mask.png") - #layer_comp = inl_mask - mstr_msg("layergen", "Inland water mask generated and saved") - """ - # Return the completed image return layer_comp @@ -495,7 +504,7 @@ class mstr_layergen: # If we encounter one of these road-specific tags, we need to proceed differently. - if self._isline == True or self._tag == "building": + if self._isline == True or self._tag != "building": # We will need the mask in question #mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + ".png" ) @@ -546,15 +555,24 @@ class mstr_layergen: d = randrange(41, 61) layer_comp_pix[x, y] = ( d,d,d,a[3] ) if self._tag == "highway" and self._value != "motorway": - dr = randrange(110,121) - dg = randrange(110,121) - db = randrange(115,130) - layer_comp_pix[x, y] = ( dr,dg,db,a[3] ) + d = randrange(0, 36) + dr = 90+d + dg = 90+d + db = 95+d + da = a[3] + layer_comp_pix[x, y] = ( dr,dg,db,da ) if self._tag == "highway" and self._value == "motorway": - dr = randrange(77,89) - dg = randrange(88,96) - db = randrange(90,101) + d = randrange(0, 36) + dr = 57+d + dg = 68+d + db = 70+d layer_comp_pix[x, y] = ( dr,dg,db,a[3] ) + if self._tag == "highway" and (self._value == "footway" or self._value == "track" or self._value == "path"): + dr = randrange(158, 183) + dg = randrange(143, 178) + db = randrange(90, 161) + da = a[3]-20 + layer_comp_pix[x, y] = (dr, dg, db, da) if self._tag == "waterway" and (self._value == "stream" or self._value == "river"): d = randrange(1, 15) # Rock, grass, water @@ -566,29 +584,6 @@ class mstr_layergen: if t < 0: t = 0 if e[3] > 0: layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], 35 ) - - # A bit special here - if self._tag == "building": - # Find a color range for the pixel - d = randrange(1,21) - nr = a[0]+40 - d - ng = a[1]+40 - d - nb = a[2]+40 - d - if nr < 0: nr = 0 - if ng < 0: ng = 0 - if nb < 0: nb = 0 - if nr > 255: nr = 255 - if ng > 255: ng = 255 - if nb > 255: nb = 255 - nc = (nr, ng, nb, 255) - layer_comp_pix[x,y] = (nr,ng,nb,255) - - if self._value == "track" or self._value == "path": - d = randrange(1,20) - r = 164 - d - g = 159 - d - b = 138 - d - layer_comp_pix[x, y] = ( r,g,b,a[3] ) # A bit different for tree rows if self._tag == "natural" and self._value == "tree_row": @@ -599,8 +594,7 @@ class mstr_layergen: a = mask_pix[lx,ly] if a[3] > 0: if lx < self._imgsize and ly < self._imgsize: - p = randrange(1,16) - tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png") + tree = self.generate_tree() trees.alpha_composite(tree, (lx, ly)) if mstr_shadow_enabled == True: tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) @@ -618,221 +612,8 @@ class mstr_layergen: tree_shadow.alpha_composite(trees) layer_comp.alpha_composite(tree_shadow) - # We will do some super magic here to let houses look more realistic - if self._tag == "building": - - details = Image.new("RGBA", (self._imgsize, self._imgsize)) - tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) - trees = Image.new("RGBA", (self._imgsize, self._imgsize)) - roof_details = Image.new("RGBA", (self._imgsize, self._imgsize)) - shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) - - if mstr_shadow_enabled == True: - fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_building-" + self._value + "_layer_shadow.png" - if os.path.isfile(fn): - shadow = Image.open(fn) - - vls = [ "detached", "hotel", "farm", "semidetached_house", "apartments", "civic", "house", "school", "kindergarten", "yes" ] - if self._value in vls: - # Generate a new image - details_pix = details.load() - layer_pix = layer_comp.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - p = layer_pix[x,y] - if p[3] > 0: - shf_x = x+randrange(1, 16) - shf_y = y+randrange(1, 16) - shf_x2 = x-randrange(1, 16) - shf_y2 = y-randrange(1, 16) - if shf_x < self._imgsize and shf_y < self._imgsize and shf_x2 < self._imgsize and shf_y2 < self._imgsize: - st = random.uniform(0.65, 0.85) - ca = 255 * st - aa = int(ca) - d = randrange(1,26) - d2 = randrange(1,26) - details_pix[shf_x, shf_y] = (187-d, 179-d, 176-d, aa) - details_pix[shf_x2, shf_y2] = (187-d2, 179-d2, 176-d2, aa) - - # Image for roof details - roof_det_pix = roof_details.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - mp = mask_pix[x,y] - if mp[3] == 255: - # Determine if we render some pixel - rnd = randrange(1, 3) - if rnd == 2: - # Find a range for the base color of the pixel - d = randrange(21) - # Find a random alpha value - a = randrange(1, 151) - nc = (mstr_building_detail_colors[0][0]-d, mstr_building_detail_colors[0][1]-d, mstr_building_detail_colors[0][2]-d, a) - roof_det_pix[x,y] = nc - - - # Let's see how it works with this method - #details.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_details.png") - #layer_comp.alpha_composite(details) - - # Add some random trees - div = int(self._imgsize/200) - for y in range(0, self._imgsize, div): - for x in range(0, self._imgsize, div): - if x > 0 and x < self._imgsize and y > 0 and y < self._imgsize: - p = mask_pix[x, y] - if p[3] != 0: - # We found something... - # Determine if we put something somewhere - placement = randrange(0, 5) - if placement == 1: - # Do some random shift away from this location - shf_x = randrange(x-11, x+11) - shf_y = randrange(y-11, y+11) - if shf_x < self._imgsize and shf_y < self._imgsize: - # Pick a number of trees to place - numtrees = randrange(1, 16) - for i in range(1, numtrees+1): - # Pick some file - pick = str(randrange(1, 16)) - tree = Image.open(mstr_datafolder + "textures/building/area/p" + pick + ".png") - # Do a correction for the location if needed - if shf_x < 1: shf_x = 1 - if shf_y < 1: shf_y = 1 - if shf_x > self._imgsize - tree.width: shf_x = self._imgsize - tree.width - 1 - if shf_y > self._imgsize - tree.height: shf_y = self._imgsize - tree.height - 1 - trees.alpha_composite(tree, (shf_x, shf_y)) - - - if mstr_shadow_enabled == True: - tree_pix = trees.load() - shadow_pix = tree_shadow.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - tp = tree_pix[x,y] - if tp[3] > 0: - rndshd = randrange(5, 210) - sc = (0,0,0,rndshd) - if x+8 < self._imgsize and y+5 < self._imgsize: - shadow_pix[x+8,y+5] = sc - tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2)) - tree_shadow.alpha_composite(trees) - - # Let's try this one on for size - bld_comp = Image.new("RGBA", (self._imgsize, self._imgsize)) - details = details.filter(ImageFilter.GaussianBlur(radius=1)) - bld_comp.alpha_composite(details) - bld_comp.alpha_composite(tree_shadow) - bld_comp.alpha_composite(trees) - shd_p = shadow.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - c = shd_p[x,y] - if c[3] > 0: - s = (0,0,0,120-(randrange(0,21))) - shd_p[x,y] = s - shadow = shadow.filter(ImageFilter.GaussianBlur(radius=1)) - bld_comp.alpha_composite(shadow) - layer_comp = layer_comp.filter(ImageFilter.GaussianBlur(radius=1.1)) - bld_comp.alpha_composite(layer_comp) - layer_comp = bld_comp - layer_comp.alpha_composite(roof_details) - mstr_msg("layergen", "Layer image generated") - # Building shadow - if mstr_shadow_enabled == True: - - # Some funnies with shadows - if self._tag == "building" and (self._value == "detached" or self._value == "semidetached_house" or self._value == "apartments" or self._value == "civic" or self._value == "house" or self._value == "terrace"): - mask_pix = mask.load() - roofshadow = Image.new("RGBA", (self._imgsize, self._imgsize)) - roofpix = roofshadow.load() - # Generate a pseudo shifted roof shadow - for y in range(self._imgsize): - for x in range(self._imgsize): - mp = mask_pix[x,y] - if mp[3] == 255: - nx = x+8 - ny = y+4 - if nx < self._imgsize and ny < self._imgsize: - roofpix[nx,ny] = (0,0,0,255) - - # Now apply the shift where necessary - roofpix = roofshadow.load() - mask_pix = mask.load() - layer_comp_pix = layer_comp.load() - for y in range(self._imgsize): - for x in range(self._imgsize): - rp = roofpix[x,y] - mp = mask_pix[x,y] - if rp[3] == 255 and mp[3] == 255: - c = layer_comp_pix[x,y] - dim = randrange(30,61) - nr = c[0] - dim - ng = c[1] - dim - nb = c[2] - dim - if nr < 0: nr = 0 - if ng < 0: ng = 0 - if nb < 0: nb = 0 - layer_comp_pix[x,y] = (nr, ng, nb, c[3]) - #layer_comp = layer_comp.filter(ImageFilter.GaussianBlur(radius=1)) - - - # Let's add some details to the roofs - if self._tag == "building": - vls = [ "detached", "hotel", "farm", "semidetached_house", "apartments", "civic", "house", "school", "kindergarten", "yes" ] - if self._value in vls: - roof_additional_detail = Image.new("RGBA", (self._imgsize, self._imgsize)) - rad_pix = roof_additional_detail.load() - for r in range(30001): - lx = randrange(self._imgsize) - ly = randrange(self._imgsize) - mp = mask_pix[lx,ly] - if mp[3] == 255: - # Brighter or darker pixel - bod = randrange(1,3) - c = 0 - if bod == 2: - c = 40 - else: - c = 200 - dt = (c, c, c, 130) - rad_pix[lx,ly] = dt - if lx+1 < self._imgsize: - rad_pix[lx+1, ly] = dt - if lx+1 < self._imgsize and ly+1 < self._imgsize: - rad_pix[lx+1, ly+1] = dt - if ly+1 < self._imgsize: - rad_pix[lx, ly+1] = dt - layer_comp.alpha_composite(roof_additional_detail) - - # Let's put some other details on commercial buildings - if self._tag == "building": - vls = [ "office", "retail", "industrial" ] - if self._value in vls: - - # Find a suitable location to render something - for r in range(15001): - lx = randrange(self._imgsize) - ly = randrange(self._imgsize) - mp = mask_pix[lx,ly] - - # Think of some random shape - if mp[3] == 255: - rw = randrange(3,8) - rh = randrange(3,8) - sh = Image.new("RGBA", (rw, rh), (30,30,30,130)) - shp = sh.load() - for sy in range(rh): - for sx in range(rw): - if sx > 0 and sx < rw and sy > 0 and sy < rh: shp[sx, sy] = (180,180,180,160) - rt = randrange(1, 3) - if rt == 2: - sh = sh.rotate(45, expand=True) - - layer_comp.alpha_composite(sh, (lx, ly)) - # Highways and runways of any kind get some special treatment if (self._tag == "highway" and self._value == "motorway") or (self._tag == "highway" and self._value == "primary") or (self._tag == "highway" and self._value == "secondary") or (self._tag == "highway" and self._value == "tertiary") or (self._tag == "aeroway" and self._value == "runway"): @@ -889,175 +670,165 @@ class mstr_layergen: mstr_msg("layergen", "Layer image finalized and saved.") - # Depending on if scenery for XP should be made, AND if normal maps should be made, we would - # need to make them at this exact point - """ - if mstr_xp_genscenery == True: - if mstr_xp_scn_normalmaps == True and self._is_completion == False: - nm = False - for n in mstr_xp_normal_maps: - if n[0] == self._tag and (n[1] == self._value or n[1] == "*"): - nm = True - break - if nm == True: - nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) - nrm.build_normalmap(layer_comp) - """ - - # Return image return layer_comp + # ------------------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------------------ - # Should we find more than one source, the first one found will take precedence. - # For the others, we will need to generate fading images, so that the final layer - # image works with other tiles - def generate_adjacent_fades(self, mask): - adj_sources = self.find_all_adjacent_sources() - precedence = -1 + # As we now have a specific pool for buildings, we will need to enter a third branch + # to handle all kinds of buildings in the orthos. + if self._tag == "building": + # Access to pixels from OSM mask + mask_pix = mask.load() - # Be prepared for every border - brd_t = Image.open(mstr_datafolder + "textures/multi_source/brd_t.png") - brd_r = Image.open(mstr_datafolder + "textures/multi_source/brd_r.png") - brd_b = Image.open(mstr_datafolder + "textures/multi_source/brd_b.png") - brd_l = Image.open(mstr_datafolder + "textures/multi_source/brd_l.png") + # Separate images for additional details + tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) + trees = Image.new("RGBA", (self._imgsize, self._imgsize)) + shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) + bld_src = Image.new("RGBA", (self._imgsize, self._imgsize)) + bld_main = Image.new("RGBA", (self._imgsize, self._imgsize)) + osm_edge = mask.filter(ImageFilter.FIND_EDGES) - brd_t_pix = brd_t.load() - brd_r_pix = brd_r.load() - brd_b_pix = brd_b.load() - brd_l_pix = brd_l.load() + # Determine which sources to use. + src = self.findLayerSource() - for s in range(0, 4): - if adj_sources[s] != -1: - precedence = adj_sources[s] - break + # Patch and border sources. There can only be one for each. + brd_src = None + ptc_src = [] - # Generate required images - # Basically a shortened version of the main layergen call - adj_image = Image.new("RGBA", (self._imgsize, self._imgsize)) - for s in range(0, 4): - if adj_sources[s] != precedence and adj_sources[s] != -1: - src = adj_sources[s] + # Find this layer's predetermined contrast + lyr_contrast = self.findLayerContrast() - adj_pix = adj_image.load() + rg = mstr_resourcegen(self._tag, self._value, src) + rg.setLayerContrast(int(lyr_contrast)) + bldg_src = rg.gensource() - # Root folder - root_folder = mstr_datafolder + "textures/" + self._tag + "/" + self._value + # Open the images + ptc_src.append(bldg_src[0]) # Used to be an array, so let's keep it + brd_src = bldg_src[1] - # Load in the sources to work with - ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png") - brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png") - ptc_src = [] - for p in ptc: - ptc_src.append(Image.open(p)) + # Begin producing a largely random image + samples = 250 # <- We need this in a moment + for i in range(samples): + imgid = 0 + if len(ptc_src) == 1: imgid = 0 + if len(ptc_src) >= 2: + imgid = randrange(1, len(ptc_src) + 1) - 1 + l = 0 - int(ptc_src[imgid].width / 2) + r = layer.width - int(ptc_src[imgid].width / 2) + t = 0 - int(ptc_src[imgid].height / 2) + b = layer.height - int(ptc_src[imgid].height / 2) + bld_src.alpha_composite(ptc_src[imgid], (randrange(l, r), randrange(t, b))) + mstr_msg("layergen", "Layer image generated") - #mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + ".png" ) - #lyr_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" ) + bld_src_pix = bld_src.load() + bld_main_pix = bld_main.load() + for y in range(0, self._imgsize): + for x in range(0, self._imgsize): + mp = mask_pix[x,y] + bp = bld_src_pix[x,y] + if mp[3] > 0: + nc = ( bp[0], bp[1], bp[2], mp[3] ) + bld_main_pix[x,y] = nc - for i in mstr_mask_blur: - if i[0] == self._tag and i[1] == self._value: - if self._tag != "place" and (self._value != "sea" or self._value != "ocean"): - mask = mask.filter(ImageFilter.BoxBlur(radius=i[2])) - break - mask_pix = mask.load() - - # Begin producing a largely random image - samples = 250 # <- We need this in a moment - for i in range(samples): - imgid = 0 - if len(ptc_src) == 1: imgid = 0 - if len(ptc_src) >= 2: - imgid = randrange(1, len(ptc_src)+1) - 1 - l = 0 - int(ptc_src[imgid].width / 2) - r = adj_image.width - int(ptc_src[imgid].width / 2) - t = 0 - int(ptc_src[imgid].height / 2) - b = adj_image.height - int(ptc_src[imgid].height / 2) - adj_image.alpha_composite( ptc_src[imgid], ( randrange(l, r), randrange(t, b) ) ) + bld_main = bld_main.filter(ImageFilter.GaussianBlur(radius=0.5)) + osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=1.5)) + bld_edge = Image.new("RGBA", (self._imgsize, self._imgsize)) + bld_edge_pix = bld_edge.load() + edge_pix = osm_edge.load() + for y in range(osm_edge.height): + for x in range(osm_edge.width): + ep = edge_pix[x,y] + bp = bld_src_pix[x,y] + if ep[3] == 255 and bp[3] == 255: + nc = (0,0,0,60) + bld_edge_pix[x,y] = nc + bld_main.alpha_composite(bld_edge) - adj_image.alpha_composite( brd_src ) - #lyr_pix = lyr_mask.load() + if mstr_shadow_enabled == True: + fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str( + self._longitude) + "-" + str(self._lng_number) + "_building-" + self._value + "_layer_shadow.png" + if os.path.isfile(fn): + shadow = Image.open(fn) + + # Add some random trees + for t in range(0, 5000): + loc_x = randrange(0, self._imgsize) + loc_y = randrange(0, self._imgsize) + shf_val = 21 + shf_x = randrange(loc_x - shf_val, loc_x + shf_val) + shf_y = randrange(loc_y - shf_val, loc_y + shf_val) + mp = mask_pix[loc_x, loc_y] + if mp[3] == 255: + shf_att = 0 + shf_ok = False + shf_x = randrange(loc_x - shf_val, loc_x + shf_val) + shf_y = randrange(loc_y - shf_val, loc_y + shf_val) + while shf_att < 51: + if shf_x > 0 and shf_x < self._imgsize and shf_y > 0 and shf_y < self._imgsize: + sp = mask_pix[shf_x, shf_y] + if sp[3] == 0: + shf_ok = True + break + else: + shf_att = shf_att + 1 + else: + shf_val = shf_val + 10 + + shf_att = shf_att + 1 + + if shf_ok == True: + tree = self.generate_tree() + trees.alpha_composite(tree, (shf_x, shf_y)) + + # Perform correction for tree rendering + tree_pix = trees.load() + for y in range (0, self._imgsize): + for x in range(0, self._imgsize): + mp = mask_pix[x,y] + tp = tree_pix[x,y] + if mp[3] == 255 and tp[3] == 255: + tree_pix[x,y] = (0,0,0,0) + + + + if mstr_shadow_enabled == True: + tree_pix = trees.load() + shadow_pix = tree_shadow.load() for y in range(self._imgsize): for x in range(self._imgsize): - if mask_pix[x, y][3] > 0: - rgb=adj_pix[x,y] - a=mask_pix[x,y] - adj_pix[x, y] = ( rgb[0], rgb[1], rgb[2], a[3]) + tp = tree_pix[x, y] + if tp[3] > 0: + rndshd = randrange(5, 210) + sc = (0, 0, 0, rndshd) + if x + 8 < self._imgsize and y + 5 < self._imgsize: + shadow_pix[x + 8, y + 5] = sc + tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2)) + tree_shadow.alpha_composite(trees) - # Up until here we mimiced the exact same behavior as layergen. However, now - # we need to adjust the alpha to make this layer fade. - # Then, we save the image - if s == 0: - for y in range(self._imgsize): - for x in range(self._imgsize): - fade_a = brd_t_pix[0, y] - if mask_pix[x, y][3] > 0: - c = adj_pix[x,y] - adj_pix[x,y] = (c[0], c[1], c[2], fade_a[3]) - else: - adj_pix[x,y] = (0,0,0,0) - #adj_image.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_fade_top.png") - - if s == 1: - for y in range(self._imgsize): - for x in range(self._imgsize): - fade_a = brd_r_pix[x, 0] - if mask_pix[x, y][3] > 0: - c = adj_pix[x,y] - adj_pix[x,y] = (c[0], c[1], c[2], fade_a[3]) - else: - adj_pix[x,y] = (0,0,0,0) - #adj_image.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_fade_right.png") - - if s == 2: - for y in range(self._imgsize): - for x in range(self._imgsize): - fade_a = brd_b_pix[0, y] - if mask_pix[x, y][3] > 0: - c = adj_pix[x,y] - adj_pix[x,y] = (c[0], c[1], c[2], fade_a[3]) - else: - adj_pix[x,y] = (0,0,0,0) - #adj_image.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_fade_bottom.png") + # Let's try this one on for size + bld_comp = Image.new("RGBA", (self._imgsize, self._imgsize)) + bld_comp.alpha_composite(tree_shadow) + bld_comp.alpha_composite(trees) + shd_p = shadow.load() + for y in range(self._imgsize): + for x in range(self._imgsize): + c = shd_p[x, y] + if c[3] > 0: + s = (0, 0, 0, 120 - (randrange(0, 21))) + shd_p[x, y] = s + shadow = shadow.filter(ImageFilter.GaussianBlur(radius=1)) + bld_comp.alpha_composite(shadow) + #bld_comp = bld_comp.filter(ImageFilter.GaussianBlur(radius=1.1)) + bld_comp.alpha_composite(bld_main) - if s == 3: - for y in range(self._imgsize): - for x in range(self._imgsize): - fade_a = brd_l_pix[x, 0] - if mask_pix[x, y][3] > 0: - c = adj_pix[x,y] - adj_pix[x,y] = (c[0], c[1], c[2], fade_a[3]) - else: - adj_pix[x,y] = (0,0,0,0) - #adj_image.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_fade_left.png") - - # Return the image - return adj_image + return bld_comp - def find_all_adjacent_sources(self): - # Sources for this tag and value - top, right, bottom, left - sources = [-1,-1,-1,-1] - - # Perform query for each neighboring tile - src_top = self._tileinfo.get_adjacency_for_tag_and_value(self._lat_number+1, self._lng_number, self._tag, self._value) - src_rgt = self._tileinfo.get_adjacency_for_tag_and_value(self._lat_number, self._lng_number+1, self._tag, self._value) - src_btm = self._tileinfo.get_adjacency_for_tag_and_value(self._lat_number-1, self._lng_number, self._tag, self._value) - src_lft = self._tileinfo.get_adjacency_for_tag_and_value(self._lat_number, self._lng_number-1, self._tag, self._value) - - if len(src_top) == 2: - if "b" in src_top[1]: sources[0] = src_top[0] - if len(src_rgt) == 2: - if "l" in src_rgt[1]: sources[1] = src_rgt[0] - if len(src_btm) == 2: - if "t" in src_btm[1]: sources[2] = src_btm[0] - if len(src_lft) == 2: - if "r" in src_lft[1]: sources[3] = src_lft[0] - - # Report our findings - return sources - # Find the next "by-ten" numbers for the current latitude and longitude def find_earthnavdata_number(self): earthnavdata = [] diff --git a/log.py b/log.py index 2a26dd5..34eb6d2 100644 --- a/log.py +++ b/log.py @@ -11,9 +11,15 @@ # ------------------------------------------------------------------- import datetime +from colorama import init as colorama_init +from colorama import Fore +from colorama import Style from defines import * def mstr_msg(fnc, msg): if mstr_show_log == True: + colorama_init() now = datetime.datetime.now() - print(now.strftime(" %H:%M:%S" + " | ["+fnc+"] | " + msg)) \ No newline at end of file + + print(f' {Fore.GREEN}'+now.strftime("%H:%M:%S")+f'{Style.RESET_ALL} | {Fore.YELLOW}[' + fnc + f']{Style.RESET_ALL} | {Fore.CYAN}'+ msg + f'{Style.RESET_ALL}') + #print(f"{Fore.GREEN}" + now.strftime(" %H:%M:%S" + " | ["+fnc+"] | " + msg)) \ No newline at end of file diff --git a/maskgen.py b/maskgen.py index 519e811..b8080c2 100644 --- a/maskgen.py +++ b/maskgen.py @@ -252,6 +252,14 @@ class mstr_maskgen: if sp[3] != 0: bld_shadow_pix[x,y] = (0,0,0,120) + + # Mark buildings in red + for y in range(mstr_photores): + for x in range(mstr_photores): + mp = mask_pix[x,y] + if mp[3] != 0: + bld_shadow_pix[x,y] = (255,0,0,255) + # Store if os.path.isfile(fn) == True: lyr = Image.open(fn) diff --git a/og.py b/og.py index 8b0f4e4..9c646ff 100644 --- a/og.py +++ b/og.py @@ -35,13 +35,9 @@ prep = False if len(sys.argv) == 4: cli = True - if sys.argv[3] == "true": prep = True - -#if len(sys.argv) == 4: -# pbf = True # Only if we find enough arguments, proceed. -if cli == True: +if cli: lat = int(sys.argv[1]) lng = int(sys.argv[2]) @@ -50,28 +46,18 @@ if cli == True: # Create the class and init values og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd(), prep) - if prep == True: + # Prepare a tile + if sys.argv[3] == "prepare": og._prepareTile() - if prep == False: - if sys.argv[3] != "xpscenery": - og._generateOrthos_mt(int(sys.argv[3])) - - # Build the terrain mesh and assign ground textures - if sys.argv[3] == "xpscenery": - og.generate_xp_scenery() + # Generate orthos + if sys.argv[3] != "prepare" and sys.argv[3] != "xpscenery": + og._generateOrthos_mt(int(sys.argv[3])) + # Build the terrain mesh and assign ground textures + if sys.argv[3] == "xpscenery": + og.generate_xp_scenery() -# Only if we find enough arguments, proceed. -if pbf == True: - lat = int(sys.argv[1]) - lng = int(sys.argv[2]) - pbf = sys.argv[3] - - if pbf == "pbf": - # Create the class and init values - og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd()) - og._generateData() if cli == False and pbf == False: diff --git a/orthographic.py b/orthographic.py index c16e705..459bb68 100644 --- a/orthographic.py +++ b/orthographic.py @@ -170,9 +170,13 @@ class mstr_orthographic: mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng)) maxlatlng = [ mlat, mlng ] + # For completion layers + numbers = list(range(1, 16)) + res = random.sample(numbers, 5) + procs = [] for p in range(1, amtsmt+1): - proc = Process(target=self._buildOrtho, args=[1, p, amtsmt]) + proc = Process(target=self._buildOrtho, args=[1, p, amtsmt, res]) procs.append(proc) proc.start() mstr_msg("orthographic", "Ortho threads started") @@ -181,7 +185,7 @@ class mstr_orthographic: # Starts a threading loop to build orthos, with the defined starting point in # the lat-lng grid. You will also need to provide the horizontal stepping so # that the thread keeps running. - def _buildOrtho(self, v, h, step): + def _buildOrtho(self, v, h, step, cpl): # Starting point grid_lat = v @@ -256,7 +260,6 @@ class mstr_orthographic: lg = mstr_layergen(layer[0], layer[1], self._lat, grid_lat, self._long, grid_lng, layer[2]) lg.set_max_latlng_tile(maxlatlng) lg.set_latlng_folder(self._latlngfld) - #lg.open_db() lg.open_tile_info() lyr = lg.genlayer(mask, osmxml) photolayers.append(lyr) @@ -274,7 +277,8 @@ class mstr_orthographic: # Snap a photo with our satellite :) mstr_msg("orthographic", "Generating ortho photo") pg = mstr_photogen(self._lat, self._long, grid_lat, grid_lng, maxlatlng[0], maxlatlng[1]) - pg.genphoto(photolayers, waterlayers) + pg.setLayerNames(layers) + pg.genphoto(photolayers, waterlayers, cpl) mstr_msg("orthographic", " -- Ortho photo generated -- ") print("") print("") @@ -375,12 +379,12 @@ class mstr_orthographic: bb_lat = self._lat bb_lng = self._long - # We will now prepare the graphic tile generation. We do this by only generating # the masks and determine which sources to use in the actual images. # Previously, I downloaded all XML files in one go - but to ease the # stress on OSM servers and my server, we will do acquire the data # only for the current processed part of the tile. + """ for lat_grid in range(1, maxlatlng[0]+1): for lng_grid in range(1, maxlatlng[1]+1): # Adjust bounding box @@ -403,7 +407,7 @@ class mstr_orthographic: mask = mg._build_mask(osmxml, is_prep=True) # We need an object here tp = mstr_tileprep(self._lat, self._long, lat_grid, lng_grid, layer[0], layer[1], mask, False) - tp._prepareTile() + tp._determineEdges() curlyr = curlyr+1 @@ -428,6 +432,24 @@ class mstr_orthographic: if cur_tile_y > top_lat: top_lat = cur_tile_y + exit(1) + """ + + # We now need to "raytrace" the resources for correct placement + mstr_msg("orthographic", "Performing resource plamement tracing for tile") + for lat_grid in range(1, maxlatlng[0]+1): + for lng_grid in range(1, maxlatlng[1]+1): + mstr_msg("orthographic", "Placement tracing for " + str(lat_grid) + ":" + str(lng_grid)) + df = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(lat_grid) + "_" + str(lng_grid) + fnlines = [] + with open(df) as textfile: + fnlines = textfile.readlines() + + for l in range(0, len(fnlines)): + lyr = fnlines[l].split(" ") + tp = mstr_tileprep(self._lat, self._long, lat_grid, lng_grid, lyr[0], lyr[1], None, False) + tp._setLatLngFold(self._latlngfld) + tp._placeTileSources(lat_grid, lng_grid) # Generates X-Plane 11/12 scenery with diff --git a/photogen.py b/photogen.py index 56a4fba..2a8cb5e 100644 --- a/photogen.py +++ b/photogen.py @@ -1,12 +1,14 @@ import os -from PIL import Image, ImageFilter, ImageEnhance +from PIL import Image, ImageFilter, ImageEnhance, ImageFile from defines import * from layergen import * from log import * from functions import * from xp_normalmap import * +ImageFile.LOAD_TRUNCATED_IMAGES = True + # ------------------------------------------------------------------- # ORTHOGRAPHIC # Your personal aerial satellite. Always on. At any altitude.* @@ -36,17 +38,30 @@ class mstr_photogen: self._latlngfld = self.latlng_folder([lat,lng]) mstr_msg("photogen", "Photogen initialized") + + # Defines the order of layer names that were processed + # Called by orthographic prior to starting the photo gen process + def setLayerNames(self, names): + self._lyrnames = names + # This puts it all together. Bonus: AND saves it. - def genphoto(self, layers, waterlayers): + def genphoto(self, layers, waterlayers, cpl): # Template for the file name which is always the same #root_filename = mstr_datafolder + "/_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + # Correct layers + #mstr_msg("photogen", "Correcting layer order issues") + #layers = self.correctLayerIssues(layers) + # First, we walk through all layers and blend them on top of each other, in order mstr_msg("photogen", "Merging layers") + lyr=0 for l in layers: - self._tile.alpha_composite(l) + if self._lyrnames[lyr] != "building": + self._tile.alpha_composite(l) + lyr=lyr+1 # When we have run through this loop, we will end up with a sandwiched @@ -65,65 +80,80 @@ class mstr_photogen: if emptyspace == True: mstr_msg("photogen", "Patching empty space") - mask = self.buildCompletionMask() - - # Load the mask - mask_px = mask.load() cmpl = Image.new("RGBA", (self._imgsize, self._imgsize)) - cmp_px = cmpl.load() edn = self.find_earthnavdata_number() edns = self.latlng_folder(edn) - idx = 0 - for r in mstr_completion_colors: - if r[0] == edns: - break - else: - idx = idx+1 - - for y in range(self._imgsize): - for x in range(self._imgsize): - p = mask_px[x,y] - if p[3] > 0: - cidx = randrange(0, len(mstr_completion_colors[idx][1])-1) - clr = mstr_completion_colors[idx][1][cidx] - cmp_px[x,y] = (clr[0], clr[1], clr[2], 255) - # Some features - patches = glob.glob(mstr_datafolder + "textures/tile/completion/*.png") + cplstr = "" + for c in range(0, len(cpl)): + cplstr = cplstr + str(cpl[c]) + if c < len(cpl)-1: + cplstr = cplstr + "_" - # Pick an amount of features to add - patch_amt = randrange(1, 7) + # Should this not exist yet, we need to create it + rg = mstr_resourcegen("landuse", "meadow", cpl) + rg.setLayerContrast(randrange(1,4)) + ptcimg = rg.gensource() - # Add those somewhere - for p in range(1, patch_amt+1): - # Load some patch - ptc = Image.open(mstr_datafolder + "textures/tile/completion/p" + str(randrange(1, len(patches)+1)) + ".png") - # Rotate it - ptc = ptc.rotate(randrange(0, 360), expand=True) + ptc_src = [ptcimg[0]] + samples = 250 # <- We need this in a moment + for i in range(samples): + imgid = 0 + if len(ptc_src) == 1: imgid = 0 + l = 0 - int(ptc_src[imgid].width / 2) + r = cmpl.width - int(ptc_src[imgid].width / 2) + t = 0 - int(ptc_src[imgid].height / 2) + b = cmpl.height - int(ptc_src[imgid].height / 2) + cmpl.alpha_composite(ptc_src[imgid], (randrange(l, r), randrange(t, b))) - # Make sure ortho generation does not crash - if ptc.width >= mstr_photores: - ptc = ptc.resize((1536, 1536), Image.Resampling.BILINEAR) + brd_img = ptcimg[1] + cmpl.alpha_composite(brd_img) - # Adjust alpha on this image - ptc_p = ptc.load() - for y in range(ptc.height): - for x in range(ptc.width): - c = ptc_p[x,y] - if c[3] > 0: - na = c[3] - 160 - if na < 0: na = 0 - nc = (c[0], c[1], c[2], na) - ptc_p[x,y] = nc + # Patches to add from other sources. If they don't exist, we also need to make them + masks = glob.glob(mstr_datafolder + "textures/tile/completion/*.png") + amt = randrange(5, 16) + for i in range(1, amt + 1): + pick = randrange(0, len(masks)) + patchmask = Image.open(masks[pick]) + patchpix = patchmask.load() + # Pick from possible tags and values for the patches + patchtags = [ + ["landuse", "meadow"], + ["landuse", "grass"], + ["natural", "heath"], + ["natural", "scrub"] + ] - # Find a location INSIDE the image! - px = randrange(1, randrange(self._imgsize - ptc.width - 1)) - py = randrange(1, randrange(self._imgsize - ptc.height - 1)) + numbers = list(range(1, 16)) + src = random.sample(numbers, 5) - # Add it to the completion image - cmpl.alpha_composite(ptc, dest=(px,py)) + patchpick = randrange(0, len(patchtags)) + + rg = mstr_resourcegen(patchtags[patchpick][0], patchtags[patchpick][1], src) + rg.setLayerContrast(randrange(1, 4)) + ptch = rg.gensource() + + rg_img = ptch[0] + rg_pix = rg_img.load() + + # The patch to be used in the layer + layerpatch = Image.new("RGBA", (patchmask.width, patchmask.height)) + lp_pix = layerpatch.load() + for y in range(0, patchmask.height): + for x in range(0, patchmask.width): + ptc_msk = patchpix[x, y] + if ptc_msk[3] > 0: + oc = rg_pix[x, y] + nc = (oc[0], oc[1], oc[2], ptc_msk[3]) + lp_pix[x, y] = nc + + layerpatch = layerpatch.rotate(randrange(0, 360), expand=True) + + lx = randrange(self._imgsize - layerpatch.width) + ly = randrange(self._imgsize - layerpatch.height) + cmpl.alpha_composite(layerpatch, (lx, ly)) # Merge the images cmpl.alpha_composite(self._tile) @@ -152,11 +182,22 @@ class mstr_photogen: corrpix[x,y] = nc if c[3] == 0: corrpix[x,y] = (0,0,0,0) + + # One more thing... + mstr_msg("photogen", "Adding features to layers") + self.addTreesToFeatures(layers) + + # Throw missing buildings on top + lyr = 0 + for l in layers: + if self._lyrnames[lyr] == "building": + self._tile.alpha_composite(l) + lyr = lyr + 1 # We are now in posession of the final image. # Contrast - self._tile = ImageEnhance.Contrast(self._tile).enhance(1) + #self._tile = ImageEnhance.Contrast(self._tile).enhance(0.1) # This we can save accordingly. self._tile.save(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png") @@ -191,6 +232,172 @@ class mstr_photogen: nrmimg.save(nrmfln) + # Generates some random tree. + # We will now move away from using pre-made trees... + # they didn't look so great + def generate_tree(self): + sx = randrange(18, 31) + sy = randrange(18, 31) + + treepoly = Image.new("RGBA", (sx, sy)) + draw = ImageDraw.Draw(treepoly) + + draw.ellipse((4, 4, sx - 4, sy - 4), fill="black") + + tree = Image.new("RGBA", (sx, sy)) + treepx = tree.load() + maskpx = treepoly.load() + + # How many tree points do we want? + treepts = 75 + # How many of those have been drawn? + ptsdrawn = 0 + + bc = [ + (36, 50, 52), + (30, 41, 39), + (32, 45, 37), + (32, 39, 49), + (33, 34, 40), + (44, 50, 53), + (40, 46, 48), + (14, 31, 38), + (17, 41, 33), + (39, 56, 35), + (51, 51, 42), + (12, 27, 31), + (45, 59, 48), + (37, 54, 29), + (59, 50, 34), + (59, 59, 35), + (59, 51, 35), + (70, 72, 45), + (48, 59, 44), + (29, 47, 23), + (47, 61, 43), + (29, 68, 15), + (53, 77, 63), + (20, 68, 40) + ] + + bcp = randrange(0, len(bc)) + + treedraw = ImageDraw.Draw(tree) + while ptsdrawn < treepts + 1: + rx = randrange(0, sx) + ry = randrange(0, sy) + mp = maskpx[rx, ry] + if mp[3] > 0: + d = randrange(0, 51) + r = bc[bcp][0] + g = bc[bcp][1] + d + b = bc[bcp][2] + (d // 2) + a = randrange(170, 256) + c = (r, g, b, a) + ex = randrange(2, 5) + ey = randrange(2, 5) + treedraw.ellipse((rx - (ex // 2), ry - (ey // 2), rx + (ey // 2), ry + (ey // 2)), fill=c) + ptsdrawn = ptsdrawn + 1 + + for y in range(0, tree.height): + for x in range(0, tree.width): + tp = treepx[x, y] + diff = randrange(0, 31) + nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) + treepx[x, y] = nc + + tree = tree.filter(ImageFilter.GaussianBlur(radius=0.5)) + + for y in range(0, tree.height): + for x in range(0, tree.width): + tp = treepx[x, y] + diff = randrange(0, 51) + nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) + treepx[x, y] = nc + + return tree + + + # This used to be in layergen and solves the problem of roads being rendered above trees. + # It is the only solution that worked, after some research. + def addTreesToFeatures(self, layers): + + # Walk through list of layers to decide where to add the trees + curlyr = 0 + for lyr in self._lyrnames: + # Add trees only in some features + if (lyr[0] == "landuse" and lyr[1] == "cemetery") or ( + lyr[0] == "landuse" and lyr[1] == "residential") or ( + lyr[0] == "leisure" and lyr[1] == "park"): + trees = Image.new("RGBA", (self._imgsize, self._imgsize)) + amt = 4000 + lyrmask = layers[curlyr].load() # We can use the layer image as alpha mask + for i in range(1, amt + 1): + lx = randrange(0, self._imgsize) + ly = randrange(0, self._imgsize) + lp = lyrmask[lx,ly] + if lp[3] > 0: + tree = self.generate_tree() + trees.alpha_composite(tree, (lx, ly)) + + tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize)) + tree_pix = trees.load() + shadow_pix = tree_shadow.load() + for y in range(self._imgsize): + for x in range(self._imgsize): + tp = tree_pix[x, y] + if tp[3] > 0: + rndshd = randrange(5, 210) + sc = (0, 0, 0, rndshd) + if x + 8 < self._imgsize and y + 5 < self._imgsize: + shadow_pix[x + 8, y + 5] = sc + tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2)) + tree_shadow.alpha_composite(trees) + self._tile.alpha_composite(tree_shadow) + curlyr = curlyr + 1 + + # Reset + curlyr = 0 + bldg = [] + tilepix = self._tile.load() + for lyr in self._lyrnames: + if lyr[0] == "building": + bldg.append(curlyr) + curlyr = curlyr + 1 + + for b in range(0, len(bldg)): + bldg_lyr = layers[bldg[b]].load() + shdw = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + self._lyrnames[bldg[b]][0] + "-" + self._lyrnames[bldg[b]][1] + "_layer_shadow.png") + shdw_pix = shdw.load() + for y in range(0, self._imgsize): + for x in range(0, self._imgsize): + bpix = bldg_lyr[x,y] + spix = shdw_pix[x,y] + if bpix[3] > 0 and spix[0] == 255: + tilepix[x,y] = ( bpix[0], bpix[1], bpix[2], bpix[3] ) + + + + # Correct some layer issues + def correctLayerIssues(self, layers): + + # First the residential/forest dilemma + residential = 0 + forest = 0 + + curlyr = 0 + for lyr in self._lyrnames: + if lyr[0] == "landuse" and lyr[1] == "residential": + residential=curlyr + if lyr[0] == "landuse" and lyr[1] == "forest": + forest = curlyr + curlyr = curlyr+1 + + layers[forest].alpha_composite(layers[residential]) + + return layers + + # This checks the final image for empty patches. Should one be diff --git a/tileinfo.py b/tileinfo.py index 18a13e8..de242e7 100644 --- a/tileinfo.py +++ b/tileinfo.py @@ -29,7 +29,6 @@ class mstr_tileinfo: #self._adjfile = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/adjinfo" self._cplfile = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/cplinfo" self.createDataFile() - self.createCompletionFile() def createDataFile(self): @@ -37,15 +36,8 @@ class mstr_tileinfo: open(self._adjfile, 'a').close() - def createCompletionFile(self): - if os.path.isfile(self._cplfile) == False: - open(self._cplfile, 'a').close() - - - def add_adjacency_data(self, tv, th, tag, value, source, adj): + def add_adjacency_data(self, tag, value, source, adj): line = "" - line = line + str(tv) + " " - line = line + str(th) + " " line = line + tag + " " line = line + value + " " line = line + str(source) + " " @@ -55,19 +47,6 @@ class mstr_tileinfo: textfile.write(line) - def add_completion_data(self, tv, th, tag, value, source, adj): - line = "" - line = line + str(tv) + " " - line = line + str(th) + " " - line = line + tag + " " - line = line + value + " " - line = line + str(source) + " " - line = line + str(adj) + "\n" - - with open(self._cplfile, 'a') as textfile: - textfile.write(line) - - def get_adjacency_for_tag_and_value(self, tv, th, tag, value): adj = [] fnlines = [] @@ -78,12 +57,11 @@ class mstr_tileinfo: for ln in fnlines: l = ln.split(" ") - if int(l[0]) == tv and int(l[1]) == th: - if l[2] == tag and l[3] == value: - l[5] = l[5].replace("\n", "") - adj.append(int(l[4])) - adj.append(l[5]) - break + if l[0] == tag and l[1] == value: + l[3] = l[3].replace("\n", "") + adj.append(l[2]) + adj.append(l[3]) + break return adj @@ -243,3 +221,4 @@ class mstr_tileinfo: if abs(numbers[1]) >= 100 : fstr = fstr + str(numbers[1]) return fstr + diff --git a/tileprep.py b/tileprep.py index f5197a2..4735827 100644 --- a/tileprep.py +++ b/tileprep.py @@ -14,6 +14,7 @@ # ------------------------------------------------------------------- import glob +from os import MFD_ALLOW_SEALING from random import randrange from PIL import Image from osmxml import * @@ -34,7 +35,7 @@ class mstr_tileprep: self._tag = tag self._value = value self._edges = "" # To be filled by _edgeDetect call - self._source = -1 + self._source = "" self._mask = mask latlngfld = xplane_latlng_folder([lat, lng]) self._tileinfo = mstr_tileinfo(lat, lng, v, h, latlngfld) @@ -53,8 +54,8 @@ class mstr_tileprep: return srcfld - # Prepare the tile accordingly - def _prepareTile(self): + # Use the mask to determine the edges + def _determineEdges(self): # Load the mask pixels mp = self._mask.load() imgsize = self._mask.width @@ -69,29 +70,29 @@ class mstr_tileprep: al=False # Top scan - for i in range(0, imgsize-1): + for i in range(0, imgsize): p = mp[i,0] if p[3] > 0: at=True break # Right scan - for i in range(0, imgsize-1): + for i in range(0, imgsize): p = mp[imgsize-1,i] if p[3] > 0: ar=True break # Bottom scan - for i in range(0, imgsize-1): + for i in range(0, imgsize): p = mp[i,imgsize-1] if p[3] > 0: ab=True break # Left scan - for i in range(0, imgsize-1): - p = mp[1,i] + for i in range(0, imgsize): + p = mp[0,i] if p[3] > 0: al=True break @@ -103,48 +104,187 @@ class mstr_tileprep: if ab==True: adjstr = adjstr + "b" if al==True: adjstr = adjstr + "l" - # Now find out of there is a source from any adjacent tile - adjtiles = findAdjacentTilesTo(self._tile_v, self._tile_h) - sat = [] - sar = [] - sab = [] - sal = [] - if self._is_completion == False: - if at == True: sat = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[0][0], adjtiles[0][1], self._tag, self._value) # Top - if ar == True: sar = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[1][0], adjtiles[1][1], self._tag, self._value) # Right - if ab == True: sab = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[2][0], adjtiles[2][1], self._tag, self._value) # Bottom - if al == True: sal = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[3][0], adjtiles[3][1], self._tag, self._value) # Left - if self._is_completion == True: - if at == True: sat = self._tileinfo.get_adjacency_for_completion(adjtiles[0][0], adjtiles[0][1]) # Top - if ar == True: sar = self._tileinfo.get_adjacency_for_completion(adjtiles[1][0], adjtiles[1][1]) # Right - if ab == True: sab = self._tileinfo.get_adjacency_for_completion(adjtiles[2][0], adjtiles[2][1]) # Bottom - if al == True: sal = self._tileinfo.get_adjacency_for_completion(adjtiles[3][0], adjtiles[3][1]) # Left - - if self._source == -1 and len(sat) == 2: self._source = sat[0] - if self._source == -1 and len(sar) == 2: self._source = sar[0] - if self._source == -1 and len(sab) == 2: self._source = sab[0] - if self._source == -1 and len(sal) == 2: self._source = sal[0] - - # If there was nothing in the info still, we need to select some source - if self._source == -1: - srcfld = self._findCorrectTextureFolder() - tx = mstr_datafolder + "textures/" + srcfld[0] + "/" + srcfld[1] + "/brd/b*.png" - lst = glob.glob(tx) - if len(lst) == 1: self._source = 1 - if len(lst) >= 2: self._source = randrange(1, len(lst)+1) - - - # Store into DB - but only if there is something to store + # We will now write this down first, without a source being selected if adjstr != "": - if self._is_completion == False: - r = self._tileinfo.get_adjacency_for_tag_and_value(self._tile_v, self._tile_h, self._tag, self._value) - if len(r) == 0: - self._tileinfo.add_adjacency_data(self._tile_v, self._tile_h, self._tag, self._value, self._source, adjstr) - mstr_msg("tileprep", "Adjacency info stored in database") + self._tileinfo.add_adjacency_data(self._tag, self._value, "0", adjstr) - if self._is_completion == True: - r = self._tileinfo.get_adjacency_for_completion(self._tile_v, self._tile_h) - if len(r) == 0: - self._tileinfo.add_completion_data(self._tile_v, self._tile_h, self._tag, self._value, self._source, adjstr) - mstr_msg("tileprep", "Adjacency info for completion stored in database") + # Set the latlng folder + def _setLatLngFold(self, latlngfld): + self._latlngfld = latlngfld + + + # Find out if there is already something in place for this tag of this tile + def _getResourceInfo(self, tv, th): + # This either remains 0 or a string different to "0" in the end + src = "0" + df = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(tv) + "_" + str(th) + fnlines = [] + if os.path.isfile(df) == True: # It is possible that the requested file does not yet exist + with open(df) as textfile: + fnlines = textfile.readlines() + + for ln in fnlines: + l = ln.split(" ") + if l[0] == self._tag and l[1] == self._value: + l[3] = l[3].replace("\n", "") + src = l[2] + + return src + + + # Find the edge touch info + def _getResourceTouch(self, tv, th): + touch = "" + df = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(tv) + "_" + str(th) + fnlines = [] + if os.path.isfile(df) == True: # It is possible that the requested file does not yet exist + with open(df) as textfile: + fnlines = textfile.readlines() + + for ln in fnlines: + l = ln.split(" ") + if l[0] == self._tag and l[1] == self._value: + l[3] = l[3].replace("\n", "") + touch = l[3] + + return touch + + + # Select a combination of resources + def _selectResources(self): + numbers = list(range(1, 16)) + res = random.sample(numbers, 5) + + # Construct a string of the array + resstr = "" + for r in range(len(res)): + resstr = resstr + str(res[r]) + if r < len(res)-1: + resstr = resstr + "," + + return resstr + + + # Store the required resource information into the appropriate tile + def _storeResourceInfo(self, tile_v, tile_h, res): + df = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/" + str(tile_v) + "_" + str(tile_h) + fnlines = [] + contrast = 0 + if os.path.isfile(df) == True: # It is possible that the requested file does not yet exist + with open(df) as textfile: + fnlines = textfile.readlines() + curline = 0 + for ln in fnlines: + l = ln.split(" ") + if l[0] == self._tag and l[1] == self._value: + l[2] = res + contrast = int(l[4]) + + # Find contrast values for some tags + if ( + (l[0] == "landuse" and l[1] == "forest") or + (l[0] == "landuse" and l[1] == "meadow") or + (l[0] == "landuse" and l[1] == "grass") or + (l[0] == "leisure" and l[1] == "nature_reserve") or + (l[0] == "natural" and l[1] == "grassland") or + (l[0] == "landuse" and l[1] == "greenfield") or + (l[0] == "natural" and l[1] == "heath") or + (l[0] == "natural" and l[1] == "wetland") or + (l[0] == "leisure" and l[1] == "park") or + (l[0] == "building") + ): + if int(l[4]) == 0: contrast = randrange(1, 4) + + l[3] = l[3].replace("\n", "") + fnlines[curline] = l[0] + " " + l[1] + " " + l[2] + " " + l[3] + " " + str(contrast) + "\n" + + curline = curline+1 + + lines = "" + for l in range(len(fnlines)): + lines = lines + fnlines[l] + + with open(df, 'w') as textfile: + textfile.write(lines) + + + # Walk through the now existing data files and make sure we always pick the correct + # sources for every tile, thus evading previous edge detection errors + def _placeTileSources(self, mlat, mlng): + + # The first tile gets to choose something for itself + if self._tile_v == 1 and self._tile_h == 1: + resstr = self._selectResources() + self._storeResourceInfo(1, 1, resstr) + + # Start "raytracing" + + # Initial reset + tv = self._tile_v + th = self._tile_h + # Start marching north + while tv < mlat+1: + restch = self._getResourceTouch(tv, th) + if "t" in restch: + resstr = self._getResourceInfo(tv, th) + if resstr == "0": + resstr = self._selectResources() + self._storeResourceInfo(tv, th, resstr) + resd = self._getResourceInfo(tv+1, th) + if resd=="0": + self._storeResourceInfo(tv + 1, th, resstr) + else: + break + tv = tv + 1 + + # Start marching east + tv = self._tile_v + th = self._tile_h + while th < mlng + 1: + restch = self._getResourceTouch(tv, th) + if "r" in restch: + resstr = self._getResourceInfo(tv, th) + if resstr == "0": + resstr = self._selectResources() + self._storeResourceInfo(tv, th, resstr) + resd = self._getResourceInfo(tv, th+1) + if resd == "0": + self._storeResourceInfo(tv, th + 1, resstr) + else: + break + th = th + 1 + + # Start marching south + tv = self._tile_v + th = self._tile_h + while tv > 0: + restch = self._getResourceTouch(tv, th) + if "b" in restch: + resstr = self._getResourceInfo(tv, th) + if resstr == "0": + resstr = self._selectResources() + self._storeResourceInfo(tv, th, resstr) + resd = self._getResourceInfo(tv - 1, th) + if resd == "0": + self._storeResourceInfo(tv - 1, th, resstr) + else: + break + tv = tv - 1 + + # Start marching west + tv = self._tile_v + th = self._tile_h + while th > 0: + restch = self._getResourceTouch(tv, th) + if "l" in restch: + resstr = self._getResourceInfo(tv, th) + if resstr == "0": + resstr = self._selectResources() + self._storeResourceInfo(tv, th, resstr) + resd = self._getResourceInfo(tv, th-1) + if resd == "0": + self._storeResourceInfo(tv, th - 1, resstr) + else: + break + th = th - 1 \ No newline at end of file diff --git a/xp_normalmap.py b/xp_normalmap.py index 6a32660..9f39266 100644 --- a/xp_normalmap.py +++ b/xp_normalmap.py @@ -1,3 +1,4 @@ +from random import randrange # ------------------------------------------------------------------- # ORTHOGRAPHIC @@ -73,7 +74,7 @@ class mstr_xp_normalmap: # Resize original image = image.resize((int(mstr_photores/4), int(mstr_photores/4)), Image.Resampling.BILINEAR) - nmp = Image.new("RGBA", (image.width, image.height), (128,128,1,1)) + nmp = Image.new("RGBA", (image.width, image.height), (128,128,0,0)) if water: nmp = Image.new("RGBA", (image.width, image.height), (128, 128, 255, 0)) @@ -83,48 +84,62 @@ class mstr_xp_normalmap: # Let's try some shenanigans w = image.width h = image.height - for y in range(h): - for x in range(w): - p = org[x,y] - if p[3] > 0: # Only do something if there is something to do in layer - # Neighboring pixels - px_t = org[ self.clamp(x, w), self.clamp(y+1, h) ] - px_tr = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] - px_r = org[ self.clamp(x+1, w), self.clamp(y, h) ] - px_br = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] - px_b = org[ self.clamp(x, w), self.clamp(y-1, h) ] - px_bl = org[ self.clamp(x-1, w), self.clamp(y-1, h) ] - px_l = org[ self.clamp(x-1, w), self.clamp(y, h) ] - px_tl = org[ self.clamp(x-1, w), self.clamp(y+1, h) ] + if not water: + for y in range(h): + for x in range(w): + p = org[x,y] + if p[3] > 0: # Only do something if there is something to do in layer + # Neighboring pixels + px_t = org[ self.clamp(x, w), self.clamp(y+1, h) ] + px_tr = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] + px_r = org[ self.clamp(x+1, w), self.clamp(y, h) ] + px_br = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] + px_b = org[ self.clamp(x, w), self.clamp(y-1, h) ] + px_bl = org[ self.clamp(x-1, w), self.clamp(y-1, h) ] + px_l = org[ self.clamp(x-1, w), self.clamp(y, h) ] + px_tl = org[ self.clamp(x-1, w), self.clamp(y+1, h) ] - # Intensities of pixels - it_t = self.intensity(px_t) - it_tr = self.intensity(px_tr) - it_r = self.intensity(px_r) - it_br = self.intensity(px_br) - it_b = self.intensity(px_b) - it_bl = self.intensity(px_bl) - it_l = self.intensity(px_l) - it_tl = self.intensity(px_tl) + # Intensities of pixels + it_t = self.intensity(px_t) + it_tr = self.intensity(px_tr) + it_r = self.intensity(px_r) + it_br = self.intensity(px_br) + it_b = self.intensity(px_b) + it_bl = self.intensity(px_bl) + it_l = self.intensity(px_l) + it_tl = self.intensity(px_tl) - # Sobel filter - dx = (it_tr + 2.0 * it_r + it_br) - (it_tl + 2.0 * it_l + it_bl) - dy = (it_bl + 2.0 * it_b + it_br) - (it_tl + 2.0 * it_t + it_tr) - dz = 10 # This is usually a good value for strength - v = (dx, dy, dz) - nrm = self.normalize_vector(v) - - if nrm[1] > 0: - nrm[1] = 0 - (abs(nrm[1])) - else: - nrm[1] = abs(nrm[1]) + # Sobel filter + dx = (it_tr + 2.0 * it_r + it_br) - (it_tl + 2.0 * it_l + it_bl) + dy = (it_bl + 2.0 * it_b + it_br) - (it_tl + 2.0 * it_t + it_tr) + dz = 10 # This is usually a good value for strength + v = (dx, dy, dz) + nrm = self.normalize_vector(v) - # Set pixel - if water: - nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), int(self.map_component(nrm[2])), int(self.map_component(nrm[2]))) - if not water: + if nrm[1] > 0: + nrm[1] = 0 - (abs(nrm[1])) + else: + nrm[1] = abs(nrm[1]) + + # Set pixel nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), 255 - int(self.map_component(nrm[2])), 1) + # Previous code produced bad and eerie looking lines at the border of the image. Let's have a go with this. + if water: + for y in range(h): + for x in range(w): + p = org[x,y] + if p[3] > 0: + wr = randrange(116, 141) + wg = randrange(111, 139) + wb = 255 + if x == 0 or x == w-1: wb = 0 + if y == 0 or y == h-1: wb = 0 + wa = p[3] + c = (wr, wg, wb, wa) + nmp_pix[x,y] = c + + mstr_msg("xp_normalmap", "[X-Plane] Normal map generated") return nmp