Merge branch 'main' into ui_fixes
This commit is contained in:
@@ -212,6 +212,17 @@ func _physics_process(_delta: float) -> void:
|
||||
last_pos_train = current_pos
|
||||
_refresh_world(current_pos)
|
||||
|
||||
func generate_initial_world_now() -> void:
|
||||
if rail_path == null or rail_path.train_instance == null:
|
||||
return
|
||||
|
||||
var train_pos = rail_path.train_instance.global_position
|
||||
var current_pos = Vector2i(roundi(train_pos.x / chunk_size), roundi(train_pos.z / chunk_size))
|
||||
last_pos_train = current_pos
|
||||
_rebuild_generation_queue(current_pos)
|
||||
_drain_generation_queue_now()
|
||||
_drain_wire_queue_now()
|
||||
|
||||
func collect_all_chunkinfo(root: Node, list: Array[Node]) -> void:
|
||||
if root == null: return
|
||||
|
||||
@@ -535,6 +546,20 @@ func _drain_generation_queue() -> void:
|
||||
pending_generation_cells.clear()
|
||||
pending_generation_cursor = 0
|
||||
|
||||
func _drain_generation_queue_now() -> void:
|
||||
while pending_generation_cursor < pending_generation_cells.size():
|
||||
var grid_pos: Vector2i = pending_generation_cells[pending_generation_cursor]
|
||||
pending_generation_cursor += 1
|
||||
if board.has(grid_pos):
|
||||
continue
|
||||
|
||||
var is_obstacle = _register_cell_with_ray(grid_pos)
|
||||
if not is_obstacle:
|
||||
_add_compatible_biome(grid_pos)
|
||||
|
||||
pending_generation_cells.clear()
|
||||
pending_generation_cursor = 0
|
||||
|
||||
func _drain_cleanup_queue() -> void:
|
||||
var processed: int = 0
|
||||
var start_usec: int = Time.get_ticks_usec()
|
||||
@@ -595,6 +620,22 @@ func _drain_wire_queue() -> void:
|
||||
pending_wire_cells.clear()
|
||||
pending_wire_cursor = 0
|
||||
|
||||
func _drain_wire_queue_now() -> void:
|
||||
while pending_wire_cursor < pending_wire_cells.size():
|
||||
var grid_pos: Vector2i = pending_wire_cells[pending_wire_cursor]
|
||||
pending_wire_cursor += 1
|
||||
pending_wire_lookup.erase(grid_pos)
|
||||
|
||||
if not board.has(grid_pos):
|
||||
continue
|
||||
if not board[grid_pos].get("have_lamppost", false):
|
||||
continue
|
||||
|
||||
_connect_lamppost_wires(grid_pos)
|
||||
|
||||
pending_wire_cells.clear()
|
||||
pending_wire_cursor = 0
|
||||
|
||||
func _generate_pieces_around_train(center: Vector2i) -> void:
|
||||
for x in range(-eye_line, eye_line + 1):
|
||||
for z in range(-eye_line, eye_line + 1):
|
||||
|
||||
@@ -50,10 +50,14 @@ enum TrainFacingDirection { KEEP_PATH_DIRECTION, LOCOMOTIVE_LEFT, LOCOMOTIVE_RIG
|
||||
@export_enum("keep_path_direction", "locomotive_left", "locomotive_right") var train_facing_direction: int = TrainFacingDirection.KEEP_PATH_DIRECTION
|
||||
|
||||
@export_group("Rails Bulding")
|
||||
##if true builds visible rail pieces along the Path3D
|
||||
@export var build_track_visuals: bool = true
|
||||
##distance between sleepers of the rails
|
||||
@export var sleepers_distance: float = 1.0
|
||||
##nodel for the sleeper of the rail
|
||||
@export var sleepers_model: PackedScene
|
||||
##if true add procedural rail bars even when sleepers_model is assigned
|
||||
@export var add_procedural_rails_to_sleepers_model: bool = false
|
||||
|
||||
@export_group("Manual Controls")
|
||||
##max speed
|
||||
@@ -115,7 +119,8 @@ func _ready() -> void:
|
||||
_initial_is_inmotion = is_inmotion
|
||||
|
||||
if curve != null and curve.get_baked_length() > 0:
|
||||
build_rails()
|
||||
if build_track_visuals:
|
||||
build_rails()
|
||||
build_train()
|
||||
_plan_stops()
|
||||
_apply_initial_train_start()
|
||||
@@ -258,6 +263,7 @@ func build_rails() -> void:
|
||||
|
||||
var total_length = curve.get_baked_length()
|
||||
var piece_number = int(total_length / sleepers_distance)
|
||||
var should_add_procedural_rails: bool = sleepers_model == null or add_procedural_rails_to_sleepers_model
|
||||
|
||||
for i in range(piece_number):
|
||||
var distance = i * sleepers_distance
|
||||
@@ -289,17 +295,18 @@ func build_rails() -> void:
|
||||
sleeper.material_override = mat_wood
|
||||
rail_piece.add_child(sleeper)
|
||||
|
||||
var rail_sx = MeshInstance3D.new()
|
||||
rail_sx.mesh = mesh_rail
|
||||
rail_sx.material_override = mat_iron
|
||||
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_sx)
|
||||
|
||||
var rail_dx = MeshInstance3D.new()
|
||||
rail_dx.mesh = mesh_rail
|
||||
rail_dx.material_override = mat_iron
|
||||
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_dx)
|
||||
if should_add_procedural_rails:
|
||||
var rail_sx = MeshInstance3D.new()
|
||||
rail_sx.mesh = mesh_rail
|
||||
rail_sx.material_override = mat_iron
|
||||
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_sx)
|
||||
|
||||
var rail_dx = MeshInstance3D.new()
|
||||
rail_dx.mesh = mesh_rail
|
||||
rail_dx.material_override = mat_iron
|
||||
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_dx)
|
||||
|
||||
func _plan_stops() -> void:
|
||||
stop_offset.clear()
|
||||
|
||||
@@ -4,6 +4,15 @@ extends Node3D
|
||||
const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres")
|
||||
const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres")
|
||||
const DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME: int = 32 #how many update of the environment (apply materials) will be done per frame
|
||||
const FORCE_WEATHER_MATERIAL_PATH_PARTS: PackedStringArray = [
|
||||
"/chunk/material/field.tres",
|
||||
"/chunk/material/grassflat_chunk.tres",
|
||||
"/chunk/material/path_chunk.tres",
|
||||
"/chunk/material/railway_chunk.tres",
|
||||
"/chunk/material/terrain.tres",
|
||||
"/chunk/prop/house/house.tres",
|
||||
"/chunk/prop/house/house_emissiv.tres",
|
||||
]
|
||||
|
||||
@export var environment_config: EnvironmentConfig
|
||||
|
||||
@@ -157,9 +166,17 @@ func _restore_keyboard_action_events() -> void:
|
||||
_blocked_keyboard_events_by_action.clear()
|
||||
|
||||
func _on_tree_node_added(node: Node) -> void:
|
||||
if node.is_in_group("wind_node") or node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"):
|
||||
if _should_queue_dynamic_environment_node(node):
|
||||
_queue_dynamic_environment_node(node)
|
||||
|
||||
func _should_queue_dynamic_environment_node(node: Node) -> bool:
|
||||
if node.is_in_group("wind_node"):
|
||||
return true
|
||||
if node.is_in_group("weather_vegetables_node"):
|
||||
return true
|
||||
|
||||
return node is Node3D
|
||||
|
||||
func _queue_dynamic_environment_node(node: Node) -> void:
|
||||
if not is_instance_valid(node):
|
||||
return
|
||||
@@ -205,7 +222,7 @@ func _apply_dynamic_environment_materials(node: Node) -> void:
|
||||
if node.is_in_group("wind_node"):
|
||||
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
|
||||
|
||||
if node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"):
|
||||
if node is Node3D:
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
|
||||
if _should_ignore_weather_overlay(node):
|
||||
@@ -231,11 +248,9 @@ func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
|
||||
_apply_wind_noise_to_node(child, noise_tex)
|
||||
|
||||
func ApplyWeatherShaderToMaterials():
|
||||
for node in get_tree().get_nodes_in_group("weather_node"):
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
|
||||
for node in get_tree().get_nodes_in_group("weather_vegetables_node"):
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
var current_scene := get_tree().current_scene
|
||||
if current_scene != null:
|
||||
_apply_weather_overlay_to_node(current_scene, _get_weather_overlay_material(current_scene))
|
||||
|
||||
func _get_weather_overlay_material(node: Node) -> Material:
|
||||
if not node.is_in_group("weather_overlay_no_noise"):
|
||||
@@ -255,13 +270,21 @@ func _apply_weather_overlay_to_node(node: Node, material: Material) -> void:
|
||||
return
|
||||
|
||||
if node is GeometryInstance3D:
|
||||
if _should_clear_weather_overlay(node) or _geometry_uses_alpha_texture(node):
|
||||
if _geometry_should_never_receive_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
elif _should_clear_weather_overlay(node) and not _geometry_forces_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
elif _geometry_uses_alpha_texture(node) or _geometry_should_skip_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
else:
|
||||
node.material_overlay = material
|
||||
|
||||
var child_material := _get_weather_overlay_material(node)
|
||||
if child_material == WEATHER_SHADER and material == weather_shader_no_noise:
|
||||
child_material = material
|
||||
|
||||
for child in node.get_children():
|
||||
_apply_weather_overlay_to_node(child, material)
|
||||
_apply_weather_overlay_to_node(child, child_material)
|
||||
|
||||
func _clear_weather_overlay_from_node(node: Node) -> void:
|
||||
if node is GeometryInstance3D:
|
||||
@@ -289,11 +312,99 @@ func _geometry_uses_alpha_texture(node: GeometryInstance3D) -> bool:
|
||||
|
||||
return false
|
||||
|
||||
func _geometry_should_never_receive_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if node is GPUParticles3D:
|
||||
return true
|
||||
|
||||
var node_name := String(node.name).to_lower()
|
||||
return node_name.contains("cloud") or node_name.contains("fog") or node_name.contains("godray")
|
||||
|
||||
func _geometry_should_skip_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if _geometry_forces_weather_overlay(node):
|
||||
return false
|
||||
if _node_name_indicates_water(node):
|
||||
return true
|
||||
|
||||
var has_material: bool = false
|
||||
var all_materials_ignored: bool = true
|
||||
var material_override := node.material_override
|
||||
if material_override != null:
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(material_override)
|
||||
|
||||
if node is MeshInstance3D:
|
||||
for surface_index in node.get_surface_override_material_count():
|
||||
var surface_material: Material = node.get_surface_override_material(surface_index)
|
||||
if surface_material == null:
|
||||
continue
|
||||
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(surface_material)
|
||||
|
||||
if node.mesh:
|
||||
for surface_index in node.mesh.get_surface_count():
|
||||
var mesh_material: Material = node.mesh.surface_get_material(surface_index)
|
||||
if mesh_material == null:
|
||||
continue
|
||||
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(mesh_material)
|
||||
|
||||
return has_material and all_materials_ignored
|
||||
|
||||
func _geometry_forces_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if _material_forces_weather_overlay(node.material_override):
|
||||
return true
|
||||
|
||||
if node is MeshInstance3D:
|
||||
for surface_index in node.get_surface_override_material_count():
|
||||
if _material_forces_weather_overlay(node.get_surface_override_material(surface_index)):
|
||||
return true
|
||||
|
||||
if node.mesh:
|
||||
for surface_index in node.mesh.get_surface_count():
|
||||
if _material_forces_weather_overlay(node.mesh.surface_get_material(surface_index)):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func _material_forces_weather_overlay(material: Material) -> bool:
|
||||
if material == null:
|
||||
return false
|
||||
|
||||
var resource_path := material.resource_path.to_lower()
|
||||
for path_part in FORCE_WEATHER_MATERIAL_PATH_PARTS:
|
||||
if resource_path.contains(path_part):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func _node_name_indicates_water(node: Node) -> bool:
|
||||
var node_name := String(node.name).to_lower()
|
||||
return node_name.begins_with("water") or node_name.contains("/water")
|
||||
|
||||
func _material_ignores_weather_overlay(material: Material) -> bool:
|
||||
if material == null:
|
||||
return false
|
||||
|
||||
var resource_path := material.resource_path.to_lower()
|
||||
if resource_path.contains("/water") or resource_path.contains("water_"):
|
||||
return true
|
||||
if resource_path.contains("/cloud/") or resource_path.contains("cloud"):
|
||||
return true
|
||||
|
||||
var base_material := material as BaseMaterial3D
|
||||
if base_material != null:
|
||||
return base_material.transparency != BaseMaterial3D.TRANSPARENCY_DISABLED or base_material.albedo_color.a < 0.99
|
||||
|
||||
return false
|
||||
|
||||
func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
|
||||
if material == null or material.shader == null:
|
||||
return false
|
||||
|
||||
return material.shader.code.find("alpha_texture") != -1
|
||||
var shader_code := material.shader.code
|
||||
return shader_code.find("alpha_texture") != -1 or shader_code.find("ALPHA") != -1 or shader_code.find("opacity") != -1
|
||||
|
||||
func _should_ignore_weather_overlay(node: Node) -> bool:
|
||||
return node.is_in_group("weather_overlay_ignore")
|
||||
|
||||
@@ -13,8 +13,9 @@ global uniform float global_snow_melt_speed = 0.1;
|
||||
global uniform float global_snow_amount = 0.0;
|
||||
global uniform vec4 global_snow_color;
|
||||
global uniform float global_rain_intensity;
|
||||
const float LEAVES_FULL_SNOW_START = 0.58;
|
||||
const float LEAVES_FULL_SNOW_END = 0.86;
|
||||
const float LEAVES_FULL_SNOW_START = 0.96;
|
||||
const float LEAVES_FULL_SNOW_END = 1.0;
|
||||
const float LEAVES_TOP_SNOW_COVERAGE = 0.75;
|
||||
|
||||
uniform sampler2D wind_noise : filter_linear_mipmap;
|
||||
uniform bool billboard_enabled = true;
|
||||
@@ -110,7 +111,7 @@ void fragment() {
|
||||
float snow_amount = pow(get_snow_progress(), 0.55);
|
||||
|
||||
float top_mask = 1.0 - shifted_uv.y;
|
||||
float snow_mask = smoothstep(1.0 - snow_amount * 1.35, 1.08 - snow_amount * 1.35, top_mask) * snow_visibility;
|
||||
float snow_mask = smoothstep(1.0 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, 1.08 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, top_mask) * snow_visibility;
|
||||
snow_mask *= step(0.01, snow_amount);
|
||||
float full_snow_mask = smoothstep(LEAVES_FULL_SNOW_START, LEAVES_FULL_SNOW_END, snow_amount) * clamp(snow_visibility, 0.0, 1.0);
|
||||
float final_snow_mask = max(snow_mask, full_snow_mask);
|
||||
|
||||
@@ -17,8 +17,8 @@ global uniform float global_snow_cap_flatness_end = 0.96;
|
||||
global uniform float global_snow_cap_noise_scale = 0.22;
|
||||
global uniform float global_snow_cap_noise_strength = 0.18;
|
||||
const float SNOW_VISUAL_RESPONSE = 0.55;
|
||||
const float FULL_SNOW_VISUAL_START = 0.82;
|
||||
const float FULL_SNOW_VISUAL_END = 0.98;
|
||||
const float FULL_SNOW_VISUAL_START = 0.58;
|
||||
const float FULL_SNOW_VISUAL_END = 0.86;
|
||||
uniform bool snow_noise_enabled = true;
|
||||
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
|
||||
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
|
||||
@@ -154,7 +154,7 @@ void fragment() {
|
||||
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
|
||||
float snow_noise_mask = get_snow_noise_mask(world_pos, snow_progress);
|
||||
float snow_variation = mix(0.75, 1.15, noise_val);
|
||||
float snow_coverage = clamp(mix(snow_progress * 0.35, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.15, 0.0, 1.0);
|
||||
float snow_coverage = clamp(mix(snow_progress * 0.55, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.2, 0.0, 1.0);
|
||||
float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
|
||||
float snow_opacity = clamp(snow_factor, 0.0, 1.0);
|
||||
snow_opacity = max(snow_opacity, flat_accumulation * mix(0.45, 0.9, snow_noise_mask));
|
||||
|
||||
Reference in New Issue
Block a user