Added custom district generator and old GDMC functions and classes (to get the height, water and tree map)
This commit is contained in:
34
world_maker/Block.py
Normal file
34
world_maker/Block.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from gdpc import Editor, Block, geometry
|
||||||
|
from gdpc.lookup import *
|
||||||
|
import numpy as np
|
||||||
|
import math
|
||||||
|
|
||||||
|
SOLID_NATURAL_BLOCKS = SOILS | STONES | ORES | LIQUIDS
|
||||||
|
|
||||||
|
class Block:
|
||||||
|
def __init__(self, coordinates:tuple, name:str):
|
||||||
|
self.coordinates = coordinates
|
||||||
|
self.name = name
|
||||||
|
self.neighbors = []
|
||||||
|
self.surface = None
|
||||||
|
|
||||||
|
|
||||||
|
def addNeighbors(self, neighbors:list[Block]):
|
||||||
|
for neighbor in neighbors:
|
||||||
|
self.neighbors.append(neighbor)
|
||||||
|
|
||||||
|
def isSurface(self):
|
||||||
|
if self.surface == None:
|
||||||
|
if str(self.name) in SOLID_NATURAL_BLOCKS:
|
||||||
|
for neighbor in self.neighbors:
|
||||||
|
if str(neighbor.name) not in SOLID_NATURAL_BLOCKS:
|
||||||
|
self.surface = True
|
||||||
|
return True
|
||||||
|
if len(self.neighbors) != 0:
|
||||||
|
self.surface = False
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.surface = False
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return self.surface
|
||||||
126
world_maker/City.py
Normal file
126
world_maker/City.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
from District import District, CustomDistrict, VoronoiDistrict
|
||||||
|
from Position import Position
|
||||||
|
from PIL import Image
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class City:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
districts (list): The list of districts in the city.
|
||||||
|
map_data (list): The 2D list representing the map of the city.
|
||||||
|
height_map (list): The 2D list representing the height map of the city.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
The constructor for the City class.
|
||||||
|
"""
|
||||||
|
self.districts = []
|
||||||
|
self.map_data = []
|
||||||
|
self.height_map = []
|
||||||
|
self.init_maps()
|
||||||
|
|
||||||
|
def init_maps(self):
|
||||||
|
"""
|
||||||
|
Initialize the maps of the city. It reads the heightmap and watermap images and converts them into 2D lists.
|
||||||
|
"""
|
||||||
|
heightmap = Image.open('./data/heightmap.png').convert('L')
|
||||||
|
watermap = Image.open('./data/watermap.png').convert('L')
|
||||||
|
width, height = heightmap.size
|
||||||
|
self.map_data = [[-1 if watermap.getpixel((x, y)) > 0 else 0 for x in range(width)] for y in range(height)]
|
||||||
|
self.height_map = [[heightmap.getpixel((x, y)) for x in range(width)] for y in range(height)]
|
||||||
|
watermap.close()
|
||||||
|
heightmap.close()
|
||||||
|
|
||||||
|
def add_district(self, center: Position):
|
||||||
|
"""
|
||||||
|
Add a new district to the city.
|
||||||
|
|
||||||
|
:param center: The center position of the new district.
|
||||||
|
"""
|
||||||
|
self.districts.append(CustomDistrict(len(self.districts) + 1, center))
|
||||||
|
self.map_data[center.y][center.x] = len(self.districts)
|
||||||
|
|
||||||
|
def is_expend_finished(self):
|
||||||
|
"""
|
||||||
|
Check if the expansion of all districts in the city is finished.
|
||||||
|
|
||||||
|
:return: True if the expansion is finished, False otherwise.
|
||||||
|
"""
|
||||||
|
for district in self.districts:
|
||||||
|
if len(district.area_expend_from_point) > 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def choose_expend_point(self, point: Position, index_district: int):
|
||||||
|
"""
|
||||||
|
Choose a point to expand a district based on the distance between the center of the district and the point itself.
|
||||||
|
|
||||||
|
:param point: The point to be expanded.
|
||||||
|
:param index_district: The index of the district to be expanded.
|
||||||
|
"""
|
||||||
|
min_distance = point.distance_to(self.districts[index_district].center_expend)
|
||||||
|
index_district_chosen = index_district
|
||||||
|
for i in range(index_district + 1, len(self.districts)):
|
||||||
|
if point in self.districts[i].area_expend:
|
||||||
|
distance = point.distance_to(self.districts[i].center_expend)
|
||||||
|
if distance < min_distance:
|
||||||
|
min_distance = distance
|
||||||
|
self.districts[index_district_chosen].area_expend.remove(point)
|
||||||
|
index_district_chosen = i
|
||||||
|
else:
|
||||||
|
self.districts[i].area_expend.remove(point)
|
||||||
|
self.districts[index_district_chosen].area.append(point)
|
||||||
|
self.districts[index_district_chosen].area_expend_from_point.append(point)
|
||||||
|
self.districts[index_district_chosen].area_expend.remove(point)
|
||||||
|
self.map_data[point.y][point.x] = index_district_chosen + 1
|
||||||
|
|
||||||
|
def update_expend_district(self):
|
||||||
|
"""
|
||||||
|
Update the expansion points of all districts in the city.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for district in self.districts:
|
||||||
|
if len(district.area_expend_from_point) > 0:
|
||||||
|
district.update_expend_points(district.area_expend_from_point[0], self.map_data, self.height_map)
|
||||||
|
for district in self.districts:
|
||||||
|
for point in district.area_expend:
|
||||||
|
self.choose_expend_point(point, district.tile_id - 1)
|
||||||
|
|
||||||
|
def loop_expend_district(self):
|
||||||
|
"""
|
||||||
|
Loop the expansion of all districts in the city until all districts are fully expanded.
|
||||||
|
"""
|
||||||
|
loop_count = 0
|
||||||
|
while not self.is_expend_finished():
|
||||||
|
self.update_expend_district()
|
||||||
|
loop_count += 1
|
||||||
|
if loop_count % 100 == 0:
|
||||||
|
print("[City] Loop count: ", loop_count)
|
||||||
|
|
||||||
|
def custom_district_draw_map(self):
|
||||||
|
"""
|
||||||
|
Draw the map of the city with different colors for each district.
|
||||||
|
"""
|
||||||
|
width, height = len(self.map_data[0]), len(self.map_data)
|
||||||
|
img = Image.new('RGB', (width, height))
|
||||||
|
colors = {i: (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||||
|
for i in range(1, len(self.districts) + 1)}
|
||||||
|
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if self.map_data[y][x] <= 0:
|
||||||
|
img.putpixel((x, y), (0, 0, 0))
|
||||||
|
else:
|
||||||
|
img.putpixel((x, y), colors[self.map_data[y][x]])
|
||||||
|
|
||||||
|
img.save('./data/custom_district.png')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
city = City()
|
||||||
|
for i in range(10):
|
||||||
|
city.add_district(Position(random.randint(0, 600), random.randint(0, 600)))
|
||||||
|
city.loop_expend_district()
|
||||||
|
city.custom_district_draw_map()
|
||||||
90
world_maker/District.py
Normal file
90
world_maker/District.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
from Position import Position
|
||||||
|
|
||||||
|
|
||||||
|
class District:
|
||||||
|
"""
|
||||||
|
The District class represents a district in the world.
|
||||||
|
A district can be characterized by its type and its unique id.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
tile_id (int): The unique id of the district.
|
||||||
|
type (str): The type of the district. Can be "Forest", "City", "Mountain" or "Villa".
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tile_id: int):
|
||||||
|
"""
|
||||||
|
The constructor for the District class.
|
||||||
|
|
||||||
|
:param tile_id: Unique id of the district (Must be greater than 0)
|
||||||
|
"""
|
||||||
|
if tile_id <= 0:
|
||||||
|
raise ValueError("Tile id must be greater than 0")
|
||||||
|
self.tile_id = tile_id
|
||||||
|
self.type = "" #Forest, City, Montain, Villa
|
||||||
|
|
||||||
|
|
||||||
|
def verify_point(point: Position, point_new: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
||||||
|
"""
|
||||||
|
Function to verify if a new point can be added to a district extend area list.
|
||||||
|
|
||||||
|
:param point: The current point.
|
||||||
|
:param point_new: The new point to be verified.
|
||||||
|
:param map_data: The 2D list representing the map.
|
||||||
|
:param height_map: The 2D list representing the height map.
|
||||||
|
:return: True if the new point can be added, False otherwise.
|
||||||
|
"""
|
||||||
|
return (0 <= point_new.x < len(map_data[0]) and
|
||||||
|
0 <= point_new.y < len(map_data) and
|
||||||
|
map_data[point_new.y][point_new.x] == 0 and
|
||||||
|
abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDistrict(District):
|
||||||
|
"""
|
||||||
|
The CustomDistrict class represents a district that can be expanded.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
center_expend (Position): The center position from which the district expands.
|
||||||
|
area (list): The list of positions that are part of the district.
|
||||||
|
area_expend_from_point (list): The list of positions from which the district can expand.
|
||||||
|
area_expend (list): The list of positions to which the district will maybe expand.
|
||||||
|
"""
|
||||||
|
def __init__(self, tile_id: int, center: Position):
|
||||||
|
"""
|
||||||
|
The constructor for the CustomDistrict class.
|
||||||
|
|
||||||
|
:param tile_id: Unique id of the district (Must be greater than 0)
|
||||||
|
:param center: The center position from which the district expands.
|
||||||
|
"""
|
||||||
|
super().__init__(tile_id)
|
||||||
|
self.center_expend = center
|
||||||
|
self.area = [center]
|
||||||
|
self.area_expend_from_point = [center]
|
||||||
|
self.area_expend = []
|
||||||
|
|
||||||
|
def update_expend_points(self, point: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
||||||
|
"""
|
||||||
|
Update the points to which the district can expand.
|
||||||
|
|
||||||
|
:param point: The current point.
|
||||||
|
:param map_data: The 2D list representing the map.
|
||||||
|
:param height_map: The 2D list representing the height map.
|
||||||
|
"""
|
||||||
|
for pos in [Position(1, 0), Position(-1, 0), Position(0, 1), Position(0, -1)]:
|
||||||
|
if verify_point(point, point + pos, map_data, height_map):
|
||||||
|
if point + pos not in self.area_expend:
|
||||||
|
self.area_expend.append(point + pos)
|
||||||
|
self.area_expend_from_point.remove(point)
|
||||||
|
|
||||||
|
|
||||||
|
class Edge: #I'm Edging rn
|
||||||
|
def __init__(self, point1, point2):
|
||||||
|
self.point1 = point1
|
||||||
|
self.point2 = point2
|
||||||
|
|
||||||
|
|
||||||
|
class VoronoiDistrict(District):
|
||||||
|
def __init__(self, tile_id: int, center: Position):
|
||||||
|
super().__init__(tile_id)
|
||||||
|
self.center = center
|
||||||
|
self.edges = []
|
||||||
37
world_maker/Position.py
Normal file
37
world_maker/Position.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from math import sqrt, atan2
|
||||||
|
|
||||||
|
|
||||||
|
class Position:
|
||||||
|
def __init__(self, x: int = 0, y: int = 0):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __add__(self, other: "Position") -> "Position":
|
||||||
|
return Position(self.x + other.x, self.y + other.y)
|
||||||
|
|
||||||
|
def __sub__(self, other: "Position") -> "Position":
|
||||||
|
return Position(self.x - other.x, self.y - other.y)
|
||||||
|
|
||||||
|
def __mul__(self, other: float) -> "Position":
|
||||||
|
return Position(int(self.x * other), int(self.y * other))
|
||||||
|
|
||||||
|
def __truediv__(self, other: float) -> "Position":
|
||||||
|
return Position(int(self.x / other), int(self.y / other))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.x}, {self.y})"
|
||||||
|
|
||||||
|
def __eq__(self, other: "Position"):
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
|
def get_tuple(self) -> tuple[int, int]:
|
||||||
|
return self.x, self.y
|
||||||
|
|
||||||
|
def distance_to(self, other: "Position") -> float:
|
||||||
|
return sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
|
||||||
|
|
||||||
|
def norm(self) -> float:
|
||||||
|
return sqrt(self.x ** 2 + self.y ** 2)
|
||||||
|
|
||||||
|
def angle_to(self, other: "Position") -> float:
|
||||||
|
return atan2(self.y - other.y, other.x - self.x)
|
||||||
237
world_maker/World.py
Normal file
237
world_maker/World.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
from gdpc import Editor, geometry, lookup
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from Block import Block
|
||||||
|
|
||||||
|
waterBiomes = [
|
||||||
|
"minecraft:ocean",
|
||||||
|
"minecraft:deep_ocean",
|
||||||
|
"minecraft:warm_ocean",
|
||||||
|
"minecraft:lukewarm_ocean",
|
||||||
|
"minecraft:deep_lukewarm_ocean",
|
||||||
|
"minecraft:cold_ocean",
|
||||||
|
"minecraft:deep_cold_ocean",
|
||||||
|
"minecraft:frozen_ocean",
|
||||||
|
"minecraft:deep_frozen_ocean",
|
||||||
|
"minecraft:mushroom_fieds",
|
||||||
|
"minecraft:river",
|
||||||
|
"minecraft:frozen_river",
|
||||||
|
]
|
||||||
|
|
||||||
|
waterBlocks = [
|
||||||
|
"minecraft:water",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class World:
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
editor = Editor(buffering=True)
|
||||||
|
buildArea = editor.getBuildArea()
|
||||||
|
|
||||||
|
self.coordinates_min = [min(buildArea.begin[i], buildArea.last[i]) for i in range(3)]
|
||||||
|
self.coordinates_max = [max(buildArea.begin[i], buildArea.last[i]) for i in range(3)]
|
||||||
|
|
||||||
|
self.length_x = self.coordinates_max[0] - self.coordinates_min[0] + 1
|
||||||
|
self.length_y = self.coordinates_max[1] - self.coordinates_min[1] + 1
|
||||||
|
self.length_z = self.coordinates_max[2] - self.coordinates_min[2] + 1
|
||||||
|
|
||||||
|
self.volume = [[[None for _ in range(self.length_z)] for _ in range(self.length_y)] for _ in
|
||||||
|
range(self.length_x)]
|
||||||
|
|
||||||
|
def isInVolume(self, coordinates):
|
||||||
|
if (self.coordinates_min[0] <= coordinates[0] <= self.coordinates_max[0] and
|
||||||
|
self.coordinates_min[1] <= coordinates[1] <= self.coordinates_max[1] and
|
||||||
|
self.coordinates_min[2] <= coordinates[2] <= self.coordinates_max[2]):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def addBlocks(self, blocks: list[Block]):
|
||||||
|
"""
|
||||||
|
Add block or list of block to the volume.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for block in blocks:
|
||||||
|
if self.isInVolume(block.coordinates):
|
||||||
|
self.volume[block.coordinates[0] - self.coordinates_min[0]][
|
||||||
|
block.coordinates[1] - self.coordinates_min[1]][
|
||||||
|
block.coordinates[2] - self.coordinates_min[2]] = block
|
||||||
|
|
||||||
|
def removeBlock(self, volumeCoordinates):
|
||||||
|
"""
|
||||||
|
Add block or list of block to the volume.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.volume[volumeCoordinates[0]][volumeCoordinates[1]][volumeCoordinates[2]] = None
|
||||||
|
|
||||||
|
def getBlockFromCoordinates(self, coordinates):
|
||||||
|
"""
|
||||||
|
Use already created volume to get block data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
editor = Editor(buffering=True)
|
||||||
|
if self.volume[coordinates[0] - self.coordinates_min[0]][coordinates[1] - self.coordinates_min[1]][
|
||||||
|
coordinates[2] - self.coordinates_min[2]] == None:
|
||||||
|
self.volume[coordinates[0] - self.coordinates_min[0]][coordinates[1] - self.coordinates_min[1]][
|
||||||
|
coordinates[2] - self.coordinates_min[2]] = Block((coordinates[0], coordinates[1], coordinates[2]),
|
||||||
|
editor.getBlock((coordinates[0], coordinates[1],
|
||||||
|
coordinates[2])).id)
|
||||||
|
|
||||||
|
return self.volume[coordinates[0] - self.coordinates_min[0]][coordinates[1] - self.coordinates_min[1]][
|
||||||
|
coordinates[2] - self.coordinates_min[2]]
|
||||||
|
|
||||||
|
def getNeighbors(self, Block):
|
||||||
|
for i in range(-1, 2):
|
||||||
|
for j in range(-1, 2):
|
||||||
|
for k in range(-1, 2):
|
||||||
|
if not (i == 0 and j == 0 and k == 0):
|
||||||
|
coordinates = (Block.coordinates[0] + i, Block.coordinates[1] + j, Block.coordinates[2] + k)
|
||||||
|
if self.isInVolume(coordinates):
|
||||||
|
Block.addNeighbors([self.getBlockFromCoordinates(coordinates)])
|
||||||
|
|
||||||
|
def setVolume(self):
|
||||||
|
"""
|
||||||
|
Scan the world with no optimization. Not tested on large areas.
|
||||||
|
"""
|
||||||
|
|
||||||
|
editor = Editor(buffering=True)
|
||||||
|
|
||||||
|
for x in range(self.coordinates_min[0], self.coordinates_max[0] + 1):
|
||||||
|
for y in range(self.coordinates_min[1], self.coordinates_max[1] + 1):
|
||||||
|
for z in range(self.coordinates_min[2], self.coordinates_max[2] + 1):
|
||||||
|
self.addBlocks([Block((x, y, z), editor.getBlock((x, y, z)).id)])
|
||||||
|
|
||||||
|
def getData(self):
|
||||||
|
"""
|
||||||
|
Generate all needed datas for the generator : heightmap, watermap, and preset the volume with data from the heightmap.
|
||||||
|
"""
|
||||||
|
|
||||||
|
editor = Editor()
|
||||||
|
buildArea = editor.getBuildArea()
|
||||||
|
buildRect = buildArea.toRect()
|
||||||
|
|
||||||
|
xzStart = buildRect.begin
|
||||||
|
print(xzStart, "xzStart")
|
||||||
|
xzDistance = (max(buildRect.end[0], buildRect.begin[0]) - min(buildRect.end[0], buildRect.begin[0]),
|
||||||
|
max(buildRect.end[1], buildRect.begin[1]) - min(buildRect.end[1], buildRect.begin[1]))
|
||||||
|
watermap = Image.new("L", xzDistance, 0)
|
||||||
|
heightmap = Image.new("RGBA", xzDistance, 0)
|
||||||
|
treesmap = Image.new("RGBA", xzDistance, 0)
|
||||||
|
|
||||||
|
slice = editor.loadWorldSlice(buildRect)
|
||||||
|
|
||||||
|
heightmapData = list(np.array(slice.heightmaps["MOTION_BLOCKING_NO_LEAVES"], dtype=np.uint8))
|
||||||
|
treesmapData = list(np.array(slice.heightmaps["MOTION_BLOCKING"], dtype=np.uint8))
|
||||||
|
|
||||||
|
for x in range(0, xzDistance[0]):
|
||||||
|
for z in range(0, xzDistance[1]):
|
||||||
|
|
||||||
|
y = heightmapData[x][z] - 1
|
||||||
|
yTree = treesmapData[x][z] - 1
|
||||||
|
|
||||||
|
print('getData', xzStart[0] + x, y, xzStart[1] + z)
|
||||||
|
|
||||||
|
biome = slice.getBiome((x, y, z))
|
||||||
|
block = slice.getBlock((x, y, z))
|
||||||
|
maybeATree = slice.getBlock((x, yTree, z))
|
||||||
|
|
||||||
|
if maybeATree.id in lookup.TREES:
|
||||||
|
treesmap.putpixel((x, z), (yTree, yTree, yTree))
|
||||||
|
|
||||||
|
if block.id not in lookup.TREES:
|
||||||
|
heightmap.putpixel((x, z), (y, y, y))
|
||||||
|
else:
|
||||||
|
height = 0
|
||||||
|
number = 0
|
||||||
|
for i in range(-1, 2):
|
||||||
|
for j in range(-1, 2):
|
||||||
|
if (i != 0 or j != 0):
|
||||||
|
if (0 <= x + i < xzDistance[0]) and (0 <= z + j < xzDistance[1]):
|
||||||
|
k = heightmapData[x + i][z + j] - 1
|
||||||
|
|
||||||
|
print('getData for tree', xzStart[0] + x + i, k, xzStart[1] + z + j)
|
||||||
|
|
||||||
|
blockNeighbor = slice.getBlock((x + i, k, z + j))
|
||||||
|
if blockNeighbor.id not in lookup.TREES:
|
||||||
|
height += k
|
||||||
|
number += 1
|
||||||
|
if number != 0:
|
||||||
|
average = round(height / number)
|
||||||
|
print(average, "average")
|
||||||
|
heightmap.putpixel((x, z), (average, average, average))
|
||||||
|
|
||||||
|
if ((biome in waterBiomes) or (block.id in waterBlocks)):
|
||||||
|
watermap.putpixel((x, z), (255))
|
||||||
|
else:
|
||||||
|
watermap.putpixel((x, z), (0))
|
||||||
|
|
||||||
|
self.addBlocks([Block((xzStart[0] + x, 100, xzStart[1] + z), block)]) # y set to 100 for 2D
|
||||||
|
|
||||||
|
return heightmap, watermap, treesmap
|
||||||
|
|
||||||
|
def propagate(self, coordinates, scanned=[]):
|
||||||
|
print('propagate', coordinates)
|
||||||
|
i = 0
|
||||||
|
editor = Editor(buffering=True)
|
||||||
|
if self.isInVolume(coordinates):
|
||||||
|
Block = self.getBlockFromCoordinates(coordinates)
|
||||||
|
self.getNeighbors(Block)
|
||||||
|
for neighbor in Block.neighbors:
|
||||||
|
if neighbor not in scanned:
|
||||||
|
scanned.append(neighbor)
|
||||||
|
self.getNeighbors(neighbor)
|
||||||
|
if neighbor.isSurface():
|
||||||
|
self.propagate(neighbor.coordinates, scanned)
|
||||||
|
|
||||||
|
def volumeTo3DBinaryImage(self):
|
||||||
|
binaryImage = []
|
||||||
|
for x in range(self.length_x):
|
||||||
|
binaryImage.append([])
|
||||||
|
for y in range(self.length_y):
|
||||||
|
binaryImage[x].append([])
|
||||||
|
for z in range(self.length_z):
|
||||||
|
if (self.volume[x][y][z] != None):
|
||||||
|
binaryImage[x][y].append(True)
|
||||||
|
else:
|
||||||
|
binaryImage[x][y].append(False)
|
||||||
|
|
||||||
|
return np.array(binaryImage)
|
||||||
|
|
||||||
|
def maskVolume(self, mask):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Delete unusable area of the volume to not let it be use by the skeletonize, based on a filtered image that act as a mask.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mask (image): white or black image : combined watermap smoothed and sobel smoothed.
|
||||||
|
"""
|
||||||
|
editor = Editor()
|
||||||
|
buildArea = editor.getBuildArea()
|
||||||
|
buildRect = buildArea.toRect()
|
||||||
|
|
||||||
|
xzStart = buildRect.begin
|
||||||
|
xzDistance = (max(buildRect.end[0], buildRect.begin[0]) - min(buildRect.end[0], buildRect.begin[0]),
|
||||||
|
max(buildRect.end[1], buildRect.begin[1]) - min(buildRect.end[1], buildRect.begin[1]))
|
||||||
|
|
||||||
|
mask = Image.open(mask)
|
||||||
|
|
||||||
|
slice = editor.loadWorldSlice(buildRect)
|
||||||
|
|
||||||
|
heightmapData = list(np.array(slice.heightmaps["MOTION_BLOCKING_NO_LEAVES"], dtype=np.uint8))
|
||||||
|
|
||||||
|
for x in range(0, xzDistance[0]):
|
||||||
|
for z in range(0, xzDistance[1]):
|
||||||
|
y = heightmapData[x][z] - 1
|
||||||
|
if mask.getpixel((x, z)) == (255):
|
||||||
|
self.removeBlock((x, 100, z)) # y set to 100 for 2D
|
||||||
|
|
||||||
|
def simplifyVolume(self):
|
||||||
|
array = self.volumeTo3DBinaryImage()
|
||||||
|
# array = ndimage.binary_dilation(array, iterations=15)
|
||||||
|
|
||||||
|
return array
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
w = World()
|
||||||
|
w.getData()
|
||||||
BIN
world_maker/data/custom_district.png
Normal file
BIN
world_maker/data/custom_district.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
BIN
world_maker/data/heightmap.png
Normal file
BIN
world_maker/data/heightmap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
world_maker/data/treemap.png
Normal file
BIN
world_maker/data/treemap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
world_maker/data/watermap.png
Normal file
BIN
world_maker/data/watermap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
17
world_maker/world_maker.py
Normal file
17
world_maker/world_maker.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import World
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(world: World):
|
||||||
|
heightmap, watermap, treemap = world.getData()
|
||||||
|
heightmap.save('./data/heightmap.png')
|
||||||
|
watermap.save('./data/watermap.png')
|
||||||
|
treemap.save('./data/treemap.png')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
world = World.World()
|
||||||
|
get_data(world)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user