nature-sim/Entities/Tree/scripts/tree.gd
Dan Baker f98773237e Implements tree visibility occlusion system.
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.
2025-07-01 10:32:21 +01:00

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