Improve tree gathering and rain occlusion
This commit is contained in:
parent
a7c71213cc
commit
3608be41ab
8 changed files with 165 additions and 2 deletions
56
world/environment/precipitation_occlusion.gd
Normal file
56
world/environment/precipitation_occlusion.gd
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
class_name PrecipitationOcclusion
|
||||
extends RefCounted
|
||||
|
||||
## A secondary visual layer used only when building the local rain height field.
|
||||
## Canopy meshes remain on layer 1 for ordinary cameras.
|
||||
const RENDER_LAYER_NUMBER: int = 20
|
||||
const RENDER_LAYER_MASK: int = 1 << (RENDER_LAYER_NUMBER - 1)
|
||||
const CANOPY_MATERIAL_NAMES: Array[String] = [
|
||||
"leaf",
|
||||
"leaf_light",
|
||||
"leaf_mid",
|
||||
"leaf_dark",
|
||||
"pine",
|
||||
"tree",
|
||||
]
|
||||
|
||||
|
||||
static func mark_canopy_meshes(root_node: Node) -> int:
|
||||
if root_node == null:
|
||||
return 0
|
||||
var marked_count: int = 0
|
||||
var mesh_instance := root_node as MeshInstance3D
|
||||
if mesh_instance != null and _has_canopy_material(mesh_instance):
|
||||
mesh_instance.layers |= RENDER_LAYER_MASK
|
||||
marked_count += 1
|
||||
for child: Node in root_node.get_children():
|
||||
marked_count += mark_canopy_meshes(child)
|
||||
return marked_count
|
||||
|
||||
|
||||
## Generated tree definitions are already explicit terrain metadata, so every
|
||||
## mesh in their visual scene can safely participate. This also supports older
|
||||
## combined tree meshes whose imported material name does not identify leaves.
|
||||
static func mark_tree_meshes(root_node: Node) -> int:
|
||||
if root_node == null:
|
||||
return 0
|
||||
var marked_count: int = 0
|
||||
var mesh_instance := root_node as MeshInstance3D
|
||||
if mesh_instance != null:
|
||||
mesh_instance.layers |= RENDER_LAYER_MASK
|
||||
marked_count += 1
|
||||
for child: Node in root_node.get_children():
|
||||
marked_count += mark_tree_meshes(child)
|
||||
return marked_count
|
||||
|
||||
|
||||
static func _has_canopy_material(mesh_instance: MeshInstance3D) -> bool:
|
||||
if mesh_instance == null or mesh_instance.mesh == null:
|
||||
return false
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var material := mesh_instance.mesh.surface_get_material(surface_index)
|
||||
if material == null:
|
||||
continue
|
||||
if material.resource_name.to_lower() in CANOPY_MATERIAL_NAMES:
|
||||
return true
|
||||
return false
|
||||
|
|
@ -12,6 +12,9 @@ const PlayerStorageInteractionType = preload(
|
|||
const MeshSurfaceAnchorSamplerType = preload(
|
||||
"res://world/generation/mesh_surface_anchor_sampler.gd"
|
||||
)
|
||||
const PrecipitationOcclusionType = preload(
|
||||
"res://world/environment/precipitation_occlusion.gd"
|
||||
)
|
||||
const WATER_BODY_SCENE: PackedScene = preload("res://world/water_body.tscn")
|
||||
const SALT_WATER_MATERIAL: Material = preload(
|
||||
"res://world/materials/stylized_water.tres"
|
||||
|
|
@ -292,9 +295,21 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
_place_spawn_amenities(spawn_position)
|
||||
_configure_fresh_water(records)
|
||||
_configure_diggable_area()
|
||||
# Imported prop instances may finish applying their scene state while the
|
||||
# generated world is assembled. Mark the final decoration tree once every
|
||||
# prop and material variant is in place so rain canopy occlusion persists.
|
||||
_mark_precipitation_occluders()
|
||||
world_generated.emit(_current_seed, summary)
|
||||
|
||||
|
||||
func _mark_precipitation_occluders() -> void:
|
||||
for prop: Node in _decorations.get_children():
|
||||
var prop_group := prop.get_meta(&"terrain_prop_group", &"") as StringName
|
||||
if prop_group not in [&"grass_tree", &"sand_tree"]:
|
||||
continue
|
||||
PrecipitationOcclusionType.mark_tree_meshes(prop)
|
||||
|
||||
|
||||
func _assign_biomes(
|
||||
records: Array[Dictionary],
|
||||
summary: Dictionary,
|
||||
|
|
|
|||
|
|
@ -17,4 +17,3 @@ minimum_visual_scale = 0.65
|
|||
maximum_visual_scale = 1.2
|
||||
collision_radius = 0.5
|
||||
collision_height = 4.0
|
||||
gatherable_surface_material_names = PackedStringArray("wood")
|
||||
|
|
|
|||
|
|
@ -17,4 +17,3 @@ minimum_visual_scale = 0.75
|
|||
maximum_visual_scale = 1.2
|
||||
collision_radius = 0.65
|
||||
collision_height = 9.5
|
||||
gatherable_surface_material_names = PackedStringArray("wood")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ const PlayerStorageInteractionType = preload(
|
|||
const FOLIAGE_WIND_SHADER: Shader = preload(
|
||||
"res://world/materials/foliage_wind.gdshader"
|
||||
)
|
||||
const PrecipitationOcclusionType = preload(
|
||||
"res://world/environment/precipitation_occlusion.gd"
|
||||
)
|
||||
const FOLIAGE_MATERIAL_NAMES: Array[StringName] = [
|
||||
&"leaf",
|
||||
&"leaf_light",
|
||||
|
|
@ -60,6 +63,9 @@ var _foliage_wind_enabled: bool = false
|
|||
func _ready() -> void:
|
||||
if rebuild_terrain_collision_on_ready or not has_terrain_collision():
|
||||
_build_terrain_collision()
|
||||
PrecipitationOcclusionType.mark_canopy_meshes(
|
||||
get_node_or_null(terrain_visual_root_path)
|
||||
)
|
||||
_set_foliage_wind_enabled(true)
|
||||
if Engine.is_editor_hint():
|
||||
update_configuration_warnings()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ const LIGHT_RAIN_FIXED_FPS: int = 8
|
|||
const RAIN_VELOCITY_MIN: float = 16.0
|
||||
const RAIN_VELOCITY_MAX: float = 20.0
|
||||
const RAIN_DROP_SIZE := Vector3(0.014, 0.34, 0.014)
|
||||
const RAIN_COLLISION_BASE_SIZE: float = 0.08
|
||||
const RAIN_COLLISION_SIZE := Vector3(28.0, 24.0, 28.0)
|
||||
const FOG_DAYLIGHT_SCENE_BRIGHTNESS: float = 0.42
|
||||
const FOG_DAYLIGHT_FOG_LIGHT_BRIGHTNESS: float = 0.42
|
||||
const FOG_DAYLIGHT_WATER_BRIGHTNESS: float = 0.42
|
||||
|
|
@ -37,6 +39,9 @@ const FRESH_WATER_MATERIAL: ShaderMaterial = preload(
|
|||
const LocalStormCloudLayerType = preload(
|
||||
"res://world/environment/local_storm_cloud_layer.gd"
|
||||
)
|
||||
const PrecipitationOcclusionType = preload(
|
||||
"res://world/environment/precipitation_occlusion.gd"
|
||||
)
|
||||
|
||||
const DAY_SKY_TOP := Color(0.204, 0.498, 0.643)
|
||||
const DAY_SKY_HORIZON := Color(0.663, 0.843, 0.847)
|
||||
|
|
@ -88,6 +93,7 @@ var _rain_camera_provider: Callable
|
|||
var _environment: Environment
|
||||
var _sky_material: ShaderMaterial
|
||||
var _rain: GPUParticles3D
|
||||
var _rain_collision: GPUParticlesCollisionHeightField3D
|
||||
var _storm_clouds: LocalStormCloudLayer
|
||||
var _elapsed: float = 0.0
|
||||
var _weather_from: WorldWeatherService.Weather = (
|
||||
|
|
@ -211,6 +217,7 @@ func _prepare_rain() -> void:
|
|||
LIGHT_RAIN_FIXED_FPS if _light_performance_profile else 30
|
||||
)
|
||||
_rain.local_coords = false
|
||||
_rain.collision_base_size = RAIN_COLLISION_BASE_SIZE
|
||||
_rain.visibility_aabb = RAIN_VISIBILITY_AABB
|
||||
var process_material := ParticleProcessMaterial.new()
|
||||
process_material.emission_shape = (
|
||||
|
|
@ -222,6 +229,9 @@ func _prepare_rain() -> void:
|
|||
process_material.initial_velocity_min = RAIN_VELOCITY_MIN
|
||||
process_material.initial_velocity_max = RAIN_VELOCITY_MAX
|
||||
process_material.gravity = Vector3(0.0, -2.0, 0.0)
|
||||
process_material.collision_mode = (
|
||||
ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT
|
||||
)
|
||||
_rain.process_material = process_material
|
||||
var drop_mesh := BoxMesh.new()
|
||||
drop_mesh.size = RAIN_DROP_SIZE
|
||||
|
|
@ -232,9 +242,30 @@ func _prepare_rain() -> void:
|
|||
drop_mesh.material = drop_material
|
||||
_rain.draw_pass_1 = drop_mesh
|
||||
add_child(_rain)
|
||||
_prepare_rain_collision()
|
||||
_update_rain_position()
|
||||
|
||||
|
||||
func _prepare_rain_collision() -> void:
|
||||
_rain_collision = GPUParticlesCollisionHeightField3D.new()
|
||||
_rain_collision.name = "LocalRainCanopyCollision"
|
||||
_rain_collision.size = RAIN_COLLISION_SIZE
|
||||
_rain_collision.resolution = (
|
||||
GPUParticlesCollisionHeightField3D.RESOLUTION_256
|
||||
)
|
||||
_rain_collision.update_mode = (
|
||||
GPUParticlesCollisionHeightField3D.UPDATE_MODE_WHEN_MOVED
|
||||
)
|
||||
_rain_collision.follow_camera_enabled = true
|
||||
_rain_collision.heightfield_mask = (
|
||||
PrecipitationOcclusionType.RENDER_LAYER_MASK
|
||||
)
|
||||
# Local rain remains on its normal layer; unrelated particle systems do not
|
||||
# need to query this weather-specific collision field.
|
||||
_rain_collision.cull_mask = 1
|
||||
add_child(_rain_collision)
|
||||
|
||||
|
||||
func _prepare_storm_clouds() -> void:
|
||||
_storm_clouds = LocalStormCloudLayerType.new()
|
||||
_storm_clouds.name = "LocalStormClouds"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue