maps4fs 1.8.171__tar.gz → 1.8.173__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of maps4fs might be problematic. Click here for more details.
- {maps4fs-1.8.171 → maps4fs-1.8.173}/PKG-INFO +10 -7
- {maps4fs-1.8.171 → maps4fs-1.8.173}/README.md +9 -6
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/background.py +18 -12
- maps4fs-1.8.173/maps4fs/generator/component/base/component_mesh.py +242 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dem.py +138 -93
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/dtm.py +2 -153
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/srtm.py +2 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/settings.py +11 -7
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs.egg-info/PKG-INFO +10 -7
- {maps4fs-1.8.171 → maps4fs-1.8.173}/pyproject.toml +1 -1
- {maps4fs-1.8.171 → maps4fs-1.8.173}/tests/test_generator.py +12 -3
- maps4fs-1.8.171/maps4fs/generator/component/base/component_mesh.py +0 -125
- {maps4fs-1.8.171 → maps4fs-1.8.173}/LICENSE.md +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/base/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/base/component.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/base/component_image.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/base/component_xml.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/config.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/grle.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/component/i3d.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/base/wcs.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/base/wms.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/bavaria.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/canada.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/england.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/finland.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/flanders.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/france.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/hessen.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/italy.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/niedersachsen.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/norway.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/nrw.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/scotland.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/spain.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/usgs.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/dtm/utils.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/game.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/map.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/qgis.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/satellite.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/generator/texture.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/logger.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/toolbox/__init__.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/toolbox/background.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/toolbox/custom_osm.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs/toolbox/dem.py +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs.egg-info/SOURCES.txt +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs.egg-info/dependency_links.txt +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs.egg-info/requires.txt +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/maps4fs.egg-info/top_level.txt +0 -0
- {maps4fs-1.8.171 → maps4fs-1.8.173}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: maps4fs
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.173
|
4
4
|
Summary: Generate map templates for Farming Simulator from real places.
|
5
5
|
Author-email: iwatkot <iwatkot@gmail.com>
|
6
6
|
License: MIT License
|
@@ -562,13 +562,17 @@ You can also apply some advanced settings to the map generation process.<br>
|
|
562
562
|
|
563
563
|
### DEM Advanced settings
|
564
564
|
|
565
|
-
-
|
565
|
+
- Adjust terrain to ground level: Enabling this setting (default) will raise or lower the terrain so that it's lowest point is at ground level (taking into account the plateau and water depth settings).
|
566
566
|
|
567
|
-
-
|
567
|
+
- Multiplier: DEM multiplier can be used to make the terrain more pronounced. By default the DEM file will be exact copy of the real terrain. If you want to make it more steep, you can increase this value. The recommended value of the multiplier is 1.
|
568
568
|
|
569
|
-
-
|
569
|
+
- Minimum height scale: This value is used as the heightScale in your map i3d. It will automatically be set higher if the elevation in your map (plus plateau, ceiling and water depth) is higher than this value.
|
570
570
|
|
571
|
-
-
|
571
|
+
- Plateau: DEM plateau value (in meters) is used to make the whole map higher or lower. This value will be added to each pixel of the DEM image, making it higher. It can be useful if you're working on a plain area and need to add some negative height (to make rivers, for example).
|
572
|
+
|
573
|
+
- Ceiling: DEM ceiling value (in meters) is used to add padding in the DEM above the highest elevation in your map area. It can be useful if you plan to manually add some height to the map by sculpting the terrain in GE.
|
574
|
+
|
575
|
+
- Water depth: Water depth value (in meters) will be subtracted from the DEM image, making the water deeper. The pixel value used for this is calculated based on the heightScale value for your map.
|
572
576
|
|
573
577
|
### Background terrain Advanced settings
|
574
578
|
|
@@ -578,7 +582,7 @@ You can also apply some advanced settings to the map generation process.<br>
|
|
578
582
|
|
579
583
|
- Resize factor - the factor by which the background terrain will be resized. It will be used as 1 / resize_factor while generating the models. Which means that the larger the value the more the terrain will be resized. The lowest value is 1, in this case background terrain will not be resized. Note, than low values will lead to long processing and enormous size of the obj files.
|
580
584
|
|
581
|
-
- Remove center - if enabled, the playable region (map terrain) will be removed from the background terrain.
|
585
|
+
- Remove center - if enabled, the playable region (map terrain) will be removed from the background terrain. By default, it's set to True.
|
582
586
|
|
583
587
|
- Apply decimation - if enabled, the mesh will be simplified to reduce the number of faces.
|
584
588
|
|
@@ -703,4 +707,3 @@ But also, I want to thank the people who helped me with the project in some way,
|
|
703
707
|
- [kbrandwijk](https://github.com/kbrandwijk) - for providing [awesome tool](https://github.com/Paint-a-Farm/satmap_downloader) to download the satellite images from the Google Maps and giving a permission to modify it and create a Python Package.
|
704
708
|
- [Maaslandmods](https://github.com/Maaslandmods) - for the awesome idea to edit the tree schema in UI, images and code snippets on how to do it.
|
705
709
|
- [StrauntMaunt](https://gitlab.com/StrauntMaunt) - for developing procedural generation scripts, providing with the required updates for maps4fs and preparing the docs on how to use procedural generation.
|
706
|
-
|
@@ -536,13 +536,17 @@ You can also apply some advanced settings to the map generation process.<br>
|
|
536
536
|
|
537
537
|
### DEM Advanced settings
|
538
538
|
|
539
|
-
-
|
539
|
+
- Adjust terrain to ground level: Enabling this setting (default) will raise or lower the terrain so that it's lowest point is at ground level (taking into account the plateau and water depth settings).
|
540
540
|
|
541
|
-
-
|
541
|
+
- Multiplier: DEM multiplier can be used to make the terrain more pronounced. By default the DEM file will be exact copy of the real terrain. If you want to make it more steep, you can increase this value. The recommended value of the multiplier is 1.
|
542
542
|
|
543
|
-
-
|
543
|
+
- Minimum height scale: This value is used as the heightScale in your map i3d. It will automatically be set higher if the elevation in your map (plus plateau, ceiling and water depth) is higher than this value.
|
544
544
|
|
545
|
-
-
|
545
|
+
- Plateau: DEM plateau value (in meters) is used to make the whole map higher or lower. This value will be added to each pixel of the DEM image, making it higher. It can be useful if you're working on a plain area and need to add some negative height (to make rivers, for example).
|
546
|
+
|
547
|
+
- Ceiling: DEM ceiling value (in meters) is used to add padding in the DEM above the highest elevation in your map area. It can be useful if you plan to manually add some height to the map by sculpting the terrain in GE.
|
548
|
+
|
549
|
+
- Water depth: Water depth value (in meters) will be subtracted from the DEM image, making the water deeper. The pixel value used for this is calculated based on the heightScale value for your map.
|
546
550
|
|
547
551
|
### Background terrain Advanced settings
|
548
552
|
|
@@ -552,7 +556,7 @@ You can also apply some advanced settings to the map generation process.<br>
|
|
552
556
|
|
553
557
|
- Resize factor - the factor by which the background terrain will be resized. It will be used as 1 / resize_factor while generating the models. Which means that the larger the value the more the terrain will be resized. The lowest value is 1, in this case background terrain will not be resized. Note, than low values will lead to long processing and enormous size of the obj files.
|
554
558
|
|
555
|
-
- Remove center - if enabled, the playable region (map terrain) will be removed from the background terrain.
|
559
|
+
- Remove center - if enabled, the playable region (map terrain) will be removed from the background terrain. By default, it's set to True.
|
556
560
|
|
557
561
|
- Apply decimation - if enabled, the mesh will be simplified to reduce the number of faces.
|
558
562
|
|
@@ -677,4 +681,3 @@ But also, I want to thank the people who helped me with the project in some way,
|
|
677
681
|
- [kbrandwijk](https://github.com/kbrandwijk) - for providing [awesome tool](https://github.com/Paint-a-Farm/satmap_downloader) to download the satellite images from the Google Maps and giving a permission to modify it and create a Python Package.
|
678
682
|
- [Maaslandmods](https://github.com/Maaslandmods) - for the awesome idea to edit the tree schema in UI, images and code snippets on how to do it.
|
679
683
|
- [StrauntMaunt](https://gitlab.com/StrauntMaunt) - for developing procedural generation scripts, providing with the required updates for maps4fs and preparing the docs on how to use procedural generation.
|
680
|
-
|
@@ -235,24 +235,16 @@ class Background(MeshComponent, ImageComponent):
|
|
235
235
|
This setting is used for a Background Terrain, where the center part where the
|
236
236
|
playable area is will be cut out.
|
237
237
|
"""
|
238
|
-
resize_factor = 1 / self.map.background_settings.resize_factor
|
239
|
-
dem_data = cv2.resize(dem_data, (0, 0), fx=resize_factor, fy=resize_factor)
|
240
|
-
if remove_center:
|
241
|
-
half_size = int(self.map_size // 2 * resize_factor)
|
242
|
-
dem_data = self.cut_out_np(dem_data, half_size, set_zeros=True)
|
243
|
-
self.logger.debug("Center removed from DEM data.")
|
244
|
-
self.logger.debug(
|
245
|
-
"DEM data resized to shape: %s with factor: %s", dem_data.shape, resize_factor
|
246
|
-
)
|
247
|
-
|
248
238
|
mesh = self.mesh_from_np(
|
249
239
|
dem_data,
|
250
240
|
include_zeros=include_zeros,
|
251
241
|
z_scaling_factor=self.get_z_scaling_factor(),
|
252
|
-
resize_factor=resize_factor,
|
242
|
+
resize_factor=self.map.background_settings.resize_factor,
|
253
243
|
apply_decimation=self.map.background_settings.apply_decimation,
|
254
244
|
decimation_percent=self.map.background_settings.decimation_percent,
|
255
245
|
decimation_agression=self.map.background_settings.decimation_agression,
|
246
|
+
remove_center=remove_center,
|
247
|
+
remove_size=self.map_size,
|
256
248
|
)
|
257
249
|
|
258
250
|
mesh.export(save_path)
|
@@ -414,10 +406,18 @@ class Background(MeshComponent, ImageComponent):
|
|
414
406
|
water_resources_image = cv2.imread(self.water_resources_path, cv2.IMREAD_UNCHANGED)
|
415
407
|
dem_image = cv2.imread(self.output_path, cv2.IMREAD_UNCHANGED)
|
416
408
|
|
409
|
+
# fall back to default value for height_scale 255, it is defined as float | None
|
410
|
+
# but it is always set at this point
|
411
|
+
z_scaling_factor: float = (
|
412
|
+
self.map.shared_settings.mesh_z_scaling_factor
|
413
|
+
if self.map.shared_settings.mesh_z_scaling_factor is not None
|
414
|
+
else 257
|
415
|
+
)
|
416
|
+
|
417
417
|
dem_image = self.subtract_by_mask(
|
418
418
|
dem_image,
|
419
419
|
water_resources_image,
|
420
|
-
self.map.dem_settings.water_depth,
|
420
|
+
int(self.map.dem_settings.water_depth * z_scaling_factor),
|
421
421
|
)
|
422
422
|
|
423
423
|
# Save the modified dem_image back to the output path
|
@@ -432,6 +432,12 @@ class Background(MeshComponent, ImageComponent):
|
|
432
432
|
|
433
433
|
# Single channeled 8 bit image, where the water have values of 255, and the rest 0.
|
434
434
|
plane_water = cv2.imread(self.water_resources_path, cv2.IMREAD_UNCHANGED)
|
435
|
+
|
436
|
+
# Check if the image contains non-zero values.
|
437
|
+
if not np.any(plane_water):
|
438
|
+
self.logger.warning("Water resources image is empty, skipping water generation.")
|
439
|
+
return
|
440
|
+
|
435
441
|
dilated_plane_water = cv2.dilate(
|
436
442
|
plane_water.astype(np.uint8), np.ones((5, 5), np.uint8), iterations=5
|
437
443
|
).astype(np.uint8)
|
@@ -0,0 +1,242 @@
|
|
1
|
+
"""Base class for all components that primarily used to work with meshes."""
|
2
|
+
|
3
|
+
import cv2
|
4
|
+
import numpy as np
|
5
|
+
import trimesh
|
6
|
+
from tqdm import tqdm
|
7
|
+
|
8
|
+
from maps4fs.generator.component.base.component import Component
|
9
|
+
from maps4fs.generator.settings import Parameters
|
10
|
+
|
11
|
+
|
12
|
+
class MeshComponent(Component):
|
13
|
+
"""Base class for all components that primarily used to work with meshes."""
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def validate_np_for_mesh(image_path: str, map_size: int) -> None:
|
17
|
+
"""Checks if the given image is a valid for mesh generation.
|
18
|
+
|
19
|
+
Arguments:
|
20
|
+
image_path (str): The path to the custom background image.
|
21
|
+
map_size (int): The size of the map.
|
22
|
+
|
23
|
+
Raises:
|
24
|
+
ValueError: If the custom background image does not meet the requirements.
|
25
|
+
"""
|
26
|
+
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
|
27
|
+
if image.shape[0] != image.shape[1]:
|
28
|
+
raise ValueError("The custom background image must be a square.")
|
29
|
+
|
30
|
+
if image.shape[0] != map_size + Parameters.BACKGROUND_DISTANCE * 2:
|
31
|
+
raise ValueError("The custom background image must have the size of the map + 4096.")
|
32
|
+
|
33
|
+
if len(image.shape) != 2:
|
34
|
+
raise ValueError("The custom background image must be a grayscale image.")
|
35
|
+
|
36
|
+
if image.dtype != np.uint16:
|
37
|
+
raise ValueError("The custom background image must be a 16-bit grayscale image.")
|
38
|
+
|
39
|
+
@staticmethod
|
40
|
+
def mesh_to_stl(mesh: trimesh.Trimesh, save_path: str) -> None:
|
41
|
+
"""Converts the mesh to an STL file and saves it in the previews directory.
|
42
|
+
Uses powerful simplification to reduce the size of the file since it will be used
|
43
|
+
only for the preview.
|
44
|
+
|
45
|
+
Arguments:
|
46
|
+
mesh (trimesh.Trimesh) -- The mesh to convert to an STL file.
|
47
|
+
"""
|
48
|
+
mesh = mesh.simplify_quadric_decimation(face_count=len(mesh.faces) // 2**6)
|
49
|
+
mesh.export(save_path)
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def mesh_from_np(
|
53
|
+
image: np.ndarray,
|
54
|
+
include_zeros: bool,
|
55
|
+
z_scaling_factor: float,
|
56
|
+
resize_factor: int,
|
57
|
+
apply_decimation: bool,
|
58
|
+
decimation_percent: int,
|
59
|
+
decimation_agression: int,
|
60
|
+
remove_center: bool,
|
61
|
+
remove_size: int,
|
62
|
+
) -> trimesh.Trimesh:
|
63
|
+
"""Generates a mesh from the given numpy array.
|
64
|
+
|
65
|
+
Arguments:
|
66
|
+
image (np.ndarray): The numpy array to generate the mesh from.
|
67
|
+
include_zeros (bool): Whether to include zero values in the mesh.
|
68
|
+
z_scaling_factor (float): The scaling factor for the Z-axis.
|
69
|
+
resize_factor (int): The resizing factor.
|
70
|
+
apply_decimation (bool): Whether to apply decimation to the mesh.
|
71
|
+
decimation_percent (int): The percent of the decimation.
|
72
|
+
decimation_agression (int): The agression of the decimation.
|
73
|
+
remove_center (bool): Whether to remove the center from the mesh.
|
74
|
+
remove_size (int): The size of the center to remove.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
trimesh.Trimesh: The generated mesh.
|
78
|
+
"""
|
79
|
+
output_x_size, _ = image.shape
|
80
|
+
image = image.max() - image
|
81
|
+
|
82
|
+
image = image[::resize_factor, ::resize_factor]
|
83
|
+
|
84
|
+
rows, cols = image.shape
|
85
|
+
x = np.linspace(0, cols - 1, cols)
|
86
|
+
y = np.linspace(0, rows - 1, rows)
|
87
|
+
x, y = np.meshgrid(x, y)
|
88
|
+
z = image
|
89
|
+
|
90
|
+
ground = z.max()
|
91
|
+
|
92
|
+
vertices = np.column_stack([x.ravel(), y.ravel(), z.ravel()])
|
93
|
+
faces = []
|
94
|
+
|
95
|
+
skipped = 0
|
96
|
+
|
97
|
+
for i in tqdm(range(rows - 1), desc="Generating mesh", unit="row"):
|
98
|
+
for j in range(cols - 1):
|
99
|
+
top_left = i * cols + j
|
100
|
+
top_right = top_left + 1
|
101
|
+
bottom_left = top_left + cols
|
102
|
+
bottom_right = bottom_left + 1
|
103
|
+
|
104
|
+
if (
|
105
|
+
ground in [z[i, j], z[i, j + 1], z[i + 1, j], z[i + 1, j + 1]]
|
106
|
+
and not include_zeros
|
107
|
+
):
|
108
|
+
skipped += 1
|
109
|
+
continue
|
110
|
+
|
111
|
+
faces.append([top_left, bottom_left, bottom_right])
|
112
|
+
faces.append([top_left, bottom_right, top_right])
|
113
|
+
|
114
|
+
faces_np = np.array(faces)
|
115
|
+
mesh = trimesh.Trimesh(vertices=vertices, faces=faces_np)
|
116
|
+
mesh = MeshComponent.rotate_mesh(mesh)
|
117
|
+
|
118
|
+
if apply_decimation:
|
119
|
+
percent = decimation_percent / 100
|
120
|
+
mesh = mesh.simplify_quadric_decimation(
|
121
|
+
percent=percent, aggression=decimation_agression
|
122
|
+
)
|
123
|
+
|
124
|
+
try:
|
125
|
+
if not mesh.is_watertight:
|
126
|
+
mesh = MeshComponent.fix_mesh(mesh)
|
127
|
+
except Exception:
|
128
|
+
pass
|
129
|
+
|
130
|
+
mesh = MeshComponent.mesh_to_output_size(
|
131
|
+
mesh,
|
132
|
+
resize_factor,
|
133
|
+
z_scaling_factor,
|
134
|
+
output_x_size,
|
135
|
+
skip_resize_to_expected_size=not include_zeros,
|
136
|
+
)
|
137
|
+
|
138
|
+
if remove_center:
|
139
|
+
mesh = MeshComponent.remove_center_from_mesh(mesh, remove_size)
|
140
|
+
|
141
|
+
return mesh
|
142
|
+
|
143
|
+
@staticmethod
|
144
|
+
def rotate_mesh(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
|
145
|
+
"""Rotates the given mesh by 180 degrees around the Y-axis and Z-axis.
|
146
|
+
|
147
|
+
Arguments:
|
148
|
+
mesh (trimesh.Trimesh): The mesh to rotate.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
trimesh.Trimesh: The rotated mesh.
|
152
|
+
"""
|
153
|
+
mesh_copy = mesh.copy()
|
154
|
+
|
155
|
+
rotation_matrices = [
|
156
|
+
trimesh.transformations.rotation_matrix(np.pi, [0, 1, 0]),
|
157
|
+
trimesh.transformations.rotation_matrix(np.pi, [0, 0, 1]),
|
158
|
+
]
|
159
|
+
|
160
|
+
for rotation_matrix in tqdm(rotation_matrices, desc="Rotating mesh", unit="rotation"):
|
161
|
+
mesh_copy.apply_transform(rotation_matrix)
|
162
|
+
|
163
|
+
return mesh_copy
|
164
|
+
|
165
|
+
@staticmethod
|
166
|
+
def fix_mesh(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
|
167
|
+
"""Fixes the given mesh by filling holes, fixing normals, fixing winding, fixing inversion,
|
168
|
+
fixing broken faces, and stitching.
|
169
|
+
|
170
|
+
Arguments:
|
171
|
+
mesh (trimesh.Trimesh): The mesh to fix.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
trimesh.Trimesh: The fixed mesh.
|
175
|
+
"""
|
176
|
+
mesh_copy = mesh.copy()
|
177
|
+
|
178
|
+
fix_methods = [
|
179
|
+
trimesh.repair.fill_holes,
|
180
|
+
trimesh.repair.fix_normals,
|
181
|
+
trimesh.repair.fix_winding,
|
182
|
+
trimesh.repair.fix_inversion,
|
183
|
+
trimesh.repair.broken_faces,
|
184
|
+
trimesh.repair.stitch,
|
185
|
+
]
|
186
|
+
|
187
|
+
for method in tqdm(fix_methods, desc="Fixing mesh", unit="method"):
|
188
|
+
method(mesh_copy) # type: ignore
|
189
|
+
|
190
|
+
return mesh_copy
|
191
|
+
|
192
|
+
@staticmethod
|
193
|
+
def mesh_to_output_size(
|
194
|
+
mesh: trimesh.Trimesh,
|
195
|
+
resize_factor: int,
|
196
|
+
z_scaling_factor: float,
|
197
|
+
expected_size: int,
|
198
|
+
skip_resize_to_expected_size: bool = False,
|
199
|
+
) -> trimesh.Trimesh:
|
200
|
+
"""Resizes the given mesh to the expected size.
|
201
|
+
|
202
|
+
Arguments:
|
203
|
+
mesh (trimesh.Trimesh): The mesh to resize.
|
204
|
+
resize_factor (int): The resizing factor.
|
205
|
+
z_scaling_factor (float): The scaling factor for the Z-axis.
|
206
|
+
expected_size (int): The expected size.
|
207
|
+
skip_resize_to_expected_size (bool): Whether to skip resizing to the expected size.
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
trimesh.Trimesh: The resized mesh.
|
211
|
+
"""
|
212
|
+
mesh_copy = mesh.copy()
|
213
|
+
|
214
|
+
mesh_copy.apply_scale([resize_factor / 1, resize_factor / 1, z_scaling_factor])
|
215
|
+
|
216
|
+
if not skip_resize_to_expected_size:
|
217
|
+
x_size, y_size, _ = mesh_copy.extents
|
218
|
+
x_resize_factor = expected_size / x_size
|
219
|
+
y_resize_factor = expected_size / y_size
|
220
|
+
|
221
|
+
mesh_copy.apply_scale([x_resize_factor, y_resize_factor, 1])
|
222
|
+
return mesh_copy
|
223
|
+
|
224
|
+
@staticmethod
|
225
|
+
def remove_center_from_mesh(mesh: trimesh.Trimesh, remove_size: int) -> trimesh.Trimesh:
|
226
|
+
"""Removes the center from the given mesh.
|
227
|
+
|
228
|
+
Arguments:
|
229
|
+
mesh (trimesh.Trimesh): The mesh to remove the center from.
|
230
|
+
remove_size (int): The size of the center to remove.
|
231
|
+
|
232
|
+
Returns:
|
233
|
+
trimesh.Trimesh: The mesh with the center removed.
|
234
|
+
"""
|
235
|
+
mesh_copy = mesh.copy()
|
236
|
+
|
237
|
+
_, _, z_size = mesh_copy.extents
|
238
|
+
|
239
|
+
cube_mesh = trimesh.creation.box([remove_size, remove_size, z_size * 4])
|
240
|
+
cube_mesh.apply_translation(mesh_copy.centroid - cube_mesh.centroid)
|
241
|
+
|
242
|
+
return trimesh.boolean.difference([mesh_copy, cube_mesh], check_volume=False)
|