Adds tree fading based on camera line of sight. Trees now fade out when they obstruct the player's view, improving visibility. Also includes several fixes: - Fixes tree distribution. - Adds a see-through shader. - Adds camera and occlusion scripts.
175 lines
5 KiB
GDScript
175 lines
5 KiB
GDScript
class_name TreeNode
|
|
extends Node3D
|
|
|
|
var tree_data: TreeDataResource
|
|
var model_instance: Node3D
|
|
var mesh_instances: Array[MeshInstance3D] = []
|
|
var original_materials: Array[Material] = []
|
|
var outline_material: Material
|
|
var is_invisible: bool = false
|
|
var fade_tween: Tween
|
|
|
|
@onready var area: Area3D = $InteractRange
|
|
@onready var last_blocked: Timer = $LastBlocked
|
|
|
|
func _ready():
|
|
setup_outline_material()
|
|
area.body_entered.connect(_on_player_entered)
|
|
area.body_exited.connect(_on_player_exited)
|
|
last_blocked.timeout.connect(_on_fade_timer_timeout)
|
|
|
|
func set_tree_data(data: TreeDataResource):
|
|
tree_data = data
|
|
spawn_model()
|
|
|
|
func spawn_model():
|
|
if not tree_data or not tree_data.model:
|
|
Log.pr("No tree data or model provided")
|
|
return
|
|
|
|
# Clear any existing model
|
|
if model_instance:
|
|
model_instance.queue_free()
|
|
mesh_instances.clear()
|
|
original_materials.clear()
|
|
|
|
# Instantiate the model from the TreeDataResource
|
|
model_instance = tree_data.model.instantiate()
|
|
tree_data.apply_seasonal_colors(model_instance, Season.current)
|
|
add_child(model_instance)
|
|
|
|
# Re-scan for mesh instances in the new model
|
|
find_all_mesh_instances(model_instance)
|
|
|
|
# Apply any additional properties from tree_data
|
|
apply_tree_properties()
|
|
|
|
func apply_tree_properties():
|
|
if not model_instance or not tree_data:
|
|
return
|
|
|
|
# Apply scale variations if defined in tree_data
|
|
if tree_data.has_method("get_random_scale"):
|
|
model_instance.scale *= tree_data.get_random_scale()
|
|
|
|
# Apply random scale based on tree properties
|
|
if tree_data.max_height > 0:
|
|
var scale_variation = randf_range(1.5, 2)
|
|
model_instance.scale *= scale_variation
|
|
|
|
func setup_outline_material() -> void:
|
|
# Create outline material with your shader
|
|
outline_material = ShaderMaterial.new()
|
|
outline_material.shader = preload("res://outline.gdshader")
|
|
outline_material.set_shader_parameter("color", Vector3(0.702, 0.557, 0.259))
|
|
|
|
func find_all_mesh_instances(node: Node):
|
|
if node is MeshInstance3D:
|
|
mesh_instances.append(node)
|
|
for child in node.get_children():
|
|
find_all_mesh_instances(child)
|
|
|
|
func fade_out_tree_simple(duration: float = 0.25):
|
|
# Reset the timer
|
|
last_blocked.stop()
|
|
last_blocked.start()
|
|
|
|
if is_invisible:
|
|
return
|
|
|
|
is_invisible = true
|
|
|
|
# Kill any existing fade tween
|
|
if fade_tween:
|
|
fade_tween.kill()
|
|
|
|
for mesh_instance in mesh_instances:
|
|
fade_mesh_materials(mesh_instance, 1.0, 0.1, duration)
|
|
|
|
# This gets called when the timer times out
|
|
func _on_fade_timer_timeout():
|
|
fade_in_tree_immediate()
|
|
|
|
func fade_in_tree_immediate(duration: float = 0.25):
|
|
if not is_invisible:
|
|
return
|
|
|
|
# Kill any existing fade tween
|
|
if fade_tween:
|
|
fade_tween.kill()
|
|
|
|
for mesh_instance in mesh_instances:
|
|
fade_mesh_materials(mesh_instance, 0.1, 1.0, duration)
|
|
|
|
is_invisible = false
|
|
|
|
func fade_mesh_materials(mesh_instance: MeshInstance3D, from_alpha: float, to_alpha: float, duration: float):
|
|
if not mesh_instance.mesh:
|
|
return
|
|
|
|
var surface_count = mesh_instance.mesh.get_surface_count()
|
|
|
|
for surface_id in range(surface_count):
|
|
var material = mesh_instance.get_surface_override_material(surface_id)
|
|
|
|
# Always ensure we have a unique material instance
|
|
if not material:
|
|
# No override material - duplicate from base material
|
|
var base_material = mesh_instance.mesh.surface_get_material(surface_id)
|
|
if base_material:
|
|
material = base_material.duplicate()
|
|
mesh_instance.set_surface_override_material(surface_id, material)
|
|
else:
|
|
# Override material exists - duplicate it to ensure uniqueness
|
|
material = material.duplicate()
|
|
mesh_instance.set_surface_override_material(surface_id, material)
|
|
|
|
# Only work with StandardMaterial3D
|
|
if material and material is StandardMaterial3D:
|
|
var std_mat = material as StandardMaterial3D
|
|
|
|
# Set transparency mode based on target alpha
|
|
if to_alpha < 1.0:
|
|
std_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
|
|
# Force shadow casting
|
|
mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
|
|
|
# Set initial alpha
|
|
std_mat.albedo_color.a = from_alpha
|
|
|
|
# Create and store the tween
|
|
fade_tween = create_tween()
|
|
fade_tween.tween_method(
|
|
func(alpha: float):
|
|
std_mat.albedo_color.a = alpha,
|
|
from_alpha, to_alpha, duration
|
|
)
|
|
|
|
# When fading to full opacity, reset transparency mode
|
|
if to_alpha >= 1.0:
|
|
fade_tween.tween_callback(
|
|
func(): std_mat.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED
|
|
)
|
|
|
|
func toggle_tree_visibility(duration: float = 1.0):
|
|
if is_invisible:
|
|
fade_in_tree_immediate(duration)
|
|
else:
|
|
fade_out_tree_simple(duration)
|
|
|
|
func _on_player_entered(body: Node3D):
|
|
if body.is_in_group("player"):
|
|
# Apply outline to all mesh instances
|
|
if not is_invisible:
|
|
for mesh in mesh_instances:
|
|
mesh.material_overlay = outline_material
|
|
|
|
func _on_player_exited(body: Node3D):
|
|
if body.is_in_group("player"):
|
|
Log.pr('Out of range...')
|
|
# Remove outline from all mesh instances
|
|
if not is_invisible:
|
|
# Reset the material overlay to null
|
|
for mesh in mesh_instances:
|
|
mesh.material_overlay = null
|