From 31c79949feab19682b9a188c9eb8b7dcf47a7fe0 Mon Sep 17 00:00:00 2001 From: Stan Soldatov Date: Mon, 6 Jan 2025 21:44:16 +0100 Subject: [PATCH] Procedural layers. --- data/fs25-map-template.zip | Bin 2197113 -> 2200477 bytes data/fs25-texture-schema.json | 21 ++++++++----- maps4fs/generator/texture.py | 55 +++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/data/fs25-map-template.zip b/data/fs25-map-template.zip index 98b509f741dc723e73d058ad4a3ce74052b7e78c..677c21aa69c94f3252a1da943c8037b4d1e6a18f 100644 GIT binary patch delta 18815 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
    s)U+MsJ_hpDI?4vsTHYCHi0o*Mg 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 MZYboI00&q64=aF0^Z)<= delta 15423 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 Z9GuKj1rtaxQtcB-sGx+rNkBkA{tqkA dict[str, str | list[str] | bool]: # type: ignore """Returns dictionary with layer data. @@ -99,6 +102,7 @@ def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore "usage": self.usage, "background": self.background, "invisible": self.invisible, + "procedural": self.procedural, } data = {k: v for k, v in data.items() if v is not None} @@ -212,6 +216,10 @@ def preprocess(self) -> None: self._weights_dir = self.game.weights_dir_path(self.map_directory) self.logger.debug("Weights directory: %s.", self._weights_dir) + self.procedural_dir = os.path.join(self._weights_dir, "masks") + os.makedirs(self.procedural_dir, exist_ok=True) + self.logger.debug("Procedural directory: %s.", self.procedural_dir) + self.info_save_path = os.path.join(self.map_directory, "generation_info.json") self.logger.debug("Generation info save path: %s.", self.info_save_path) @@ -251,11 +259,56 @@ def get_layer_by_usage(self, usage: str) -> Layer | None: return layer return None - def process(self): + def process(self) -> None: + """Processes the data to generate textures.""" self._prepare_weights() self._read_parameters() self.draw() self.rotate_textures() + self.copy_procedural() + + def copy_procedural(self) -> None: + """Copies some of the textures to use them as mask for procedural generation. + Creates an empty blockmask if it does not exist.""" + blockmask_path = os.path.join(self.procedural_dir, "BLOCKMASK.png") + if not os.path.isfile(blockmask_path): + self.logger.debug("BLOCKMASK.png not found, creating an empty file.") + img = np.zeros((self.map_size, self.map_size), dtype=np.uint8) + cv2.imwrite(blockmask_path, img) # pylint: disable=no-member + + pg_layers_by_type = defaultdict(list) + for layer in self.layers: + if layer.procedural: + # Get path to the original file. + texture_path = layer.get_preview_or_path(self._weights_dir) + for procedural_layer_name in layer.procedural: + pg_layers_by_type[procedural_layer_name].append(texture_path) + + if not pg_layers_by_type: + self.logger.debug("No procedural layers found.") + return + + for procedural_layer_name, texture_paths in pg_layers_by_type.items(): + procedural_save_path = os.path.join(self.procedural_dir, f"{procedural_layer_name}.png") + if len(texture_paths) > 1: + # If there are more than one texture, merge them. + merged_texture = np.zeros((self.map_size, self.map_size), dtype=np.uint8) + for texture_path in texture_paths: + # pylint: disable=E1101 + texture = cv2.imread(texture_path, cv2.IMREAD_UNCHANGED) + merged_texture[texture == 255] = 255 + cv2.imwrite(procedural_save_path, merged_texture) # pylint: disable=no-member + self.logger.debug( + "Procedural file %s merged from %s textures.", + procedural_save_path, + len(texture_paths), + ) + elif len(texture_paths) == 1: + # Otherwise, copy the texture. + shutil.copyfile(texture_paths[0], procedural_save_path) + self.logger.debug( + "Procedural file %s copied from %s.", procedural_save_path, texture_paths[0] + ) def rotate_textures(self) -> None: """Rotates textures of the layers which have tags."""