from layergen import *\r
from log import *\r
from functions import *\r
+from xp_normalmap import *\r
\r
# -------------------------------------------------------------------\r
# ORTHOGRAPHIC\r
self._tx = tx\r
self._maxlatlng = [ maxlat, maxlng ]\r
# Define layer size depending on what is wanted\r
- self._imgsize = 0\r
- if mstr_photores == 2048: self._imgsize = 2048\r
- if mstr_photores == 4096: self._imgsize = 6000\r
+ self._imgsize = mstr_photores\r
# Empty image where everything goes into\r
self._tile = Image.new("RGBA", (self._imgsize, self._imgsize))\r
self._latlngfld = self.latlng_folder([lat,lng])\r
corrpix[x,y] = nc\r
if c[3] == 0:\r
corrpix[x,y] = (0,0,0,0)\r
-\r
- # Now cut out inland water\r
- for w in waterlayers:\r
- wtr_pix = w.load()\r
- for y in range(w.height):\r
- for x in range(w.width):\r
- v = wtr_pix[x,y]\r
- if v[3] >= 50:\r
- c = (0,0,0,0)\r
- corrpix[x,y] = c\r
-\r
- """\r
- ddsf_water = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + "_water.png"\r
- if os.path.isfile(ddsf_water) == True:\r
- wtr = Image.open(ddsf_water)\r
- wtr_pix = wtr.load()\r
- for y in range(wtr.height):\r
- for x in range(wtr.width):\r
- v = wtr_pix[x,y]\r
- if v <= 50:\r
- c = (0,0,0,0)\r
- corrpix[x,y] = c\r
- """\r
\r
# We are now in posession of the final image.\r
\r
os.remove(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png")\r
\r
\r
+ # Now generate the normal map for this ortho.\r
+ # But only if this is enabled.\r
+ if mstr_xp_genscenery and mstr_xp_scn_normalmaps:\r
+ # Generate the normal normal map first (hah)\r
+ nrm = mstr_xp_normalmap()\r
+ nrmimg = nrm.generate_normal_map_for_layer(self._tile, False)\r
+\r
+ # Now we need to walk through the water layers and generate a combined normal map\r
+ wtrlyr = Image.new("RGBA", (self._imgsize, self._imgsize))\r
+ for w in waterlayers:\r
+ wtrlyr.alpha_composite(w)\r
+ wtrlyr = wtrlyr.resize((int(mstr_photores/4), int(mstr_photores/4)), Image.Resampling.BILINEAR)\r
+ wtrimg = nrm.generate_normal_map_for_layer(wtrlyr, True)\r
+\r
+ # Blend\r
+ nrmimg.alpha_composite(wtrimg)\r
+\r
+ # Save\r
+ nrmfln = mstr_datafolder + "z_orthographic/normals/" + self._latlngfld + "/" + str(self._ty) + "_" + str(\r
+ self._tx) + ".png"\r
+ nrmimg.save(nrmfln)\r
+\r
+\r
\r
\r
# This checks the final image for empty patches. Should one be\r
class mstr_xp_normalmap:\r
\r
# Only a few params\r
- def __init__(self, lat, lng, tag, value, tv, th, latlngfld):\r
- self._lat = lat\r
- self._lng = lng\r
- self._tag = tag\r
- self._value = value\r
- self._latlngfld = latlngfld\r
- self._tv = tv\r
- self._th = th\r
+ def __init__(self):\r
mstr_msg("xp_normalmap", "[X-Plane] Normal Map generator initialized")\r
\r
\r
pavg = 255.0 / avg\r
else:\r
pavg = 0\r
- return pavg\r
+ return pavg * 3\r
\r
\r
def clamp(self, px, mpx):\r
\r
\r
# The Big Mac. Generate the normal map\r
- def generate_normal_map_for_layer(self, image):\r
+ def generate_normal_map_for_layer(self, image, water=False):\r
mstr_msg("xp_normalmap", "[X-Plane] Beginning normal map generation")\r
# No specularity, no reflectivity - but standard color\r
# Blue (reflectivity) and alpha (specularity) need to be 1 - but can be adjusted as needed\r
image = image.resize((int(mstr_photores/4), int(mstr_photores/4)), Image.Resampling.BILINEAR)\r
\r
nmp = Image.new("RGBA", (image.width, image.height), (128,128,1,1))\r
+\r
+ if water: nmp = Image.new("RGBA", (image.width, image.height), (128, 128, 255, 0))\r
+\r
org = image.load()\r
nmp_pix = nmp.load()\r
\r
nrm[1] = abs(nrm[1])\r
\r
# Set pixel\r
- nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), 255 - int(self.map_component(nrm[2])), 1)\r
+ if water:\r
+ 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])))\r
+ if not water:\r
+ nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), 255 - int(self.map_component(nrm[2])), 1)\r
\r
mstr_msg("xp_normalmap", "[X-Plane] Normal map generated")\r
return nmp\r
nrm = self.generate_normal_map_for_layer(layer)\r
\r
# Normal map final file name\r
- nrmfln = mstr_datafolder + "z_orthographic/normals/" + self._latlngfld + "/" + str(self._tv) + "_" + str(self._th) + ".png"\r
-\r
- # Check for existence of normal map file\r
- ex = os.path.isfile(nrmfln)\r
-\r
- # Does not exist? Just save\r
- if ex == False:\r
- nrm.save(nrmfln)\r
-\r
- # Exists? Open it, composite both, save\r
- if ex == True:\r
- nrmmap = Image.open(nrmfln)\r
- nrmmap.alpha_composite(nrm)\r
-\r
- # Specularity blending correction\r
- nrmmap_pix = nrmmap.load()\r
- for y in range(nrmmap.height):\r
- for x in range(nrmmap.width):\r
- c = nrmmap_pix[x,y]\r
- nrmmap_pix[x,y] = (c[0], c[1], c[2], 1)\r
- nrmmap.save(nrmfln)\r
+ #nrmfln = mstr_datafolder + "z_orthographic/normals/" + self._latlngfld + "/" + str(self._tv) + "_" + str(self._th) + ".png"\r
\r
- mstr_msg("xp_normalmap", "[X-Plane] Normal map saved")\r
+ mstr_msg("xp_normalmap", "[X-Plane] Normal map generated")\r
+\r
+ return nrm\r
self._dsfstring = ""
self._demdata = None # To be populated when the mesh is built
self._demcoord = None # Also to be populated when mesh is built
+ self._waterdata = [] # So that we know where to implement water
+ #self.load_water_data()
# Build the correct file name for the elevation model
return fn
+ # Load the water data before we generate the mesh
+ def load_water_data(self):
+ fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/wtrfile"
+ with open(fn) as file:
+ for line in file:
+ ln = line.replace(" ", "_")
+ ln = ln.replace("\n", "")
+ ln = ln.replace("\r", "")
+ self._waterdata.append(ln)
+
+
+ # Check if ortho has water
+ def does_ortho_have_water(self, ortho):
+ wtr = False
+ if ortho in self._waterdata: wtr = True
+ return wtr
+
+
+
# Build the DSF for the ortho photo overlay
def build_and_convert_dsf(self):
end = self.find_earthnavdata_number()
llf = self.xplane_latlng_folder(end)
meshtxt = mstr_datafolder + "_cache/mesh_"+self._latlngfld+".txt"
- scr = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/meshscript.txt"
cmd = mstr_xp_dsftool + " --text2dsf " + meshtxt + " '" + mstr_datafolder + "z_orthographic/Earth nav data/" + llf + "/" + self._latlngfld + ".dsf'"
os.system(cmd)
terstr = terstr + "TERRAIN\r\n"
terstr = terstr + "\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 + "TEXTURE_NOWRAP ../../orthos/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".dds\r\n"
- if mstr_xp_scn_normalmaps == True:
+ terstr = terstr + "BASE_TEX_NOWRAP ../../orthos/" + self._latlngfld + "/" + str(lat)+"_"+str(lng)+".dds\r\n"
+ if mstr_xp_scn_normalmaps:
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"
with open(meshtxt, 'w') as textfile:
textfile.write(dsf_str)
+ dsf_str = ""
+
+ # Orthos
for lat in range(1, self._mlat+1):
for lng in range(1, self._mlng+1):
# Write the line only if an ortho exists of course.
ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".dds"
- ddsf_water = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + "_water.png"
- if os.path.isfile(ddsf) == True:
- dsfstr = "TERRAIN_DEF terrain/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".ter\r\n"
-
- # Let's check if this tile needs water beneath
- if os.path.isfile(ddsf_water) == True:
- dsfstr = dsfstr + "TERRAIN_DEF terrain_Water\r\n"
+ if os.path.isfile(ddsf):
+ dsf_str = dsf_str + "TERRAIN_DEF terrain/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".ter\r\n"
- with open(meshtxt, 'a') as textfile:
- textfile.write(dsfstr)
+ with open(meshtxt, 'a') as textfile:
+ textfile.write(dsf_str)
# OK. So. Let's build the mesh.
-
- """
- # First, the ground water mesh
- with open(meshtxt, 'a') as textfile:
- textfile.write("BEGIN_PATCH 0 0.000000 -1.000000 1 5\r\n")
-
- # Vertical row (Latitude Row)
- for lat_r in range(0, len(self._demcoord)-2):
-
- # Horizontal row (Longitude Column)
- for lng_c in range(0, len(self._demcoord)-2):
-
- # Lat/lng coordinate
- lat_crd = self._demcoord[lat_r][lng_c][0]
- lat_crd_t = self._demcoord[lat_r+1][lng_c][0]
- lng_crd = self._demcoord[lat_r][lng_c][1]
- lng_crd_r = self._demcoord[lat_r][lng_c+1][1]
-
- # Coords of triangle vertices
- # 0 - Longitude
- # 1 - Latitude
- # 2 - Height in m
- t1_v1 = [ lng_crd_r, lat_crd, 0 ]
- t1_v2 = [ lng_crd, lat_crd_t, 0 ]
- t1_v3 = [ lng_crd_r, lat_crd_t, 0 ]
- t2_v1 = [ lng_crd, lat_crd_t, 0 ]
- t2_v2 = [ lng_crd_r, lat_crd, 0 ]
- t2_v3 = [ lng_crd, lat_crd, 0 ]
-
-
- t1_v1 = [ lng_crd_r, lat_crd, self._demcoord[lat_r][lng_c+1][2] ]
- t1_v2 = [ lng_crd, lat_crd_t, self._demcoord[lat_r+1][lng_c][2] ]
- t1_v3 = [ lng_crd_r, lat_crd_t, self._demcoord[lat_r+1][lng_c+1][2] ]
- t2_v1 = [ lng_crd, lat_crd_t, self._demcoord[lat_r+1][lng_c][2] ]
- t2_v2 = [ lng_crd_r, lat_crd, self._demcoord[lat_r][lng_c+1][2] ]
- t2_v3 = [ lng_crd, lat_crd, self._demcoord[lat_r][lng_c][2] ]
-
-
- # Write down the two triangles
- t_str = ""
- t_str = t_str + "BEGIN_PRIMITIVE 0\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t1_v1[0]) + " " + str(t1_v1[1]) + " " + str(t1_v1[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t1_v2[0]) + " " + str(t1_v2[1]) + " " + str(t1_v2[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t1_v3[0]) + " " + str(t1_v3[1]) + " " + str(t1_v3[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "END_PRIMITIVE 0\r\n"
- t_str = t_str + "BEGIN_PRIMITIVE 0\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t2_v1[0]) + " " + str(t2_v1[1]) + " " + str(t2_v1[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t2_v2[0]) + " " + str(t2_v2[1]) + " " + str(t2_v2[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "PATCH_VERTEX " + str(t2_v3[0]) + " " + str(t2_v3[1]) + " " + str(t2_v3[2]) + " 0.000015 0.000015\r\n"
- t_str = t_str + "END_PRIMITIVE 0\r\n"
-
- # Send to the file
- with open(meshtxt, 'a') as textfile:
- textfile.write(t_str)
-
- t_str = ""
-
- # Water mesh ends
- with open(meshtxt, 'a') as textfile:
- textfile.write("END PATCH\r\n")
- """
# Current patch
curpatch = 0
for lat in range(1, self._mlat+1):
for lng in range(1, self._mlng+1):
-
# Create the patch only if the matching ortho exists.
# This way we make sure that we hit the same order as the .ter files.
# We can also detect which lat and lng coord we are on.
- #ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/1_1.dds"
- ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".dds"
- ddsf_water = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + "_water.png"
- if os.path.isfile(ddsf) == True:
-
- scangrid = self.find_height_scan_start_end_points([ self._lat+((lat-1)*self._vstep), self._lng+((lng-1)*mstr_zl_18) ])
- #sloped = self.build_sloped_scangrid(scangrid)
+ ddsf = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".dds"
+ if os.path.isfile(ddsf):
# Base coords for this ortho
base_lat = self._lat + ((lat-1) * self._vstep)
latstep = self._vstep/odiv
lngstep = mstr_zl_18 /odiv
uv_step = 1 / odiv
-
- # Height values
- hgt_bl = 0
- hgt_br = 0
- hgt_tr = 0
- hgt_tl = 0
# Generate the ortho tile
for y in range(0,odiv):
t_str = ""
- # Height value:
- # hgtindex = self.find_height_for_coord([lat, lng])
- # height = self._demcoord[hgtindex[0]][hgtindex[1]][2]
-
-
# End this patch
with open(meshtxt, 'a') as textfile:
textfile.write("END PATCH\r\n")
# Let's check if this tile needs water beneath
- needs_water = False
- if os.path.isfile(ddsf_water) == True: needs_water = True
-
-
- if needs_water == True:
+ """
+ if self.does_ortho_have_water(str(lat) + "_" + str(lng)):
# Begin a new patch
with open(meshtxt, 'a') as textfile:
- textfile.write("BEGIN_PATCH " + str(curpatch) + " 0.000000 -1.000000 1 5\r\n")
+ textfile.write("BEGIN_PATCH " + str(curpatch) + " 0.000000 -1.000000 2 5\r\n")
# Generate the ortho tile
for y in range(0,odiv):
hgt_br_idx = self.find_height_for_coord([lat_b, lng_r])
hgt_tr_idx = self.find_height_for_coord([lat_t, lng_r])
hgt_tl_idx = self.find_height_for_coord([lat_t, lng_l])
- hgt_bl = round(self._demcoord[ hgt_bl_idx[0] ][ hgt_bl_idx[1] ][2] - .1, 6)
- hgt_br = round(self._demcoord[ hgt_br_idx[0] ][ hgt_br_idx[1] ][2] - .1, 6)
- hgt_tr = round(self._demcoord[ hgt_tr_idx[0] ][ hgt_tr_idx[1] ][2] - .1, 6)
- hgt_tl = round(self._demcoord[ hgt_tl_idx[0] ][ hgt_tl_idx[1] ][2] - .1, 6)
+ hgt_bl = round(self._demcoord[ hgt_bl_idx[0] ][ hgt_bl_idx[1] ][2] - .01, 6)
+ hgt_br = round(self._demcoord[ hgt_br_idx[0] ][ hgt_br_idx[1] ][2] - .01, 6)
+ hgt_tr = round(self._demcoord[ hgt_tr_idx[0] ][ hgt_tr_idx[1] ][2] - .01, 6)
+ hgt_tl = round(self._demcoord[ hgt_tl_idx[0] ][ hgt_tl_idx[1] ][2] - .01, 6)
# Coords of triangle vertices
# 0 - Longitude
# Increase patch number
curpatch = curpatch + 1
+ """
if startend[2] < 0: startend[2] = 0
if startend[3] > len(self._demdata)-1: startend[3] = startend[3] = len(self._demdata)-1
-
- """
- t = self._lat
- while t < startcoord[0]:
- t = t + self._vstep
- startend[0] = startend[0]+1
-
- t = self._lat
- while t < startcoord[0]+self._vstep:
- t = t + self._vstep
- startend[1] = startend[1]+1
-
- t = self._lng
- while t < startcoord[1]:
- t = t + mstr_zl_18
- startend[2] = startend[2]+1
-
- t = self._lng
- while t < startcoord[1]+mstr_zl_18:
- t = t + mstr_zl_18
- startend[3] = startend[3]+1
-
- # Some corrections
- startend[0] = startend[0]-1
- if startend[0] < 0: startend[0] = 0
-
- startend[1] = startend[1]+1
- if startend[1] > len(self._demdata)-1: startend[1] = startend[1] = len(self._demdata)-1
-
- startend[2] = startend[2]-1
- if startend[2] < 0: startend[2] = 0
-
- startend[3] = startend[3]+1
- if startend[3] > len(self._demdata)-1: startend[3] = startend[3] = len(self._demdata)-1
- """
-
return startend
-
- # Function to subdivide between two vectors
- def subdivide_vectors(self, v1, v2, subdivisions):
- #return np.linspace(v1, v2, subdivisions + 2, axis=0) # +2 to include endpoints
- return numpy.linspace(v1, v2, subdivisions + 2, axis=0) # +2 to include endpoints
-
-
- # This build a scangrid with increased resolution, extrapolated from existing points.
- # With this we can accurately depict the height of a point a long the slope of the ground mesh.
- def build_sloped_scangrid(self, grid):
-
- # Contains the data as defined by the grid passed in
- tmp_dem = []
-
- # Acquire original grid data
- for l in range(grid[2], grid[3]+1):
- row = []
- for c in range(grid[0], grid[1]+1):
- row.append(self._demcoord[l][c])
- tmp_dem.append(row)
-
- # Subdivide the array
- subdivisions = 10
- result = []
- for i in range(len(tmp_dem) - 1):
- for j in range(len(tmp_dem[i]) - 1):
- subdivided = self.subdivide_vectors(tmp_dem[i][j], tmp_dem[i][j + 1], subdivisions)
- if i > 0: # Avoid duplicating start point
- subdivided = subdivided[1:]
- result.append(subdivided)
-
- # Combine all subdivisions into one array
- result = numpy.vstack(result)
-
- return result
\ No newline at end of file