diff --git a/src/precomp/texturing.py b/src/precomp/texturing.py index ac41e4628..adf9ea4ed 100644 --- a/src/precomp/texturing.py +++ b/src/precomp/texturing.py @@ -312,6 +312,9 @@ class MaterialConf: mat: str scale: float = 1.0 rotation: QuarterRot = QuarterRot.NONE + # If set, determines the maximum number of times this tile can be repeated before a new + # one must be calculated. + repeat_limit: int = 8 # For tile materials, the original size of the surface. # This is used for aligning UVs correctly. tile_size: TileSize = TileSize.TILE_4x4 @@ -329,11 +332,15 @@ def parse(cls, kv: Keyvalues, tile_size: TileSize = TileSize.TILE_4x4) -> Materi if scale <= 0.0: LOGGER.warning('Material scale should be positive, not {}!', scale) scale = 1.0 + repeat_limit = kv.int('repeat', 8) + if repeat_limit <= 0: + LOGGER.warning('Material repeat limit should be positive, not {}!', repeat_limit) + repeat_limit = 1 try: rotation = QuarterRot.parse(kv['rotation']) except LookupError: rotation = QuarterRot.NONE - return cls(material, scale, rotation, tile_size) + return cls(material, scale, rotation, repeat_limit, tile_size) def __bool__(self) -> bool: """Blank materials are falsey.""" diff --git a/src/precomp/tiling_gen.py b/src/precomp/tiling_gen.py index 2cd7403e5..6af874c2a 100644 --- a/src/precomp/tiling_gen.py +++ b/src/precomp/tiling_gen.py @@ -375,15 +375,15 @@ def calculate_plane( else: # Only use the first. size = sizes[0] + mat_conf = rng.choice(gen.get_all(size, subtile.antigel)) if size is TileSize.TILE_4x4: # Force this to only place 1x1, so that other sizes get a chance. width = height = 1 else: - max_width = width // size.width - max_height = height // size.height - width = round(rng.triangular(1, max_width, min(1.5, max_width))) * size.width - height = round(rng.triangular(1, max_height, min(1.5, max_height))) * size.height - mat_conf = rng.choice(gen.get_all(size, subtile.antigel)) + max_width = min(width // size.width, mat_conf.repeat_limit) + max_height = min(height // size.height, mat_conf.repeat_limit) + width = round(rng.triangular(1, max_width, min(1.5, max_width))) * mat_conf.tile_size.width + height = round(rng.triangular(1, max_height, min(1.5, max_height))) * mat_conf.tile_size.height tex_def = make_texdef( mat_conf, subtile.antigel, diff --git a/src/test/precomp/test_texturing.py b/src/test/precomp/test_texturing.py index 487540047..4f06de04d 100644 --- a/src/test/precomp/test_texturing.py +++ b/src/test/precomp/test_texturing.py @@ -38,6 +38,7 @@ def test_mat_parse(caplog: pytest.LogCaptureFixture) -> None: MaterialConf.parse(Keyvalues('blah', [ Keyvalues('scale', '0.25'), Keyvalues('rotation', '0'), + Keyvalues('repeat', '4'), ])) assert MaterialConf.parse( @@ -51,8 +52,9 @@ def test_mat_parse(caplog: pytest.LogCaptureFixture) -> None: Keyvalues('scale', '0.3645'), Keyvalues('material', 'dev/devmeasuregeneric01'), Keyvalues('rotation', '90'), + Keyvalues('repeat', '4'), ]) - ) == MaterialConf('dev/devmeasuregeneric01', 0.3645, QuarterRot.CCW) + ) == MaterialConf('dev/devmeasuregeneric01', 0.3645, QuarterRot.CCW, 4) assert not caplog.records @@ -65,4 +67,16 @@ def test_mat_parse_warnings(caplog: pytest.LogCaptureFixture) -> None: ]) ) == MaterialConf('dev/devmeasuregeneric01', 1.0, QuarterRot.NONE) assert any(record.levelname == 'WARNING' for record in caplog.records) + assert 'Material scale should be positive' in caplog.text + caplog.clear() + + with caplog.at_level(logging.WARNING): + assert MaterialConf.parse( + Keyvalues('name', [ + Keyvalues('repeat', '0'), + Keyvalues('material', 'dev/devmeasuregeneric01'), + ]) + ) == MaterialConf('dev/devmeasuregeneric01', 1.0, QuarterRot.NONE, 1) + assert any(record.levelname == 'WARNING' for record in caplog.records) + assert 'Material repeat limit should be positive' in caplog.text caplog.clear()