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