From: marstr Date: Tue, 10 Sep 2024 17:38:48 +0000 (+0200) Subject: Normal map maker for X-Plane now working correctly, and properly implemented in layer... X-Git-Url: https://marstr.online/code/gitweb.cgi?a=commitdiff_plain;h=7326984762db1eb1e438da8e99d1530a6e26f5af;p=orthographic Normal map maker for X-Plane now working correctly, and properly implemented in layer generation process. Tile folder now has a proper "z_orthographic_" naming convention, data is stored in this folder. Orthos are now stored in DDS format, as opposed to jpg, which was twice as large in file size. Tile generation process completes by generating proper DSF data files for X-Plane. --- diff --git a/defines.py b/defines.py index a746dac..11c56e0 100644 --- a/defines.py +++ b/defines.py @@ -150,6 +150,8 @@ mstr_ortho_layers = [ ("natural", "wetland", "natural", "wetland"), ("natural", "scrub", "natural", "scrub"), ("natural", "heath", "natural", "heath"), + ("natural", "sand", "natural", "sand"), + ("natural", "desert", "natural", "desert"), # Z-Order 3 ("natural", "water", "natural", "water"), ("natural", "bay", "natural", "beach"), @@ -180,7 +182,9 @@ mstr_ortho_layers = [ ("building", "terrace", "building", "industrial"), ("building", "hangar", "building", "industrial"), ("building", "school", "building", "common"), - ("building", "yes", "building", "common") + ("building", "yes", "building", "common"), + ("place", "sea", "natural", "sea"), + ("place", "ocean", "natural", "sea") ] @@ -257,5 +261,7 @@ mstr_mask_blur = [ ("building", "terrace", 1), ("building", "hangar", 1), ("building", "school", 1), - ("building", "yes", 1) + ("building", "yes", 1), + ("place", "sea", 1), + ("place", "ocean", 1) ] diff --git a/layergen.py b/layergen.py index fd334af..2dde29c 100644 --- a/layergen.py +++ b/layergen.py @@ -38,8 +38,6 @@ class mstr_layergen: self._longitude = lng self._lng_number = lngnum self._layerborder = -1 - self._tiledb = mstr_tiledb(lat, lng) - self._tiledb.create_tables() self._is_completion = is_completion # Define layer size depending on what is wanted self._imgsize = 0 @@ -58,6 +56,11 @@ class mstr_layergen: def set_latlng_folder(self, latlngfld): self._latlngfld = latlngfld + # Open DB + def open_db(self): + self._tiledb = mstr_tiledb(self._latitude, self._longitude, self._latlngfld) + self._tiledb.create_tables() + # This generates a "border" image, for example farmland usually has a small space of grass # before the actual crop of farm field itself. This generates this "border" layer, # and returns it. @@ -355,8 +358,9 @@ class mstr_layergen: # on what they are. for i in mstr_mask_blur: if i[0] == self._tag and i[1] == self._value: - osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2])) - break + if self._tag != "place" and (self._value != "sea" or self._value != "ocean"): + osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2])) + break # Begin producing a largely random image samples = 250 # <- We need this in a moment @@ -414,13 +418,6 @@ class mstr_layergen: layer_border = self.genborder(osm_edge, "landuse", "meadow") layer_comp.alpha_composite(layer_border) - # Edges for waters - if self._tag == "natural" and self._value == "water": - osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES) - osm_edge = osm_edge.filter(ImageFilter.MaxFilter) - osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=2)) - layer_comp.alpha_composite(osm_edge) - # 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" ) @@ -430,6 +427,20 @@ 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_generate_normal_maps == True: + nm = False + for n in mstr_xp_normal_maps: + if n[0] == self._tag and n[1] == self._value: + 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() + + # Let's try our hand at pseudo shadows if mstr_shadow_enabled == True: if mstr_shadow_shift >= 2: @@ -759,6 +770,12 @@ class mstr_layergen: # need to make them at this exact point if mstr_xp_genscenery == True: if mstr_xp_generate_normal_maps == True: - nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) - nrm.build_normalmap() + nm = False + for n in mstr_xp_normal_maps: + if n[0] == self._tag and n[1] == self._value: + 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() diff --git a/orthographic.py b/orthographic.py index 09024db..c692a16 100644 --- a/orthographic.py +++ b/orthographic.py @@ -20,6 +20,7 @@ from layergen import * from photogen import * from osmxml import * from tilegen import * +from xp_dsfgen import * # The main class which handles the rest @@ -110,18 +111,18 @@ class mstr_orthographic: mstr_msg("orthographic", "Created Tiles sub folder: " + self._latlngfld) # Generate the orthos folder - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/orthos"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld +"/orthos") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/orthos"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld +"/orthos") mstr_msg("orthographic", "Created tile orthos folder") if mstr_xp_genscenery == True: - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/terrain"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/terrain") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/terrain"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/terrain") mstr_msg("orthographic", "Created X-Plane tile terrain folder") if mstr_xp_generate_normal_maps == True: - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/normals"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/normals") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/normals"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/normals") mstr_msg("orthographic", "Created X-Plane tile normals folder") # The tile is constructed of many smaller parts. We walk through the @@ -170,7 +171,7 @@ class mstr_orthographic: mstr_msg("orthographic", "Adjusted bounding box for XML object") # Determine what to do... maybe work was interrupted - if os.path.isfile(mstr_datafolder + "Tiles/" + self._latlngfld + "/orthos/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".jpg") == False: + if os.path.isfile(mstr_datafolder + "Tiles/" + self._latlngfld + "/orthos/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".dds") == False: # Let the user know mstr_msg("orthographic", "Generating missing orthophoto " + str(cur_tile_y) + "-" + str(cur_tile_x)) @@ -203,6 +204,7 @@ class mstr_orthographic: lg = mstr_layergen(layer[0], layer[1], self._lat, cur_tile_y, self._long, cur_tile_x, layer[2]) lg.set_max_latlng_tile(maxlatlng) lg.set_latlng_folder(self._latlngfld) + lg.open_db() lg.genlayer() curlyr = curlyr+1 mstr_msg("orthographic", "All layers created") @@ -210,11 +212,9 @@ class mstr_orthographic: # We should have all layers now. # Snap a photo with our satellite :) mstr_msg("orthographic", "Generating ortho photo") - pg = mstr_photogen(self._lat, self._long, cur_tile_y, cur_tile_x, maxlatlng[0], maxlatlng[1]) + pg = mstr_photogen(self._lat, self._long, cur_tile_y, cur_tile_x, maxlatlng[0], maxlatlng[1]) pg.genphoto() mstr_msg("orthographic", " -- Ortho photo generated -- ") - if mstr_xp_genscenery == True: - xp_datagroup = xp_datagroup + 1 print("") print("") @@ -254,6 +254,21 @@ class mstr_orthographic: mstr_msg("orthographic", "Generation of all tiles completed!") + + # Complete scenery + if mstr_xp_genscenery == True: + dsf = mstr_xp_dsfgen(self._lat, self._long, mlat, mlng, self._vstep) + dsf.build_dsf_for_tile() + mstr_msg("orthographic", "X-Plane scenery completed") + + mstr_msg("orthographic", "Final step completed.") + mstr_msg("orthographic", "Tile data in: " + self._output + "/Tiles/z_orthographic_" + self._latlngfld) + print("") + mstr_msg("orthographic", "Thanks for using Orthographic! -- Best, Marcus") + print("") + + # Let's leave this out for the moment + """ mstr_msg("orthographic", "Generating ZL16 tiles and keeping airport tiles") tg = mstr_tilegen(self._lat, self._lng, self._vstep, top_lat, top_lng) tg.genTiles() @@ -264,6 +279,7 @@ class mstr_orthographic: print("") mstr_msg("orthographic", "Thanks for using Orthographic! -- Best, Marcus") print("") + """ diff --git a/photogen.py b/photogen.py index 99b8bb0..51c9778 100644 --- a/photogen.py +++ b/photogen.py @@ -88,6 +88,8 @@ class mstr_photogen: # Generate the layer as if it were part of the OSM data lg = mstr_layergen(tag, value, self._lat, self._ty, self._lng, self._tx, False, is_completion=True) lg.set_max_latlng_tile(self._maxlatlng) + lg.set_latlng_folder(self._latlngfld) + lg.open_db() lg.genlayer() # Load the image @@ -100,6 +102,16 @@ class mstr_photogen: self._tile = completion + # There may be some tiles that have a larger sea or even an ocean in them - these need to be + # removed from the final tile + ocean_pix = self._tile.load() + for y in range(self._tile.width): + for x in range(self._tile.height): + p = ocean_pix[x,y] + if p[0] == 255 and p[1] == 0 and p[2] == 255: + t = (0,0,0,0) + ocean_pix[x,y] = t + # We are now in posession of the final image. # Scale to correct size. @@ -107,15 +119,18 @@ class mstr_photogen: # This we can save accordingly. self._tile.convert('RGB').save(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") + # Now we convert this into a DDS with image.Image(filename=mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") as img: img.compression = "dxt1" - img.save(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".dds") + img.save(filename=mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".dds") # TODO: CUT OUT OCEANS AND SEAS IN DDS os.remove(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") + + # This checks the final image for empty patches. Should one be # 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, diff --git a/tiledb.py b/tiledb.py index 021f59e..b05c1ae 100644 --- a/tiledb.py +++ b/tiledb.py @@ -18,13 +18,14 @@ from functions import * from log import * class mstr_tiledb: - def __init__(self, lat, lng): + def __init__(self, lat, lng, latlngfld): # Note coords self._latitude = lat self._longitude = lng + self._latlngfld = latlngfld # The db file will be created, should it not exist - self._conn = sqlite3.connect(mstr_datafolder + "Tiles/" + str(self._latitude) + "_" + str(self._longitude) + "/data.db") + self._conn = sqlite3.connect(mstr_datafolder + "Tiles/z_orthographic_" + latlngfld + "/data.db") self._crs = self._conn.cursor() #mstr_msg("tiledb", "Database object initiated") diff --git a/xp_dsfgen.py b/xp_dsfgen.py index 4ee910a..d764230 100644 --- a/xp_dsfgen.py +++ b/xp_dsfgen.py @@ -209,8 +209,3 @@ class mstr_xp_dsfgen: self.convert_dsf_text() mstr_msg("xp_dsfgen", "[X-Plane] DSF for tile completed") - - -# Testing -dsf = mstr_xp_dsfgen(51, 7, 101, 63, 0.01010) -dsf.build_dsf_for_tile() \ No newline at end of file diff --git a/xp_normalmap.py b/xp_normalmap.py index 3f1850b..56996c3 100644 --- a/xp_normalmap.py +++ b/xp_normalmap.py @@ -42,7 +42,7 @@ class mstr_xp_normalmap: # then provide it def load_layer(self): qtr = int(mstr_photores / 4) - image = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._lng) + "_" + self._tag + "_" + self._value + "_layer.png") + image = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._tv) + "_" + str(self._lng) + "-" + str(self._th) + "_" + self._tag + "-" + self._value + "_layer.png") image = image.resize((qtr,qtr), Image.Resampling.LANCZOS) mstr_msg("xp_normalmap", "[X-Plane] Layer image loaded") return image @@ -50,13 +50,16 @@ class mstr_xp_normalmap: # A few mathematical calls we need # -------------------------------------------------------- - def intensity(pixel): + def intensity(self, pixel): avg = (pixel[0] + pixel[1] + pixel[2]) / 3 - pavg = 255.0 / avg + if avg > 0: + pavg = 255.0 / avg + else: + pavg = 0 return pavg - def clamp(px, mpx): + def clamp(self, px, mpx): if px > mpx-1: return mpx-1 else: @@ -66,11 +69,11 @@ class mstr_xp_normalmap: return px - def map_component(px): + def map_component(self, px): return (px + 1.0) * (255.0 / 2.0) - def normalize_vector(v): + def normalize_vector(self, v): vc = np.array([v[0], v[1], v[2]]) norm = np.linalg.norm(vc) nv = vc / norm @@ -81,7 +84,8 @@ class mstr_xp_normalmap: # The Big Mac. Generate the normal map def generate_normal_map_for_layer(self, image): mstr_msg("xp_normalmap", "[X-Plane] Beginning normal map generation") - nmp = Image.new("RGBA", (image.width, image.height), (128,128,255,255)) + #nmp = Image.new("RGBA", (image.width, image.height), (128,128,255,255)) + nmp = Image.new("RGBA", (image.width, image.height), (0,0,0,0)) org = image.load() nmp_pix = nmp.load() @@ -93,44 +97,49 @@ class mstr_xp_normalmap: a = v * 255.0 alpha = int(a) + # Let's try some shenanigans - for y in range(image.width): - for x in range(image.height): - # Neighboring pixels - px_t = org[ self.clamp(x, image.width), self.clamp(y+1, image.height) ] - px_tr = org[ self.clamp(x+1, image.width), self.clamp(y+1, image.height) ] - px_r = org[ self.clamp(x+1, image.width), self.clamp(y, image.height) ] - px_br = org[ self.clamp(x+1, image.width), self.clamp(y+1, image.height) ] - px_b = org[ self.clamp(x, image.width), self.clamp(y-1, image.height) ] - px_bl = org[ self.clamp(x-1, image.width), self.clamp(y-1, image.height) ] - px_l = org[ self.clamp(x-1, image.width), self.clamp(y, image.height) ] - px_tl = org[ self.clamp(x-1, image.width), self.clamp(y+1, image.height) ] - - # 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) - - # Invert height for our Orthos - 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])), int(self.map_component(nrm[2])), alpha) + 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) ] + + # 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) + + # Invert height for our Orthos + 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])), int(self.map_component(nrm[2])), alpha) mstr_msg("xp_normalmap", "[X-Plane] Normal map generated") return nmp