274 lines
7.6 KiB
GDScript
274 lines
7.6 KiB
GDScript
extends SceneTree
|
|
|
|
const CalendarSeasonType = preload("res://world/calendar_season.gd")
|
|
const PropCatalog: TerrainPropCatalog = preload(
|
|
"res://world/generation/props/terrain_prop_catalog.tres"
|
|
)
|
|
const RegionScene: PackedScene = preload(
|
|
"res://world/generation/generated_world_region.tscn"
|
|
)
|
|
|
|
const BROADLEAF_IDS: Array[StringName] = [
|
|
&"prop_tree_1",
|
|
&"prop_tree_2",
|
|
&"prop_tree_3",
|
|
&"prop_tree_large",
|
|
]
|
|
const IMPORTED_FOLIAGE_NAMES: Dictionary = {
|
|
&"prop_tree_1": &"leaf_autumn_light",
|
|
&"prop_tree_2": &"leaf_autumn",
|
|
&"prop_tree_3": &"leaf_autumn_dark",
|
|
&"prop_tree_large": &"leaf_autumn_mid",
|
|
&"prop_pine": &"pine_summer",
|
|
&"prop_pine_large": &"pine_autumn",
|
|
&"prop_palm": &"palm_autumn",
|
|
}
|
|
const PROP_PRIMARY_MATERIAL_ROLL_META: StringName = (
|
|
&"terrain_prop_primary_material_roll"
|
|
)
|
|
const PROP_LEGACY_PRIMARY_MATERIAL_INDEX_META: StringName = (
|
|
&"terrain_prop_legacy_primary_material_index"
|
|
)
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred(&"_run")
|
|
|
|
|
|
func _run() -> void:
|
|
assert(PropCatalog.validation_errors().is_empty())
|
|
for stable_id: StringName in BROADLEAF_IDS:
|
|
var definition := PropCatalog.definition_for_id(stable_id)
|
|
assert(definition != null)
|
|
_assert_palette(
|
|
definition.summer_material_variants,
|
|
PackedStringArray([
|
|
"leaf_variant_light",
|
|
"leaf_variant_mid",
|
|
"leaf_variant_dark",
|
|
]),
|
|
[
|
|
Color(0.3387566, 0.7186728, 0, 1),
|
|
Color(0.3146831, 0.4631305, 0, 1),
|
|
Color(0.1712407, 0.2520192, 0, 1),
|
|
],
|
|
)
|
|
_assert_palette(
|
|
definition.fall_material_variants,
|
|
PackedStringArray([
|
|
"leaf_autumn_light",
|
|
"leaf_autumn",
|
|
"leaf_autumn_mid",
|
|
"leaf_autumn_dark",
|
|
]),
|
|
[
|
|
Color(0.8222013, 0.794216, 0, 1),
|
|
Color(0.6186816, 0.4523746, 0, 1),
|
|
Color(0.6841143, 0.3515543, 0, 1),
|
|
Color(0.3853541, 0.00134692, 0, 1),
|
|
],
|
|
)
|
|
assert(
|
|
definition.primary_material_variants_for_season(
|
|
CalendarSeasonType.Season.SPRING
|
|
) == definition.summer_material_variants
|
|
)
|
|
assert(
|
|
definition.primary_material_variants_for_season(
|
|
CalendarSeasonType.Season.WINTER
|
|
) == definition.summer_material_variants
|
|
)
|
|
|
|
_assert_single_seasonal_tree(
|
|
&"prop_pine",
|
|
&"pine_summer",
|
|
Color(0.02171588, 0.2242201, 0.1360403, 1),
|
|
&"pine_autumn",
|
|
Color(0.02388079, 0.1285609, 0, 1),
|
|
)
|
|
_assert_single_seasonal_tree(
|
|
&"prop_pine_large",
|
|
&"pine_summer",
|
|
Color(0.02171588, 0.2242201, 0.1360403, 1),
|
|
&"pine_autumn",
|
|
Color(0.02388079, 0.1285609, 0, 1),
|
|
)
|
|
_assert_single_seasonal_tree(
|
|
&"prop_palm",
|
|
&"palm_summer",
|
|
Color(0.3146831, 0.4631305, 0, 1),
|
|
&"palm_autumn",
|
|
Color(0.2951757, 0.3575695, 0.162893, 1),
|
|
)
|
|
|
|
for stable_id: StringName in IMPORTED_FOLIAGE_NAMES:
|
|
var definition := PropCatalog.definition_for_id(stable_id)
|
|
var instance := definition.packed_scene.instantiate()
|
|
var material_names := PackedStringArray()
|
|
_collect_material_names(instance, material_names)
|
|
assert(IMPORTED_FOLIAGE_NAMES[stable_id] in material_names)
|
|
instance.free()
|
|
|
|
var region := RegionScene.instantiate() as GeneratedWorldRegion
|
|
assert(region != null)
|
|
assert(bool(region.call(
|
|
"_material_name_matches_targets",
|
|
&"leaf_autumn_mid",
|
|
PackedStringArray(["leaf"]),
|
|
)))
|
|
assert(bool(region.call(
|
|
"_material_name_matches_targets",
|
|
&"pine_winter",
|
|
PackedStringArray(["pine"]),
|
|
)))
|
|
assert(bool(region.call(
|
|
"_material_name_matches_targets",
|
|
&"palm_spring",
|
|
PackedStringArray(["palm"]),
|
|
)))
|
|
_validate_palette_roll_mapping(region)
|
|
_validate_primary_roll_random_state(region)
|
|
region.free()
|
|
print("Terrain seasonal material validation: PASS")
|
|
quit()
|
|
|
|
|
|
func _assert_single_seasonal_tree(
|
|
stable_id: StringName,
|
|
summer_name: StringName,
|
|
summer_color: Color,
|
|
fall_name: StringName,
|
|
fall_color: Color,
|
|
) -> void:
|
|
var definition := PropCatalog.definition_for_id(stable_id)
|
|
assert(definition != null)
|
|
_assert_palette(
|
|
definition.summer_material_variants,
|
|
PackedStringArray([summer_name]),
|
|
[summer_color],
|
|
)
|
|
_assert_palette(
|
|
definition.fall_material_variants,
|
|
PackedStringArray([fall_name]),
|
|
[fall_color],
|
|
)
|
|
|
|
|
|
func _assert_palette(
|
|
materials: Array[Material],
|
|
expected_names: PackedStringArray,
|
|
expected_colors: Array[Color],
|
|
) -> void:
|
|
assert(materials.size() == expected_names.size())
|
|
assert(materials.size() == expected_colors.size())
|
|
for index: int in materials.size():
|
|
var material := materials[index] as StandardMaterial3D
|
|
assert(material != null)
|
|
assert(material.resource_name == expected_names[index])
|
|
assert(material.albedo_color.is_equal_approx(expected_colors[index]))
|
|
|
|
|
|
func _validate_palette_roll_mapping(region: GeneratedWorldRegion) -> void:
|
|
var definition := PropCatalog.definition_for_id(&"prop_tree_1")
|
|
assert(definition != null)
|
|
var instance := definition.packed_scene.instantiate()
|
|
var palette := definition.fall_material_variants
|
|
for index: int in palette.size():
|
|
var roll := (float(index) + 0.5) / float(palette.size())
|
|
region.call(
|
|
"_apply_material_variant_for_roll",
|
|
instance,
|
|
definition.variant_material_slot_names,
|
|
palette,
|
|
roll,
|
|
)
|
|
assert(_has_surface_override(instance, palette[index]))
|
|
instance.free()
|
|
|
|
|
|
func _validate_primary_roll_random_state(
|
|
region: GeneratedWorldRegion,
|
|
) -> void:
|
|
var broadleaf := PropCatalog.definition_for_id(&"prop_tree_1")
|
|
var baseline := RandomNumberGenerator.new()
|
|
baseline.seed = 29092026
|
|
var actual := RandomNumberGenerator.new()
|
|
actual.seed = baseline.seed
|
|
var legacy_index := baseline.randi_range(
|
|
0,
|
|
broadleaf.material_variants.size() - 1,
|
|
)
|
|
var selection := region.call(
|
|
"_primary_material_selection",
|
|
broadleaf,
|
|
actual,
|
|
) as Dictionary
|
|
var roll := float(selection.get("roll", -1.0))
|
|
assert(roll >= 0.0 and roll < 1.0)
|
|
assert(int(selection.get("legacy_index", -1)) == legacy_index)
|
|
assert(actual.state == baseline.state)
|
|
var prop := Node3D.new()
|
|
prop.set_meta(PROP_PRIMARY_MATERIAL_ROLL_META, 0.125)
|
|
prop.set_meta(
|
|
PROP_LEGACY_PRIMARY_MATERIAL_INDEX_META,
|
|
legacy_index,
|
|
)
|
|
var summer_roll := float(region.call(
|
|
"_primary_material_roll_for_palette",
|
|
prop,
|
|
broadleaf,
|
|
broadleaf.summer_material_variants,
|
|
))
|
|
assert(
|
|
floori(summer_roll * broadleaf.summer_material_variants.size())
|
|
== legacy_index
|
|
)
|
|
var fall_roll := float(region.call(
|
|
"_primary_material_roll_for_palette",
|
|
prop,
|
|
broadleaf,
|
|
broadleaf.fall_material_variants,
|
|
))
|
|
assert(is_equal_approx(fall_roll, 0.125))
|
|
prop.free()
|
|
|
|
var pine := PropCatalog.definition_for_id(&"prop_pine")
|
|
var pine_random := RandomNumberGenerator.new()
|
|
pine_random.seed = 29092026
|
|
var pine_initial_state := pine_random.state
|
|
var pine_selection := region.call(
|
|
"_primary_material_selection",
|
|
pine,
|
|
pine_random,
|
|
) as Dictionary
|
|
assert(int(pine_selection.get("legacy_index", -2)) == -1)
|
|
assert(pine_random.state == pine_initial_state)
|
|
|
|
|
|
func _has_surface_override(root_node: Node, expected: Material) -> bool:
|
|
var mesh_instance := root_node as MeshInstance3D
|
|
if mesh_instance != null and mesh_instance.mesh != null:
|
|
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
|
if (
|
|
mesh_instance.get_surface_override_material(surface_index)
|
|
== expected
|
|
):
|
|
return true
|
|
for child: Node in root_node.get_children():
|
|
if _has_surface_override(child, expected):
|
|
return true
|
|
return false
|
|
|
|
|
|
func _collect_material_names(
|
|
node: Node,
|
|
material_names: PackedStringArray,
|
|
) -> void:
|
|
var mesh_instance := node as MeshInstance3D
|
|
if mesh_instance != null and mesh_instance.mesh != null:
|
|
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
|
var material := mesh_instance.mesh.surface_get_material(surface_index)
|
|
if material != null:
|
|
material_names.append(material.resource_name)
|
|
for child: Node in node.get_children():
|
|
_collect_material_names(child, material_names)
|