Expand procedural terrain generation
|
|
@ -94,11 +94,13 @@ func _validate_generated_region(
|
|||
assert(_placement_count(placements, "chunk_0001") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0002") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0003") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0016") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0004") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0005") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0006") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0007") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0008") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0013") >= 1)
|
||||
assert(_placement_count(placements, "chunk_0009") == 1)
|
||||
assert(_placement_count(placements, "chunk_0010") == 4)
|
||||
assert(
|
||||
|
|
@ -350,7 +352,9 @@ func _validate_biome_catalog(
|
|||
var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO)
|
||||
var biome_id := region.get_biome_at(coordinate)
|
||||
var tags: PackedStringArray = record.get("tags", PackedStringArray())
|
||||
if "grass" in tags or "sand" in tags:
|
||||
if "mixed_surface" in tags:
|
||||
assert(biome_id == &"")
|
||||
elif "grass" in tags or "sand" in tags:
|
||||
assert(biome_id != &"")
|
||||
var biome := catalog.definition_for_id(biome_id)
|
||||
assert(biome != null and biome.supports_chunk_tags(tags))
|
||||
|
|
@ -465,11 +469,14 @@ func _validate_authored_chunk_surfaces(
|
|||
) -> void:
|
||||
var generated := generator.get_generated_chunks_root()
|
||||
var found_beach := false
|
||||
var found_stream := false
|
||||
var found_alternate_stream := false
|
||||
var found_pond := false
|
||||
var found_grass_ocean_edge := false
|
||||
var found_grass_beach_transition := false
|
||||
var found_grass_ocean_corner := false
|
||||
var found_beach_ocean_corner := false
|
||||
var found_grass_sand_diagonal := false
|
||||
for chunk_root: Node in generated.get_children():
|
||||
if chunk_root.name.begins_with("chunk_0002r"):
|
||||
var beach := chunk_root.find_child(
|
||||
|
|
@ -479,6 +486,27 @@ func _validate_authored_chunk_surfaces(
|
|||
var material := beach.get_active_material(0)
|
||||
assert(material != null and material.resource_name == "sand")
|
||||
found_beach = true
|
||||
elif (
|
||||
chunk_root.name.begins_with("chunk_0003r")
|
||||
or chunk_root.name.begins_with("chunk_0016r")
|
||||
):
|
||||
var stream_id := (
|
||||
&"chunk_0016"
|
||||
if chunk_root.name.begins_with("chunk_0016r")
|
||||
else &"chunk_0003"
|
||||
)
|
||||
var stream := chunk_root.find_child(
|
||||
String(stream_id), true, false
|
||||
) as MeshInstance3D
|
||||
assert(stream != null and stream.mesh != null)
|
||||
var stream_materials := _material_names(stream)
|
||||
assert("dirt" in stream_materials)
|
||||
assert("grass_lite" in stream_materials)
|
||||
assert("dirt_wall" in stream_materials)
|
||||
if stream_id == &"chunk_0016":
|
||||
found_alternate_stream = true
|
||||
else:
|
||||
found_stream = true
|
||||
elif chunk_root.name.begins_with("chunk_0004r"):
|
||||
var pond := chunk_root.find_child(
|
||||
"chunk_0004", true, false
|
||||
|
|
@ -532,12 +560,24 @@ func _validate_authored_chunk_surfaces(
|
|||
var beach_corner_materials := _material_names(beach_ocean_corner)
|
||||
assert("sand" in beach_corner_materials)
|
||||
found_beach_ocean_corner = true
|
||||
elif chunk_root.name.begins_with("chunk_0013r"):
|
||||
var grass_sand_diagonal := chunk_root.find_child(
|
||||
"chunk_0013", true, false
|
||||
) as MeshInstance3D
|
||||
assert(grass_sand_diagonal != null and grass_sand_diagonal.mesh != null)
|
||||
var diagonal_materials := _material_names(grass_sand_diagonal)
|
||||
assert("grass_lite" in diagonal_materials)
|
||||
assert("sand" in diagonal_materials)
|
||||
found_grass_sand_diagonal = true
|
||||
assert(found_beach)
|
||||
assert(found_stream)
|
||||
assert(found_alternate_stream)
|
||||
assert(found_pond)
|
||||
assert(found_grass_ocean_edge)
|
||||
assert(found_grass_beach_transition)
|
||||
assert(found_grass_ocean_corner)
|
||||
assert(found_beach_ocean_corner)
|
||||
assert(found_grass_sand_diagonal)
|
||||
|
||||
|
||||
func _material_names(mesh_instance: MeshInstance3D) -> PackedStringArray:
|
||||
|
|
|
|||
|
|
@ -122,4 +122,5 @@ func _make_generator() -> TerrainChunkGenerator:
|
|||
"chunk_0008",
|
||||
]
|
||||
)
|
||||
generator.grass_sand_smoothing_enabled = true
|
||||
return generator
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ build_collision = true
|
|||
show_chunk_labels = false
|
||||
force_center_chunk_id = &"chunk_spawn"
|
||||
required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004", "chunk_0005", "chunk_0006", "chunk_0007", "chunk_0008")
|
||||
grass_sand_smoothing_enabled = true
|
||||
elevated_cliff_feature_enabled = true
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("4_player")]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ const EXPECTED_IDS: Array[String] = [
|
|||
"chunk_0010",
|
||||
"chunk_0011",
|
||||
"chunk_0012",
|
||||
"chunk_0013",
|
||||
"chunk_0014",
|
||||
"chunk_0015",
|
||||
"chunk_0016",
|
||||
"chunk_spawn",
|
||||
]
|
||||
const REQUIRED_IDS: Array[String] = [
|
||||
|
|
@ -32,7 +36,7 @@ const REQUIRED_IDS: Array[String] = [
|
|||
]
|
||||
const TEST_SEED := 13001
|
||||
const CONNECTOR_STRESS_SEED := 287
|
||||
const EXPECTED_SOLVER_VARIANT_COUNT := 31
|
||||
const EXPECTED_SOLVER_VARIANT_COUNT := 35
|
||||
const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = {
|
||||
&"chunk_0000": 4,
|
||||
&"chunk_0001": 4,
|
||||
|
|
@ -47,6 +51,10 @@ const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = {
|
|||
&"chunk_0010": 4,
|
||||
&"chunk_0011": 4,
|
||||
&"chunk_0012": 4,
|
||||
&"chunk_0013": 4,
|
||||
&"chunk_0014": 4,
|
||||
&"chunk_0015": 4,
|
||||
&"chunk_0016": 4,
|
||||
&"chunk_spawn": 1,
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +68,7 @@ func _initialize() -> void:
|
|||
func _run() -> void:
|
||||
_validate_catalog()
|
||||
_validate_profiles()
|
||||
_validate_second_tier_coastal_layout()
|
||||
_validate_generation()
|
||||
_finish()
|
||||
|
||||
|
|
@ -87,6 +96,10 @@ func _validate_catalog() -> void:
|
|||
var grass_beach_transition := CATALOG.definition_for_id(&"chunk_0006")
|
||||
var grass_ocean_corner := CATALOG.definition_for_id(&"chunk_0007")
|
||||
var beach_ocean_corner := CATALOG.definition_for_id(&"chunk_0008")
|
||||
var grass_sand_diagonal := CATALOG.definition_for_id(&"chunk_0013")
|
||||
var cliff_sea_edge := CATALOG.definition_for_id(&"chunk_0014")
|
||||
var cliff_sea_corner := CATALOG.definition_for_id(&"chunk_0015")
|
||||
var stream_variant := CATALOG.definition_for_id(&"chunk_0016")
|
||||
var cliff_top := CATALOG.definition_for_id(&"chunk_0009")
|
||||
var cliff_corner := CATALOG.definition_for_id(&"chunk_0010")
|
||||
var cliff_edge := CATALOG.definition_for_id(&"chunk_0011")
|
||||
|
|
@ -101,6 +114,10 @@ func _validate_catalog() -> void:
|
|||
and grass_beach_transition != null
|
||||
and grass_ocean_corner != null
|
||||
and beach_ocean_corner != null
|
||||
and grass_sand_diagonal != null
|
||||
and cliff_sea_edge != null
|
||||
and cliff_sea_corner != null
|
||||
and stream_variant != null
|
||||
and cliff_top != null
|
||||
and cliff_corner != null
|
||||
and cliff_edge != null
|
||||
|
|
@ -117,6 +134,10 @@ func _validate_catalog() -> void:
|
|||
and grass_beach_transition != null
|
||||
and grass_ocean_corner != null
|
||||
and beach_ocean_corner != null
|
||||
and grass_sand_diagonal != null
|
||||
and cliff_sea_edge != null
|
||||
and cliff_sea_corner != null
|
||||
and stream_variant != null
|
||||
and cliff_top != null
|
||||
and cliff_corner != null
|
||||
and cliff_edge != null
|
||||
|
|
@ -235,6 +256,48 @@ func _validate_catalog() -> void:
|
|||
),
|
||||
"The beach corner must join two straight beach ocean edges.",
|
||||
)
|
||||
_check(
|
||||
grass_sand_diagonal.must_be_interior
|
||||
and not grass_sand_diagonal.participates_in_base_solver
|
||||
and "mixed_surface" in grass_sand_diagonal.tags,
|
||||
(
|
||||
"The grass-sand diagonal must remain an inland post-process "
|
||||
+ "mixed-surface chunk."
|
||||
),
|
||||
)
|
||||
_check(
|
||||
grass_sand_diagonal.surface_tags_for_edge(
|
||||
TerrainChunkTopology.Edge.NORTH
|
||||
) == PackedStringArray(["sand"])
|
||||
and grass_sand_diagonal.surface_tags_for_edge(
|
||||
TerrainChunkTopology.Edge.WEST
|
||||
) == PackedStringArray(["sand"])
|
||||
and grass_sand_diagonal.surface_tags_for_edge(
|
||||
TerrainChunkTopology.Edge.EAST
|
||||
) == PackedStringArray(["grass"])
|
||||
and grass_sand_diagonal.surface_tags_for_edge(
|
||||
TerrainChunkTopology.Edge.SOUTH
|
||||
) == PackedStringArray(["grass"]),
|
||||
"The diagonal's authored edges must retain two sand and two grass sides.",
|
||||
)
|
||||
var stream := CATALOG.definition_for_id(&"chunk_0003")
|
||||
_check(
|
||||
stream != null
|
||||
and is_equal_approx(stream.selection_weight, 0.3)
|
||||
and is_equal_approx(stream_variant.selection_weight, 0.3)
|
||||
and stream_variant.tags == stream.tags
|
||||
and stream_variant.allowed_neighbor_tags
|
||||
== stream.allowed_neighbor_tags
|
||||
and stream_variant.water_inlet_edges == stream.water_inlet_edges
|
||||
and stream_variant.water_outlet_edges == stream.water_outlet_edges
|
||||
and stream_variant.water_surface_size == stream.water_surface_size
|
||||
and stream_variant.water_surface_offset
|
||||
== stream.water_surface_offset,
|
||||
(
|
||||
"Both stream visuals must share one combined selection weight "
|
||||
+ "and identical terrain/water behavior."
|
||||
),
|
||||
)
|
||||
for cliff_definition: TerrainChunkDefinition in [
|
||||
cliff_top,
|
||||
cliff_corner,
|
||||
|
|
@ -248,6 +311,43 @@ func _validate_catalog() -> void:
|
|||
and cliff_definition.base_layer_mesh_name == &"chunk_0000",
|
||||
"Every second-tier piece must overlay level-one grass inland.",
|
||||
)
|
||||
for coastal_cliff: TerrainChunkDefinition in [
|
||||
cliff_sea_edge,
|
||||
cliff_sea_corner,
|
||||
]:
|
||||
_check(
|
||||
coastal_cliff.overlay_only
|
||||
and coastal_cliff.base_layer_scene == null
|
||||
and not coastal_cliff.must_be_interior
|
||||
and coastal_cliff.prefers_map_boundary
|
||||
and "coast" in coastal_cliff.tags
|
||||
and "elevation_2_sea_edge" in coastal_cliff.tags,
|
||||
(
|
||||
"Each second-tier sea piece must be a complete coastal "
|
||||
+ "overlay without an inland grass base."
|
||||
),
|
||||
)
|
||||
_check(
|
||||
cliff_sea_edge.ocean_facing_edges
|
||||
== (1 << int(TerrainChunkTopology.Edge.EAST)),
|
||||
"The second-tier sea edge must face east before rotation.",
|
||||
)
|
||||
_check(
|
||||
cliff_sea_corner.ocean_facing_edges
|
||||
== (
|
||||
(1 << int(TerrainChunkTopology.Edge.EAST))
|
||||
| (1 << int(TerrainChunkTopology.Edge.SOUTH))
|
||||
),
|
||||
"The second-tier sea corner must face east and south before rotation.",
|
||||
)
|
||||
_check(
|
||||
cliff_sea_corner.minimum_required_neighbors == 2
|
||||
and (
|
||||
"elevation_2_sea_edge"
|
||||
in cliff_sea_corner.required_neighbor_tags
|
||||
),
|
||||
"The second-tier sea corner must join two straight sea edges.",
|
||||
)
|
||||
_check(
|
||||
cliff_top.minimum_required_neighbors == 4
|
||||
and "elevation_2" in cliff_top.required_neighbor_tags,
|
||||
|
|
@ -293,6 +393,91 @@ func _validate_profiles() -> void:
|
|||
)
|
||||
|
||||
|
||||
func _validate_second_tier_coastal_layout() -> void:
|
||||
var generator := TerrainChunkGenerator.new()
|
||||
generator.catalog = CATALOG
|
||||
generator.grid_size = Vector2i(4, 4)
|
||||
generator.generate_on_ready = false
|
||||
generator.build_collision = false
|
||||
generator.force_center_chunk_id = &""
|
||||
root.add_child(generator)
|
||||
var keys := PackedStringArray()
|
||||
for row: int in generator.grid_size.y:
|
||||
for column: int in generator.grid_size.x:
|
||||
var at_north := row == 0
|
||||
var at_east := column == generator.grid_size.x - 1
|
||||
var at_south := row == generator.grid_size.y - 1
|
||||
var at_west := column == 0
|
||||
if at_north and at_west:
|
||||
keys.append("chunk_0015@2")
|
||||
elif at_north and at_east:
|
||||
keys.append("chunk_0015@1")
|
||||
elif at_south and at_east:
|
||||
keys.append("chunk_0015@0")
|
||||
elif at_south and at_west:
|
||||
keys.append("chunk_0015@3")
|
||||
elif at_north:
|
||||
keys.append("chunk_0014@1")
|
||||
elif at_east:
|
||||
keys.append("chunk_0014@0")
|
||||
elif at_south:
|
||||
keys.append("chunk_0014@3")
|
||||
elif at_west:
|
||||
keys.append("chunk_0014@2")
|
||||
else:
|
||||
keys.append("chunk_0009@0")
|
||||
_check(
|
||||
generator.generate_from_placement_keys(keys),
|
||||
(
|
||||
"The second-tier sea edge and corner must form a complete "
|
||||
+ "raised coastal perimeter."
|
||||
),
|
||||
)
|
||||
if generator.placement_keys().size() == keys.size():
|
||||
_check(
|
||||
_all_neighbor_edges_match(generator),
|
||||
"Every raised coastal-perimeter seam must match.",
|
||||
)
|
||||
_check(
|
||||
_count_placements(generator.placement_keys(), "chunk_0014") == 8
|
||||
and _count_placements(
|
||||
generator.placement_keys(), "chunk_0015"
|
||||
) == 4,
|
||||
"The raised coastal perimeter must use its straight and corner pieces.",
|
||||
)
|
||||
for record: Dictionary in generator.placement_records():
|
||||
var stable_id: StringName = record.get("stable_id", &"")
|
||||
if stable_id not in [&"chunk_0014", &"chunk_0015"]:
|
||||
continue
|
||||
var coordinate: Vector2i = record.get(
|
||||
"coordinate", Vector2i.ZERO
|
||||
)
|
||||
var variant := _find_variant(
|
||||
generator,
|
||||
stable_id,
|
||||
int(record.get("rotation_quarters", 0)),
|
||||
)
|
||||
_check(
|
||||
variant != null
|
||||
and generator._variant_respects_ocean_boundary(
|
||||
variant, coordinate
|
||||
),
|
||||
"Every second-tier sea opening must face outside the terrain grid.",
|
||||
)
|
||||
var generated := generator.get_generated_chunks_root()
|
||||
if generated != null:
|
||||
for child: Node in generated.get_children():
|
||||
var stable_id := StringName(
|
||||
child.get_meta(&"terrain_chunk_id", &"")
|
||||
)
|
||||
if stable_id in [&"chunk_0014", &"chunk_0015"]:
|
||||
_check(
|
||||
child.get_node_or_null("TerrainBaseLayer") == null,
|
||||
"Second-tier sea pieces must not render inland grass over ocean.",
|
||||
)
|
||||
generator.free()
|
||||
|
||||
|
||||
func _validate_generation() -> void:
|
||||
var first := _make_generator()
|
||||
root.add_child(first)
|
||||
|
|
@ -308,6 +493,10 @@ func _validate_generation() -> void:
|
|||
_placements_contain(first_keys, stable_id),
|
||||
"Generated diagnostic must contain %s." % stable_id,
|
||||
)
|
||||
_check(
|
||||
_placements_contain(first_keys, "chunk_0013"),
|
||||
"Generated terrain must replace sharp grass-sand corners with diagonals.",
|
||||
)
|
||||
_check(
|
||||
_all_neighbor_edges_match(first),
|
||||
"Every generated neighboring edge must match.",
|
||||
|
|
@ -416,8 +605,12 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
)
|
||||
|
||||
var stream := CATALOG.definition_for_id(&"chunk_0003")
|
||||
_check(stream != null, "The stream definition must be available.")
|
||||
if stream == null:
|
||||
var alternate_stream := CATALOG.definition_for_id(&"chunk_0016")
|
||||
_check(
|
||||
stream != null and alternate_stream != null,
|
||||
"Both stream visual definitions must be available.",
|
||||
)
|
||||
if stream == null or alternate_stream == null:
|
||||
return
|
||||
for quarter_turns: int in 4:
|
||||
var stream_variant := _find_variant(
|
||||
|
|
@ -429,7 +622,16 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
stream_variant != null,
|
||||
"The stream must support rotation %d." % quarter_turns,
|
||||
)
|
||||
if stream_variant == null:
|
||||
var alternate_variant := _find_variant(
|
||||
generator,
|
||||
&"chunk_0016",
|
||||
quarter_turns,
|
||||
)
|
||||
_check(
|
||||
alternate_variant != null,
|
||||
"The alternate stream must support rotation %d." % quarter_turns,
|
||||
)
|
||||
if stream_variant == null or alternate_variant == null:
|
||||
continue
|
||||
var expected_inlet := 1 << int(TerrainChunkTopology.rotated_edge(
|
||||
TerrainChunkTopology.Edge.NORTH,
|
||||
|
|
@ -449,6 +651,25 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
== expected_outlet,
|
||||
"The rotated stream outlet must follow its authored orientation.",
|
||||
)
|
||||
_check(
|
||||
alternate_variant.rotated_edge_mask(
|
||||
alternate_stream.water_inlet_edges
|
||||
) == expected_inlet
|
||||
and alternate_variant.rotated_edge_mask(
|
||||
alternate_stream.water_outlet_edges
|
||||
) == expected_outlet,
|
||||
"The alternate stream connectors must rotate exactly like the original.",
|
||||
)
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
var edge := edge_value as TerrainChunkTopology.Edge
|
||||
_check(
|
||||
stream_variant.profile(edge).signature(
|
||||
TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION
|
||||
) == alternate_variant.profile(edge).signature(
|
||||
TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION
|
||||
),
|
||||
"Both stream visuals must expose identical terrain edge profiles.",
|
||||
)
|
||||
|
||||
var beach := CATALOG.definition_for_id(&"chunk_0002")
|
||||
_check(beach != null, "The beach definition must be available.")
|
||||
|
|
@ -611,6 +832,62 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
),
|
||||
"The beach corner's west side must join a rotated straight beach.",
|
||||
)
|
||||
var cliff_top_zero := _find_variant(generator, &"chunk_0009", 0)
|
||||
var cliff_sea_edge_zero := _find_variant(generator, &"chunk_0014", 0)
|
||||
var cliff_sea_edge_south := _find_variant(generator, &"chunk_0014", 3)
|
||||
var cliff_sea_corner_zero := _find_variant(generator, &"chunk_0015", 0)
|
||||
_check(
|
||||
cliff_top_zero != null
|
||||
and cliff_sea_edge_zero != null
|
||||
and cliff_sea_edge_south != null
|
||||
and cliff_sea_corner_zero != null,
|
||||
"The second-tier sea edge variants must be available.",
|
||||
)
|
||||
if (
|
||||
cliff_top_zero != null
|
||||
and cliff_sea_edge_zero != null
|
||||
and cliff_sea_edge_south != null
|
||||
and cliff_sea_corner_zero != null
|
||||
):
|
||||
_check(
|
||||
cliff_sea_edge_zero.profile(
|
||||
TerrainChunkTopology.Edge.EAST
|
||||
).points.is_empty(),
|
||||
"The second-tier sea edge must remain open on its ocean side.",
|
||||
)
|
||||
_check(
|
||||
cliff_sea_corner_zero.profile(
|
||||
TerrainChunkTopology.Edge.EAST
|
||||
).points.is_empty()
|
||||
and cliff_sea_corner_zero.profile(
|
||||
TerrainChunkTopology.Edge.SOUTH
|
||||
).points.is_empty(),
|
||||
"The second-tier sea corner must retain both ocean openings.",
|
||||
)
|
||||
_check(
|
||||
generator._edges_are_compatible(
|
||||
cliff_sea_edge_zero,
|
||||
TerrainChunkTopology.Edge.WEST,
|
||||
cliff_top_zero,
|
||||
TerrainChunkTopology.Edge.EAST,
|
||||
),
|
||||
"The second-tier sea edge must join the raised grass top inland.",
|
||||
)
|
||||
_check(
|
||||
generator._edges_are_compatible(
|
||||
cliff_sea_corner_zero,
|
||||
TerrainChunkTopology.Edge.NORTH,
|
||||
cliff_sea_edge_zero,
|
||||
TerrainChunkTopology.Edge.SOUTH,
|
||||
)
|
||||
and generator._edges_are_compatible(
|
||||
cliff_sea_corner_zero,
|
||||
TerrainChunkTopology.Edge.WEST,
|
||||
cliff_sea_edge_south,
|
||||
TerrainChunkTopology.Edge.EAST,
|
||||
),
|
||||
"The second-tier sea corner must join two straight sea edges.",
|
||||
)
|
||||
var flat_grass := _find_variant(generator, &"chunk_0000", 0)
|
||||
var flat_sand := _find_variant(generator, &"chunk_0001", 0)
|
||||
_check(
|
||||
|
|
@ -649,11 +926,57 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
),
|
||||
"No transition rotation may accept sand behind it.",
|
||||
)
|
||||
for quarter_turns: int in 4:
|
||||
var diagonal := _find_variant(
|
||||
generator,
|
||||
&"chunk_0013",
|
||||
quarter_turns,
|
||||
)
|
||||
_check(
|
||||
diagonal != null,
|
||||
"The grass-sand diagonal must support rotation %d."
|
||||
% quarter_turns,
|
||||
)
|
||||
if diagonal == null:
|
||||
continue
|
||||
for source_edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
var source_edge := source_edge_value as TerrainChunkTopology.Edge
|
||||
var edge := TerrainChunkTopology.rotated_edge(
|
||||
source_edge,
|
||||
quarter_turns,
|
||||
)
|
||||
var neighbor_edge := TerrainChunkTopology.opposite_edge(edge)
|
||||
var expects_sand := source_edge in [
|
||||
TerrainChunkTopology.Edge.NORTH,
|
||||
TerrainChunkTopology.Edge.WEST,
|
||||
]
|
||||
var expected := flat_sand if expects_sand else flat_grass
|
||||
var rejected := flat_grass if expects_sand else flat_sand
|
||||
_check(
|
||||
generator._edges_are_compatible(
|
||||
diagonal,
|
||||
edge,
|
||||
expected,
|
||||
neighbor_edge,
|
||||
),
|
||||
"Each rotated diagonal edge must accept its authored surface.",
|
||||
)
|
||||
_check(
|
||||
not generator._edges_are_compatible(
|
||||
diagonal,
|
||||
edge,
|
||||
rejected,
|
||||
neighbor_edge,
|
||||
),
|
||||
"No rotated diagonal edge may accept the opposite surface.",
|
||||
)
|
||||
for stable_id: StringName in [
|
||||
&"chunk_0005",
|
||||
&"chunk_0006",
|
||||
&"chunk_0007",
|
||||
&"chunk_0008",
|
||||
&"chunk_0014",
|
||||
&"chunk_0015",
|
||||
]:
|
||||
for quarter_turns: int in 4:
|
||||
var coast_variant := _find_variant(
|
||||
|
|
@ -674,15 +997,12 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
),
|
||||
"%s must never place its ocean edge inside the map." % stable_id,
|
||||
)
|
||||
if stable_id != &"chunk_0007" and stable_id != &"chunk_0008":
|
||||
var coast_definition := CATALOG.definition_for_id(stable_id)
|
||||
if coast_definition == null:
|
||||
continue
|
||||
var boundary_coordinate := Vector2i(3, 3)
|
||||
var ocean_edges := coast_variant.rotated_edge_mask(
|
||||
(
|
||||
grass_ocean_corner.ocean_facing_edges
|
||||
if stable_id == &"chunk_0007"
|
||||
else beach_ocean_corner.ocean_facing_edges
|
||||
)
|
||||
coast_definition.ocean_facing_edges
|
||||
)
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
if (ocean_edges & (1 << edge_value)) == 0:
|
||||
|
|
@ -701,15 +1021,15 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
coast_variant,
|
||||
boundary_coordinate,
|
||||
),
|
||||
"Each coast corner rotation must fit its matching map corner.",
|
||||
"Each coast rotation must fit its matching open map boundary.",
|
||||
)
|
||||
|
||||
|
||||
func _validate_generation_summary(generator: TerrainChunkGenerator) -> void:
|
||||
var summary := generator._build_summary()
|
||||
_check(
|
||||
int(summary.get("variant_count", 0)) == 53,
|
||||
"The current catalog must expose all 53 authored rotations.",
|
||||
int(summary.get("variant_count", 0)) == 69,
|
||||
"The current catalog must expose all 69 authored rotations.",
|
||||
)
|
||||
_check(
|
||||
int(summary.get("solver_variant_count", 0))
|
||||
|
|
@ -762,6 +1082,7 @@ func _make_generator() -> TerrainChunkGenerator:
|
|||
generator.build_collision = false
|
||||
generator.force_center_chunk_id = &"chunk_spawn"
|
||||
generator.required_chunk_ids = PackedStringArray(REQUIRED_IDS)
|
||||
generator.grass_sand_smoothing_enabled = true
|
||||
generator.maximum_backtracks = 100000
|
||||
return generator
|
||||
|
||||
|
|
|
|||
|
|
@ -282,15 +282,21 @@ def _discover_chunks(tolerance: float) -> list[ChunkSource]:
|
|||
# Coastline meshes may intentionally stop before the positive-X edge,
|
||||
# leaving the surrounding ocean visible. Positive X is the canonical
|
||||
# authored ocean direction; Godot supplies the quarter-turn variants.
|
||||
# Elevated inland cliff overlays intentionally stop at their downhill
|
||||
# edge. The runtime places them over the existing flat grass chunk, so
|
||||
# these omissions are not coastline openings or holes in the final cell.
|
||||
layered_inland_cliff = "cliff_2" in label
|
||||
allow_open_east_edge = "ocean_edge" in label or layered_inland_cliff
|
||||
# Elevated inland cliff overlays also stop at their downhill edge,
|
||||
# while second-tier sea pieces carry that edge down to ocean depth.
|
||||
layered_cliff = "cliff_2" in label
|
||||
allow_open_east_edge = (
|
||||
"ocean_edge" in label
|
||||
or "sea_edge" in label
|
||||
or "sea_corner" in label
|
||||
or layered_cliff
|
||||
)
|
||||
# Corner pieces additionally leave canonical negative Y open. Their
|
||||
# two marked ocean edges rotate together at runtime.
|
||||
# two marked outer edges rotate together at runtime.
|
||||
allow_open_south_edge = (
|
||||
"ocean_edge_corner" in label or layered_inland_cliff
|
||||
"ocean_edge_corner" in label
|
||||
or "sea_corner" in label
|
||||
or (layered_cliff and "corner" in label)
|
||||
)
|
||||
bound_problems = _validate_bounds(
|
||||
bounds,
|
||||
|
|
|
|||
|
|
@ -17,4 +17,5 @@ script = ExtResource("1_definition")
|
|||
stable_id = &"biome_coast"
|
||||
label = "coast"
|
||||
required_chunk_tags = PackedStringArray("sand")
|
||||
forbidden_chunk_tags = PackedStringArray("mixed_surface")
|
||||
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_sand_tree")])
|
||||
|
|
|
|||
|
|
@ -27,6 +27,6 @@ script = ExtResource("1_definition")
|
|||
stable_id = &"biome_forest"
|
||||
label = "forest"
|
||||
required_chunk_tags = PackedStringArray("grass")
|
||||
forbidden_chunk_tags = PackedStringArray("spawn")
|
||||
forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface")
|
||||
region_weight = 1.4
|
||||
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])
|
||||
|
|
|
|||
|
|
@ -27,6 +27,6 @@ script = ExtResource("1_definition")
|
|||
stable_id = &"biome_pine_forest"
|
||||
label = "pine forest"
|
||||
required_chunk_tags = PackedStringArray("grass")
|
||||
forbidden_chunk_tags = PackedStringArray("spawn")
|
||||
forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface")
|
||||
region_weight = 1.2
|
||||
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])
|
||||
|
|
|
|||
|
|
@ -24,5 +24,6 @@ script = ExtResource("1_definition")
|
|||
stable_id = &"biome_plains"
|
||||
label = "plains"
|
||||
required_chunk_tags = PackedStringArray("grass")
|
||||
forbidden_chunk_tags = PackedStringArray("mixed_surface")
|
||||
region_weight = 1.0
|
||||
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])
|
||||
|
|
|
|||
BIN
world/generation/chunks/assets/chunk_0013.glb
Normal file
45
world/generation/chunks/assets/chunk_0013.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dom2fvsn0s1wa"
|
||||
path="res://.godot/imported/chunk_0013.glb-64cf2d2bb9c9020fa3d06004b1963f09.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0013.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0013.glb-64cf2d2bb9c9020fa3d06004b1963f09.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
BIN
world/generation/chunks/assets/chunk_0013_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dct65l02vr54q"
|
||||
path.s3tc="res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0013_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.s3tc.ctex", "res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0013_sand.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
45
world/generation/chunks/assets/chunk_0013_sand.png.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://btjhnb6owbjj2"
|
||||
path.s3tc="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "adfa66b8fe9da7f3450d3ddfd432b86a"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0013_sand.png"
|
||||
dest_files=["res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex", "res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0014.glb
Normal file
45
world/generation/chunks/assets/chunk_0014.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://50aolv5pg8d2"
|
||||
path="res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0014.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
BIN
world/generation/chunks/assets/chunk_0014_cliff_wall.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://6sd1uj06ti8f"
|
||||
path.s3tc="res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "fc97184f537cc23f83a731327e10c836"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0014_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.s3tc.ctex", "res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0014_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6fftn8x2o1dd"
|
||||
path.s3tc="res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0014_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.s3tc.ctex", "res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0015.glb
Normal file
45
world/generation/chunks/assets/chunk_0015.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d26fd6sjsh50x"
|
||||
path="res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0015.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
BIN
world/generation/chunks/assets/chunk_0015_cliff_wall.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cpce75ti5cwnw"
|
||||
path.s3tc="res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "fc97184f537cc23f83a731327e10c836"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0015_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.s3tc.ctex", "res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0015_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://pmaf2oetdqln"
|
||||
path.s3tc="res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0015_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.s3tc.ctex", "res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0016.glb
Normal file
45
world/generation/chunks/assets/chunk_0016.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://te1yrm2uhin1"
|
||||
path="res://.godot/imported/chunk_0016.glb-6904d8b3583dd301bda1835c501225f3.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0016.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0016.glb-6904d8b3583dd301bda1835c501225f3.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
BIN
world/generation/chunks/assets/chunk_0016_dirt.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
45
world/generation/chunks/assets/chunk_0016_dirt.png.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dxy8u3vyyu2on"
|
||||
path.s3tc="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "07d90b3c46a4c3d0c172bed143dc9436"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0016_dirt.png"
|
||||
dest_files=["res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex", "res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0016_dirt_wall.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cn8ljtpbsg253"
|
||||
path.s3tc="res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "b5430dba0b52db87d30bae85ae640b09"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0016_dirt_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.s3tc.ctex", "res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
world/generation/chunks/assets/chunk_0016_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dn84haedw08rx"
|
||||
path.s3tc="res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0016_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.s3tc.ctex", "res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
|
@ -9,7 +9,7 @@ stable_id = &"chunk_0003"
|
|||
label = "stream"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0003"
|
||||
selection_weight = 0.6
|
||||
selection_weight = 0.3
|
||||
tags = PackedStringArray("land", "walkable", "fresh_water", "river", "flowing")
|
||||
allowed_neighbor_tags = PackedStringArray("grass")
|
||||
preferred_neighbor_tags = PackedStringArray("grass")
|
||||
|
|
|
|||
28
world/generation/chunks/definitions/chunk_0013.tres
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0013.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0013"
|
||||
label = "grass-sand diagonal"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0013"
|
||||
participates_in_base_solver = false
|
||||
allowed_rotation_mask = 15
|
||||
selection_weight = 0.8
|
||||
maximum_placements = 16
|
||||
tags = PackedStringArray("land", "walkable", "grass", "sand", "flat", "transition", "mixed_surface")
|
||||
allowed_neighbor_tags = PackedStringArray("grass", "sand")
|
||||
forbidden_neighbor_tags = PackedStringArray("fresh_water", "spawn")
|
||||
north_allowed_neighbor_tags = PackedStringArray("sand")
|
||||
east_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
south_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
west_allowed_neighbor_tags = PackedStringArray("sand")
|
||||
north_surface_tags = PackedStringArray("sand")
|
||||
east_surface_tags = PackedStringArray("grass")
|
||||
south_surface_tags = PackedStringArray("grass")
|
||||
west_surface_tags = PackedStringArray("sand")
|
||||
preferred_neighbor_tags = PackedStringArray("grass", "sand", "transition")
|
||||
must_be_interior = true
|
||||
24
world/generation/chunks/definitions/chunk_0014.tres
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0014.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0014"
|
||||
label = "second-tier grass sea edge"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0014"
|
||||
overlay_only = true
|
||||
selection_weight = 0.28
|
||||
maximum_placements = 32
|
||||
tags = PackedStringArray("land", "walkable", "grass", "coast", "cliff", "edge", "elevation_2", "elevation_2_border", "elevation_2_sea_edge")
|
||||
allowed_neighbor_tags = PackedStringArray("elevation_2")
|
||||
north_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
|
||||
south_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
|
||||
west_allowed_neighbor_tags = PackedStringArray("elevation_2_top")
|
||||
required_neighbor_tags = PackedStringArray("elevation_2_top")
|
||||
minimum_required_neighbors = 1
|
||||
preferred_neighbor_tags = PackedStringArray("elevation_2", "coast")
|
||||
prefers_map_boundary = true
|
||||
ocean_facing_edges = 2
|
||||
23
world/generation/chunks/definitions/chunk_0015.tres
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0015.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0015"
|
||||
label = "second-tier grass sea corner"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0015"
|
||||
overlay_only = true
|
||||
selection_weight = 0.12
|
||||
maximum_placements = 16
|
||||
tags = PackedStringArray("land", "walkable", "grass", "coast", "cliff", "corner", "elevation_2", "elevation_2_border", "elevation_2_sea_edge")
|
||||
allowed_neighbor_tags = PackedStringArray("elevation_2")
|
||||
north_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
|
||||
west_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
|
||||
required_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
|
||||
minimum_required_neighbors = 2
|
||||
preferred_neighbor_tags = PackedStringArray("elevation_2", "coast")
|
||||
prefers_map_boundary = true
|
||||
ocean_facing_edges = 6
|
||||
19
world/generation/chunks/definitions/chunk_0016.tres
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0016.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0016"
|
||||
label = "stream variant"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0016"
|
||||
selection_weight = 0.3
|
||||
tags = PackedStringArray("land", "walkable", "fresh_water", "river", "flowing")
|
||||
allowed_neighbor_tags = PackedStringArray("grass")
|
||||
preferred_neighbor_tags = PackedStringArray("grass")
|
||||
water_inlet_edges = 1
|
||||
water_outlet_edges = 4
|
||||
water_surface_size = Vector2(2.1, 10)
|
||||
water_surface_offset = Vector2(-0.315, 0)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=17 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=21 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_chunk_catalog.gd" id="1_catalog"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0000.tres" id="2_grass"]
|
||||
|
|
@ -16,8 +16,12 @@
|
|||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0010.tres" id="14_cliff_corner"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0011.tres" id="15_cliff_edge"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0012.tres" id="16_cliff_ramp"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0013.tres" id="17_grass_sand_diagonal"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0014.tres" id="18_cliff_sea_edge"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0015.tres" id="19_cliff_sea_corner"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0016.tres" id="20_stream_variant"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_catalog")
|
||||
chunk_size = 10.0
|
||||
definitions = Array[ExtResource("7_definition_script")]([ExtResource("2_grass"), ExtResource("3_sand"), ExtResource("4_beach"), ExtResource("5_stream"), ExtResource("6_pond"), ExtResource("9_grass_ocean_edge"), ExtResource("10_grass_beach_transition"), ExtResource("11_grass_ocean_corner"), ExtResource("12_beach_ocean_corner"), ExtResource("13_cliff_top"), ExtResource("14_cliff_corner"), ExtResource("15_cliff_edge"), ExtResource("16_cliff_ramp"), ExtResource("8_spawn")])
|
||||
definitions = Array[ExtResource("7_definition_script")]([ExtResource("2_grass"), ExtResource("3_sand"), ExtResource("4_beach"), ExtResource("5_stream"), ExtResource("20_stream_variant"), ExtResource("6_pond"), ExtResource("9_grass_ocean_edge"), ExtResource("10_grass_beach_transition"), ExtResource("11_grass_ocean_corner"), ExtResource("12_beach_ocean_corner"), ExtResource("17_grass_sand_diagonal"), ExtResource("13_cliff_top"), ExtResource("14_cliff_corner"), ExtResource("15_cliff_edge"), ExtResource("16_cliff_ramp"), ExtResource("18_cliff_sea_edge"), ExtResource("19_cliff_sea_corner"), ExtResource("8_spawn")])
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ generate_on_ready = false
|
|||
build_collision = true
|
||||
force_center_chunk_id = &"chunk_spawn"
|
||||
required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004", "chunk_0005", "chunk_0006", "chunk_0007", "chunk_0008")
|
||||
grass_sand_smoothing_enabled = true
|
||||
elevated_cliff_feature_enabled = true
|
||||
required_chunk_weight_multiplier = 64.0
|
||||
maximum_backtracks = 100000
|
||||
|
|
|
|||
|
|
@ -38,10 +38,16 @@ func validation_errors() -> PackedStringArray:
|
|||
"%s must define both base-layer scene and mesh name."
|
||||
% definition.stable_id
|
||||
)
|
||||
if definition.overlay_only and definition.base_layer_scene == null:
|
||||
if (
|
||||
definition.overlay_only
|
||||
and definition.base_layer_scene == null
|
||||
and definition.ocean_facing_edges == 0
|
||||
):
|
||||
errors.append(
|
||||
"%s is overlay-only but has no base-layer scene."
|
||||
% definition.stable_id
|
||||
(
|
||||
"%s is an inland overlay but has no base-layer scene."
|
||||
% definition.stable_id
|
||||
)
|
||||
)
|
||||
if definition.allowed_rotation_mask == 0:
|
||||
errors.append("%s allows no rotations." % definition.stable_id)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,14 @@ extends Resource
|
|||
@export var base_layer_scene: PackedScene
|
||||
@export var base_layer_mesh_name: StringName
|
||||
## Overlay-only chunks replace a reserved base-terrain placement after the
|
||||
## ordinary terrain solve. They remain in placement manifests and runtime
|
||||
## output, but do not inflate every cell's global solver domain.
|
||||
## ordinary terrain solve. Inland overlays normally provide base_layer_scene;
|
||||
## complete coastal overlays can instead carry their own descent to sea level.
|
||||
## They remain in placement manifests and runtime output, but do not inflate
|
||||
## every cell's global solver domain.
|
||||
@export var overlay_only := false
|
||||
## Post-process pieces remain available as authored variants and in placement
|
||||
## manifests, but do not participate in the ordinary base-terrain solve.
|
||||
@export var participates_in_base_solver := true
|
||||
@export_group("")
|
||||
@export_flags("0 degrees", "90 degrees", "180 degrees", "270 degrees")
|
||||
var allowed_rotation_mask := 15
|
||||
|
|
@ -38,6 +43,16 @@ var allowed_rotation_mask := 15
|
|||
@export var south_allowed_neighbor_tags := PackedStringArray()
|
||||
@export var west_allowed_neighbor_tags := PackedStringArray()
|
||||
@export_group("")
|
||||
## Optional canonical descriptions of the visible terrain surface at each
|
||||
## edge. Empty edges inherit the definition's ordinary tags. Mixed-surface
|
||||
## chunks use these to prevent a visually grass edge from joining sand (or the
|
||||
## reverse) even when the chunk itself carries both biome tags.
|
||||
@export_group("Directional Surface Rules")
|
||||
@export var north_surface_tags := PackedStringArray()
|
||||
@export var east_surface_tags := PackedStringArray()
|
||||
@export var south_surface_tags := PackedStringArray()
|
||||
@export var west_surface_tags := PackedStringArray()
|
||||
@export_group("")
|
||||
## When non-empty, at least this many in-grid cardinal neighbors must carry one
|
||||
## of the listed tags. This supports statements such as "three sides around the
|
||||
## spawn must be safe grass" without hard-coding a particular chunk ID.
|
||||
|
|
@ -106,6 +121,28 @@ func directional_allowed_neighbor_tags(
|
|||
return PackedStringArray()
|
||||
|
||||
|
||||
func surface_tags_for_edge(
|
||||
edge: TerrainChunkTopology.Edge,
|
||||
) -> PackedStringArray:
|
||||
var edge_tags := directional_surface_tags(edge)
|
||||
return tags if edge_tags.is_empty() else edge_tags
|
||||
|
||||
|
||||
func directional_surface_tags(
|
||||
edge: TerrainChunkTopology.Edge,
|
||||
) -> PackedStringArray:
|
||||
match edge:
|
||||
TerrainChunkTopology.Edge.NORTH:
|
||||
return north_surface_tags
|
||||
TerrainChunkTopology.Edge.EAST:
|
||||
return east_surface_tags
|
||||
TerrainChunkTopology.Edge.SOUTH:
|
||||
return south_surface_tags
|
||||
TerrainChunkTopology.Edge.WEST:
|
||||
return west_surface_tags
|
||||
return PackedStringArray()
|
||||
|
||||
|
||||
func _allows_neighbor_with_tags(
|
||||
neighbor: TerrainChunkDefinition,
|
||||
allowed_tags: PackedStringArray,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ func allows_non_water_neighbor_on_edge(
|
|||
)
|
||||
|
||||
|
||||
func surface_tags(edge: TerrainChunkTopology.Edge) -> PackedStringArray:
|
||||
return definition.surface_tags_for_edge(source_edge(edge))
|
||||
|
||||
|
||||
func topology_signature(quantization: float) -> String:
|
||||
var tokens := PackedStringArray()
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
|
|
@ -44,9 +48,10 @@ func topology_signature(quantization: float) -> String:
|
|||
|
||||
|
||||
func constraint_signature(quantization: float) -> String:
|
||||
return "%s|neighbors:%s|ocean:%d|in:%d|out:%d" % [
|
||||
return "%s|neighbors:%s|surfaces:%s|ocean:%d|in:%d|out:%d" % [
|
||||
topology_signature(quantization),
|
||||
_neighbor_rule_signature(),
|
||||
_surface_rule_signature(),
|
||||
rotated_edge_mask(definition.ocean_facing_edges),
|
||||
rotated_edge_mask(definition.water_inlet_edges),
|
||||
rotated_edge_mask(definition.water_outlet_edges),
|
||||
|
|
@ -65,6 +70,16 @@ func _neighbor_rule_signature() -> String:
|
|||
return "/".join(edge_tokens)
|
||||
|
||||
|
||||
func _surface_rule_signature() -> String:
|
||||
var edge_tokens := PackedStringArray()
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
var edge := edge_value as TerrainChunkTopology.Edge
|
||||
var edge_tags: PackedStringArray = surface_tags(edge).duplicate()
|
||||
edge_tags.sort()
|
||||
edge_tokens.append(",".join(edge_tags))
|
||||
return "/".join(edge_tokens)
|
||||
|
||||
|
||||
func rotated_edge_mask(source_mask: int) -> int:
|
||||
var result := 0
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
|
|
|
|||