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.
This commit is contained in:
parent
a1efaf6294
commit
f98773237e
10 changed files with 314 additions and 137 deletions
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://c27fogucecn0r"]
|
||||
[gd_scene load_steps=5 format=3 uid="uid://c27fogucecn0r"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://lcedx3lau6v5" path="res://Entities/Tree/scripts/tree.gd" id="1_702jv"]
|
||||
|
||||
|
|
@ -8,6 +8,9 @@ height = 0.5
|
|||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_702jv"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_702jv"]
|
||||
radius = 0.6
|
||||
|
||||
[node name="Tree" type="Node3D"]
|
||||
script = ExtResource("1_702jv")
|
||||
|
||||
|
|
@ -20,3 +23,15 @@ shape = SubResource("CapsuleShape3D_s6kdm")
|
|||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractRange"]
|
||||
shape = SubResource("SphereShape3D_702jv")
|
||||
|
||||
[node name="VisibilityArea" type="StaticBody3D" parent="." groups=["tree_visibility"]]
|
||||
collision_layer = 2
|
||||
collision_mask = 2
|
||||
|
||||
[node name="VisibilityShape" type="CollisionShape3D" parent="VisibilityArea"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.616604, 0)
|
||||
shape = SubResource("CapsuleShape3D_702jv")
|
||||
debug_color = Color(0.837648, 0.364145, 0.346694, 0.42)
|
||||
|
||||
[node name="LastBlocked" type="Timer" parent="."]
|
||||
wait_time = 0.5
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
class_name TreeNode
|
||||
extends Node3D
|
||||
|
||||
@onready var area: Area3D = $InteractRange
|
||||
|
||||
var tree_data: TreeDataResource
|
||||
var model_instance: Node3D
|
||||
var mesh_instances: Array[MeshInstance3D] = []
|
||||
var original_materials: Array[Material] = []
|
||||
var outline_material: Material
|
||||
var base_circle: MeshInstance3D
|
||||
var circle_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
|
||||
|
|
@ -35,16 +37,9 @@ func spawn_model():
|
|||
model_instance = tree_data.model.instantiate()
|
||||
tree_data.apply_seasonal_colors(model_instance, Season.current)
|
||||
add_child(model_instance)
|
||||
|
||||
|
||||
# Create base circle after model is loaded
|
||||
if not base_circle:
|
||||
create_base_circle()
|
||||
|
||||
# Re-scan for mesh instances in the new model
|
||||
find_all_mesh_instances(model_instance)
|
||||
|
||||
update_outline_materials()
|
||||
|
||||
# Apply any additional properties from tree_data
|
||||
apply_tree_properties()
|
||||
|
|
@ -59,7 +54,7 @@ func apply_tree_properties():
|
|||
|
||||
# Apply random scale based on tree properties
|
||||
if tree_data.max_height > 0:
|
||||
var scale_variation = randf_range(0.8, 1.2)
|
||||
var scale_variation = randf_range(1.5, 2)
|
||||
model_instance.scale *= scale_variation
|
||||
|
||||
func setup_outline_material() -> void:
|
||||
|
|
@ -68,67 +63,113 @@ func setup_outline_material() -> void:
|
|||
outline_material.shader = preload("res://outline.gdshader")
|
||||
outline_material.set_shader_parameter("color", Vector3(0.702, 0.557, 0.259))
|
||||
|
||||
func update_outline_materials():
|
||||
if mesh_instances.is_empty():
|
||||
print("Warning: No MeshInstance3D found in model!")
|
||||
return
|
||||
|
||||
# Store original materials for the new model
|
||||
original_materials.clear()
|
||||
for mesh in mesh_instances:
|
||||
if mesh.get_surface_override_material(0):
|
||||
original_materials.append(mesh.get_surface_override_material(0))
|
||||
else:
|
||||
original_materials.append(mesh.get_surface_override_material(0))
|
||||
|
||||
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 create_base_circle():
|
||||
base_circle = MeshInstance3D.new()
|
||||
add_child(base_circle)
|
||||
func fade_out_tree_simple(duration: float = 0.25):
|
||||
# Reset the timer
|
||||
last_blocked.stop()
|
||||
last_blocked.start()
|
||||
|
||||
# Create a flat cylinder for the circle
|
||||
var cylinder = CylinderMesh.new()
|
||||
cylinder.top_radius = 0.4 # Adjust size as needed
|
||||
cylinder.bottom_radius = 0.4
|
||||
cylinder.height = 1 # Very thin to make it look like a flat circle
|
||||
cylinder.rings = 1
|
||||
cylinder.radial_segments = 9 # More segments = smoother circle
|
||||
base_circle.mesh = cylinder
|
||||
|
||||
# Create transparent material with outline
|
||||
circle_material = StandardMaterial3D.new()
|
||||
circle_material.flags_transparent = true
|
||||
circle_material.albedo_color = Color(1.0, 0.843, 0.0, 0.0) # Fully transparent gold
|
||||
|
||||
# Add rim lighting effect for outline appearance
|
||||
circle_material.rim_enabled = true
|
||||
circle_material.rim = 1.0
|
||||
circle_material.rim_tint = 1.0
|
||||
circle_material.rim_color = Color(1.0, 0.843, 0.0) # Gold rim
|
||||
base_circle.material_override = circle_material
|
||||
if is_invisible:
|
||||
return
|
||||
|
||||
base_circle.position.y = 1 # Slightly above ground to avoid z-fighting
|
||||
is_invisible = true
|
||||
|
||||
# Start hidden
|
||||
base_circle.visible = 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"):
|
||||
base_circle.visible = true
|
||||
# Apply outline to all mesh instances
|
||||
for mesh in mesh_instances:
|
||||
mesh.material_overlay = outline_material
|
||||
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...')
|
||||
base_circle.visible = false
|
||||
# Remove outline from all mesh instances
|
||||
for mesh in mesh_instances:
|
||||
mesh.material_overlay = null
|
||||
if not is_invisible:
|
||||
# Reset the material overlay to null
|
||||
for mesh in mesh_instances:
|
||||
mesh.material_overlay = null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue