first commit
This commit is contained in:
330
Scripts/Binari.gd
Normal file
330
Scripts/Binari.gd
Normal file
@@ -0,0 +1,330 @@
|
||||
extends Path3D
|
||||
|
||||
@export_group("Impostazioni Treno")
|
||||
@export var velocita_treno: float = 6.0
|
||||
@export var in_movimento: bool = true
|
||||
@export var modello_treno: PackedScene
|
||||
@export var distanza_assi: float = 3.0
|
||||
@export var larghezza_binari: float = 1.2
|
||||
@export var gruppo_camere: Node3D
|
||||
|
||||
@export_group("Controlli Manuali")
|
||||
@export var velocita_max: float = 15.0
|
||||
@export var velocita_min: float = -5.0
|
||||
@export var accelerazione_manuale: float = 5.0
|
||||
|
||||
@export_group("Fermate (Stazioni)")
|
||||
@export var abilita_fermate: bool = true
|
||||
@export var tempo_di_sosta: float = 4.0
|
||||
@export var distanza_frenata: float = 15.0
|
||||
@export var tempo_ripartenza: float = 3.0
|
||||
@export var scena_fuochi: PackedScene
|
||||
|
||||
var colori_fuochi: Array[Color] = [
|
||||
Color(1.0, 0.2, 0.2), # Rosso vivo
|
||||
Color(0.2, 1.0, 0.2), # Verde lime
|
||||
Color(0.3, 0.5, 1.0), # Blu cielo
|
||||
Color(1.0, 0.8, 0.1), # Oro
|
||||
Color(0.8, 0.2, 1.0), # Viola
|
||||
Color(0.1, 1.0, 0.9) # Ciano
|
||||
]
|
||||
|
||||
@export_group("Movimento e Oscillazione")
|
||||
@export_range(0.0, 1.0) var intensita_oscillazione_base: float = 0.5
|
||||
|
||||
@export_group("Costruzione Rotaie")
|
||||
@export var distanza_traversine: float = 1.0
|
||||
@export var modello_traversina: PackedScene
|
||||
|
||||
var treno_istanziato: Node3D
|
||||
var progresso_treno: float = 0.0
|
||||
var tempo_oscillazione: float = 0.0
|
||||
|
||||
var offsets_fermate: Array[float] = []
|
||||
var posizioni_fermate: Array[Vector3] = []
|
||||
var fermata_in_corso: bool = false
|
||||
var indice_prossima_fermata: int = 0
|
||||
var moltiplicatore_stazione: float = 1.0
|
||||
var in_ripartenza: bool = false
|
||||
var tween_ripartenza: Tween
|
||||
|
||||
func _ready() -> void:
|
||||
if curve != null and curve.get_baked_length() > 0:
|
||||
_costruisci_rotaie_da_zero()
|
||||
_costruisci_treno()
|
||||
_calcola_fermate()
|
||||
else:
|
||||
print("ATTENZIONE: Devi prima disegnare i punti del Path3D!")
|
||||
|
||||
func _calcola_fermate() -> void:
|
||||
var fermate_temp = []
|
||||
for figlio in get_children():
|
||||
if figlio is Marker3D:
|
||||
var offset = curve.get_closest_offset(figlio.position)
|
||||
fermate_temp.append({
|
||||
"offset": offset,
|
||||
"posizione": figlio.global_position
|
||||
})
|
||||
fermate_temp.sort_custom(func(a, b): return a["offset"] < b["offset"])
|
||||
for dati in fermate_temp:
|
||||
offsets_fermate.append(dati["offset"])
|
||||
posizioni_fermate.append(dati["posizione"])
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
if event.keycode == KEY_T:
|
||||
_teletrasporta_prossima_fermata()
|
||||
elif event.keycode == KEY_F:
|
||||
_test_fuochi_manuale()
|
||||
|
||||
func _test_fuochi_manuale() -> void:
|
||||
if scena_fuochi == null or treno_istanziato == null: return
|
||||
|
||||
print("Lancio fuochi d'artificio di test!")
|
||||
var num_fuochi = randi_range(5, 8)
|
||||
for i in range(num_fuochi):
|
||||
var fuoco_root = scena_fuochi.instantiate()
|
||||
get_tree().current_scene.add_child(fuoco_root)
|
||||
|
||||
var offset_x = randf_range(-6.0, 6.0)
|
||||
var offset_z = randf_range(-6.0, 6.0)
|
||||
# 1. POSIZIONA
|
||||
fuoco_root.global_position = treno_istanziato.global_position + Vector3(offset_x, 0.5, offset_z)
|
||||
|
||||
# 2. COLORA E ACCENDI
|
||||
if fuoco_root.has_method("imposta_colore"):
|
||||
fuoco_root.imposta_colore(colori_fuochi.pick_random())
|
||||
if fuoco_root.has_method("accendi"):
|
||||
fuoco_root.accendi()
|
||||
|
||||
await get_tree().create_timer(randf_range(0.2, 0.6)).timeout
|
||||
|
||||
func _teletrasporta_prossima_fermata() -> void:
|
||||
if offsets_fermate.size() == 0 or fermata_in_corso or in_ripartenza:
|
||||
return
|
||||
var target_offset = offsets_fermate[indice_prossima_fermata]
|
||||
progresso_treno = target_offset
|
||||
_esegui_fermata(velocita_treno >= 0)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_gestione_input_movimento(delta)
|
||||
_gestione_movimento_treno(delta)
|
||||
|
||||
func _gestione_input_movimento(delta: float) -> void:
|
||||
if not in_movimento: return
|
||||
if Input.is_action_pressed("ui_up"):
|
||||
velocita_treno += accelerazione_manuale * delta
|
||||
elif Input.is_action_pressed("ui_down"):
|
||||
velocita_treno -= accelerazione_manuale * delta
|
||||
velocita_treno = clamp(velocita_treno, velocita_min, velocita_max)
|
||||
|
||||
func _gestione_movimento_treno(delta: float) -> void:
|
||||
if in_movimento and treno_istanziato and curve:
|
||||
var lunghezza_totale = curve.get_baked_length()
|
||||
if lunghezza_totale <= 0: return
|
||||
|
||||
if not fermata_in_corso:
|
||||
var vecchio_progresso = progresso_treno
|
||||
|
||||
if abilita_fermate and offsets_fermate.size() > 0 and not in_ripartenza:
|
||||
var target_offset = offsets_fermate[indice_prossima_fermata]
|
||||
var dist = target_offset - progresso_treno
|
||||
dist = wrapf(dist + lunghezza_totale / 2.0, 0.0, lunghezza_totale) - lunghezza_totale / 2.0
|
||||
|
||||
if abs(dist) < distanza_frenata and sign(dist) == sign(velocita_treno):
|
||||
var t = abs(dist) / distanza_frenata
|
||||
moltiplicatore_stazione = max(smoothstep(0.0, 1.0, t), 0.05)
|
||||
else:
|
||||
moltiplicatore_stazione = 1.0
|
||||
|
||||
var velocita_attuale = velocita_treno * moltiplicatore_stazione
|
||||
progresso_treno += velocita_attuale * delta
|
||||
|
||||
if abilita_fermate and offsets_fermate.size() > 0:
|
||||
var target_offset = offsets_fermate[indice_prossima_fermata]
|
||||
var superato_avanti = (velocita_attuale > 0 and vecchio_progresso < target_offset and progresso_treno >= target_offset)
|
||||
var superato_indietro = (velocita_attuale < 0 and vecchio_progresso > target_offset and progresso_treno <= target_offset)
|
||||
|
||||
if superato_avanti or superato_indietro:
|
||||
progresso_treno = target_offset
|
||||
_esegui_fermata(velocita_attuale > 0)
|
||||
|
||||
if progresso_treno > lunghezza_totale:
|
||||
progresso_treno -= lunghezza_totale
|
||||
if offsets_fermate.size() > 0: indice_prossima_fermata = 0
|
||||
elif progresso_treno < 0:
|
||||
progresso_treno += lunghezza_totale
|
||||
if offsets_fermate.size() > 0: indice_prossima_fermata = offsets_fermate.size() - 1
|
||||
|
||||
# 1. Il centro matematico perfetto e incontrovertibile del treno
|
||||
var pos_centro = to_global(curve.sample_baked(progresso_treno, true))
|
||||
|
||||
# 2. Il punto in cui deve guardare (abbastanza lontano per non vibrare)
|
||||
var prog_davanti = wrapf(progresso_treno + 2.0, 0.0, lunghezza_totale)
|
||||
var pos_davanti = to_global(curve.sample_baked(prog_davanti, true))
|
||||
|
||||
if pos_centro.distance_to(pos_davanti) > 0.01:
|
||||
# Posizionamento esatto, zero vibrazioni laterali
|
||||
treno_istanziato.global_position = pos_centro
|
||||
treno_istanziato.look_at(pos_davanti, Vector3.UP)
|
||||
treno_istanziato.rotate_y(PI)
|
||||
|
||||
if gruppo_camere:
|
||||
# La telecamera segue il punto perfetto
|
||||
gruppo_camere.global_position = gruppo_camere.global_position.lerp(pos_centro, delta * 12.0)
|
||||
|
||||
if not fermata_in_corso:
|
||||
var vel_reale = abs(velocita_treno * moltiplicatore_stazione)
|
||||
tempo_oscillazione += delta * vel_reale * 2.0
|
||||
var ampiezza_z = 0.006 * intensita_oscillazione_base
|
||||
var ampiezza_x = 0.004 * intensita_oscillazione_base
|
||||
treno_istanziato.rotation.z += sin(tempo_oscillazione) * ampiezza_z
|
||||
treno_istanziato.rotation.x += cos(tempo_oscillazione * 0.8) * ampiezza_x
|
||||
|
||||
func _esegui_fermata(andando_avanti: bool = true) -> void:
|
||||
fermata_in_corso = true
|
||||
moltiplicatore_stazione = 0.0
|
||||
|
||||
var indice_stazione_attuale = indice_prossima_fermata
|
||||
|
||||
if andando_avanti:
|
||||
indice_prossima_fermata += 1
|
||||
if indice_prossima_fermata >= offsets_fermate.size():
|
||||
indice_prossima_fermata = 0
|
||||
else:
|
||||
indice_prossima_fermata -= 1
|
||||
if indice_prossima_fermata < 0:
|
||||
indice_prossima_fermata = offsets_fermate.size() - 1
|
||||
|
||||
if scena_fuochi != null:
|
||||
var posizione_marker = posizioni_fermate[indice_stazione_attuale]
|
||||
var num_fuochi = randi_range(5, 8)
|
||||
for i in range(num_fuochi):
|
||||
var fuoco_root = scena_fuochi.instantiate()
|
||||
get_tree().current_scene.add_child(fuoco_root)
|
||||
|
||||
var offset_x = randf_range(-5.0, 5.0)
|
||||
var offset_z = randf_range(-5.0, 5.0)
|
||||
|
||||
# 1. POSIZIONA
|
||||
fuoco_root.global_position = posizione_marker + Vector3(offset_x, 0.5, offset_z)
|
||||
|
||||
# 2. COLORA E ACCENDI
|
||||
if fuoco_root.has_method("imposta_colore"):
|
||||
fuoco_root.imposta_colore(colori_fuochi.pick_random())
|
||||
if fuoco_root.has_method("accendi"):
|
||||
fuoco_root.accendi()
|
||||
|
||||
await get_tree().create_timer(randf_range(0.3, 0.8)).timeout
|
||||
|
||||
await get_tree().create_timer(tempo_di_sosta).timeout
|
||||
|
||||
fermata_in_corso = false
|
||||
in_ripartenza = true
|
||||
|
||||
if tween_ripartenza and tween_ripartenza.is_valid():
|
||||
tween_ripartenza.kill()
|
||||
|
||||
tween_ripartenza = create_tween()
|
||||
tween_ripartenza.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
tween_ripartenza.tween_property(self, "moltiplicatore_stazione", 1.0, tempo_ripartenza)
|
||||
tween_ripartenza.tween_callback(func(): in_ripartenza = false)
|
||||
|
||||
func _costruisci_rotaie_da_zero() -> void:
|
||||
var mat_legno = StandardMaterial3D.new()
|
||||
mat_legno.albedo_color = Color(0.35, 0.2, 0.1)
|
||||
var mat_ferro = StandardMaterial3D.new()
|
||||
mat_ferro.albedo_color = Color(0.6, 0.6, 0.65)
|
||||
mat_ferro.metallic = 0.8
|
||||
|
||||
var mesh_traversina_default = BoxMesh.new()
|
||||
mesh_traversina_default.size = Vector3(larghezza_binari + 0.6, 0.1, 0.3)
|
||||
var mesh_binario = BoxMesh.new()
|
||||
mesh_binario.size = Vector3(0.1, 0.15, distanza_traversine + 0.05)
|
||||
|
||||
var lunghezza_totale = curve.get_baked_length()
|
||||
var numero_pezzi = int(lunghezza_totale / distanza_traversine)
|
||||
|
||||
for i in range(numero_pezzi):
|
||||
var distanza = i * distanza_traversine
|
||||
var pezzo_binario = Node3D.new()
|
||||
add_child(pezzo_binario)
|
||||
|
||||
var pos_attuale_locale = curve.sample_baked(distanza, true)
|
||||
var distanza_avanti = distanza + 0.1
|
||||
var pos_avanti_locale = Vector3.ZERO
|
||||
|
||||
if distanza_avanti > lunghezza_totale:
|
||||
var pos_dietro = curve.sample_baked(distanza - 0.1, true)
|
||||
pos_avanti_locale = pos_attuale_locale + (pos_attuale_locale - pos_dietro)
|
||||
else:
|
||||
pos_avanti_locale = curve.sample_baked(distanza_avanti, true)
|
||||
|
||||
pezzo_binario.position = pos_attuale_locale
|
||||
var globale_attuale = to_global(pos_attuale_locale)
|
||||
var globale_avanti = to_global(pos_avanti_locale)
|
||||
if globale_attuale.distance_to(globale_avanti) > 0.001:
|
||||
pezzo_binario.look_at(globale_avanti, Vector3.UP)
|
||||
|
||||
if modello_traversina != null:
|
||||
var traversina_custom = modello_traversina.instantiate()
|
||||
pezzo_binario.add_child(traversina_custom)
|
||||
else:
|
||||
var traversina = MeshInstance3D.new()
|
||||
traversina.mesh = mesh_traversina_default
|
||||
traversina.material_override = mat_legno
|
||||
pezzo_binario.add_child(traversina)
|
||||
|
||||
var binario_sx = MeshInstance3D.new()
|
||||
binario_sx.mesh = mesh_binario
|
||||
binario_sx.material_override = mat_ferro
|
||||
binario_sx.position = Vector3(-larghezza_binari / 2.0, 0.1, 0)
|
||||
pezzo_binario.add_child(binario_sx)
|
||||
|
||||
var binario_dx = MeshInstance3D.new()
|
||||
binario_dx.mesh = mesh_binario
|
||||
binario_dx.material_override = mat_ferro
|
||||
binario_dx.position = Vector3(larghezza_binari / 2.0, 0.1, 0)
|
||||
pezzo_binario.add_child(binario_dx)
|
||||
|
||||
func _costruisci_treno() -> void:
|
||||
if modello_treno != null:
|
||||
treno_istanziato = modello_treno.instantiate()
|
||||
add_child(treno_istanziato)
|
||||
else:
|
||||
_costruisci_trenino_default()
|
||||
|
||||
func _costruisci_trenino_default() -> void:
|
||||
treno_istanziato = Node3D.new()
|
||||
add_child(treno_istanziato)
|
||||
|
||||
var mat_carrozzeria = StandardMaterial3D.new()
|
||||
mat_carrozzeria.albedo_color = Color(0.8, 0.15, 0.15)
|
||||
var mat_vetro = StandardMaterial3D.new()
|
||||
mat_vetro.albedo_color = Color(0.2, 0.8, 1.0)
|
||||
|
||||
var base_treno = MeshInstance3D.new()
|
||||
var mesh_base = BoxMesh.new()
|
||||
mesh_base.size = Vector3(1.4, 0.8, 3.0)
|
||||
base_treno.mesh = mesh_base
|
||||
base_treno.material_override = mat_carrozzeria
|
||||
base_treno.position = Vector3(0, 0.6, 0)
|
||||
treno_istanziato.add_child(base_treno)
|
||||
|
||||
var cabina = MeshInstance3D.new()
|
||||
var mesh_cabina = BoxMesh.new()
|
||||
mesh_cabina.size = Vector3(1.4, 1.0, 1.2)
|
||||
cabina.mesh = mesh_cabina
|
||||
cabina.material_override = mat_vetro
|
||||
cabina.position = Vector3(0, 1.5, -0.8)
|
||||
treno_istanziato.add_child(cabina)
|
||||
|
||||
var ciminiera = MeshInstance3D.new()
|
||||
var mesh_ciminiera = CylinderMesh.new()
|
||||
mesh_ciminiera.top_radius = 0.2
|
||||
mesh_ciminiera.bottom_radius = 0.3
|
||||
mesh_ciminiera.height = 0.8
|
||||
ciminiera.mesh = mesh_ciminiera
|
||||
ciminiera.material_override = mat_carrozzeria
|
||||
ciminiera.position = Vector3(0, 1.2, 1.0)
|
||||
treno_istanziato.add_child(ciminiera)
|
||||
1
Scripts/Binari.gd.uid
Normal file
1
Scripts/Binari.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cagpbu4pg5s1y
|
||||
127
Scripts/CameraMove.gd
Normal file
127
Scripts/CameraMove.gd
Normal file
@@ -0,0 +1,127 @@
|
||||
extends Node3D
|
||||
|
||||
@export_group("Riferimenti Camere")
|
||||
@export var camera_iso : Camera3D
|
||||
@export var camera_frontale : Camera3D
|
||||
@export var camera_laterale : Camera3D
|
||||
@export var camera_laterale_2 : Camera3D # Nuova Camera 4
|
||||
@export var camera_alta : Camera3D # Nuova Camera 5
|
||||
|
||||
@export_group("Movimento Camera 1")
|
||||
@export var move_speed : float = 20.0
|
||||
@export var rotation_speed : float = 0.2
|
||||
|
||||
@export_group("Cinematic Mode")
|
||||
@export var tempo_cambio_camera : float = 10.0 # Quanti secondi dura ogni inquadratura
|
||||
|
||||
var is_moving_enabled : bool = true
|
||||
|
||||
# --- Variabili Cinematic Mode ---
|
||||
var is_cinematic_mode : bool = false
|
||||
var cinematic_timer : float = 0.0
|
||||
var cinematic_index : int = 0
|
||||
var array_camere : Array[Camera3D] = []
|
||||
|
||||
func _ready():
|
||||
# Creiamo un array con le telecamere valide per ciclarle facilmente
|
||||
if camera_iso: array_camere.append(camera_iso)
|
||||
if camera_frontale: array_camere.append(camera_frontale)
|
||||
if camera_laterale: array_camere.append(camera_laterale)
|
||||
if camera_laterale_2: array_camere.append(camera_laterale_2)
|
||||
if camera_alta: array_camere.append(camera_alta)
|
||||
|
||||
# All'inizio, assicuriamoci che la CameraIso sia quella attiva
|
||||
if camera_iso:
|
||||
camera_iso.make_current()
|
||||
|
||||
func _input(event):
|
||||
# Cambio Camera Manuale e Cinematic Toggle
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
|
||||
# --- TASTO C: TOGGLE CINEMATIC MODE ---
|
||||
if event.keycode == KEY_C:
|
||||
is_cinematic_mode = !is_cinematic_mode
|
||||
if is_cinematic_mode:
|
||||
cinematic_timer = 0.0 # Azzera il timer
|
||||
# Cerca qual è la camera attualmente attiva per iniziare il ciclo da lì
|
||||
for i in range(array_camere.size()):
|
||||
if array_camere[i].current:
|
||||
cinematic_index = i
|
||||
break
|
||||
print("Cinematic Mode: ATTIVATA")
|
||||
else:
|
||||
print("Cinematic Mode: DISATTIVATA")
|
||||
return # Usciamo per non eseguire altri controlli in questo frame
|
||||
|
||||
# --- CONTROLLI MANUALI (1-5) ---
|
||||
var intervento_manuale = false
|
||||
|
||||
if event.keycode == KEY_1 and camera_iso:
|
||||
camera_iso.make_current()
|
||||
is_moving_enabled = true
|
||||
intervento_manuale = true
|
||||
|
||||
elif event.keycode == KEY_2 and camera_frontale:
|
||||
camera_frontale.make_current()
|
||||
is_moving_enabled = false
|
||||
intervento_manuale = true
|
||||
|
||||
elif event.keycode == KEY_3 and camera_laterale:
|
||||
camera_laterale.make_current()
|
||||
is_moving_enabled = false
|
||||
intervento_manuale = true
|
||||
|
||||
elif event.keycode == KEY_4 and camera_laterale_2:
|
||||
camera_laterale_2.make_current()
|
||||
is_moving_enabled = false
|
||||
intervento_manuale = true
|
||||
|
||||
elif event.keycode == KEY_5 and camera_alta:
|
||||
camera_alta.make_current()
|
||||
is_moving_enabled = false
|
||||
intervento_manuale = true
|
||||
|
||||
# Se l'utente preme un numero per cambiare camera manualmente, spegni la cinematic mode
|
||||
if intervento_manuale and is_cinematic_mode:
|
||||
is_cinematic_mode = false
|
||||
print("Cinematic Mode: DISATTIVATA (Intervento manuale)")
|
||||
|
||||
# Rotazione: Solo se il movimento è abilitato (Camera 1)
|
||||
if is_moving_enabled:
|
||||
if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||
rotate_y(deg_to_rad(-event.relative.x * rotation_speed))
|
||||
|
||||
func _process(delta):
|
||||
# --- LOGICA CINEMATIC MODE ---
|
||||
if is_cinematic_mode and array_camere.size() > 0:
|
||||
cinematic_timer += delta
|
||||
if cinematic_timer >= tempo_cambio_camera:
|
||||
cinematic_timer = 0.0
|
||||
# Passa alla camera successiva e se è l'ultima ricomincia da zero
|
||||
cinematic_index = (cinematic_index + 1) % array_camere.size()
|
||||
|
||||
var next_cam = array_camere[cinematic_index]
|
||||
next_cam.make_current()
|
||||
|
||||
# Abilita il movimento WASD solo se la camera inquadrata è quella isometrica (la 1)
|
||||
is_moving_enabled = (next_cam == camera_iso)
|
||||
|
||||
# --- LOGICA DI MOVIMENTO WASD ---
|
||||
# Se siamo in camera 2, 3, 4 o 5, non processare il movimento WASD
|
||||
if not is_moving_enabled:
|
||||
return
|
||||
|
||||
var input_dir = Vector3.ZERO
|
||||
if Input.is_key_pressed(KEY_W): input_dir.z -= 1
|
||||
if Input.is_key_pressed(KEY_S): input_dir.z += 1
|
||||
if Input.is_key_pressed(KEY_A): input_dir.x -= 1
|
||||
if Input.is_key_pressed(KEY_D): input_dir.x += 1
|
||||
|
||||
if input_dir != Vector3.ZERO:
|
||||
input_dir = input_dir.normalized()
|
||||
var forward = transform.basis.z
|
||||
var right = transform.basis.x
|
||||
forward.y = 0
|
||||
right.y = 0
|
||||
var motion = (forward * input_dir.z + right * input_dir.x).normalized()
|
||||
position += motion * move_speed * delta
|
||||
1
Scripts/CameraMove.gd.uid
Normal file
1
Scripts/CameraMove.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b8x6nf7u6iopv
|
||||
253
Scripts/Day_night_Cycle.gd
Normal file
253
Scripts/Day_night_Cycle.gd
Normal file
@@ -0,0 +1,253 @@
|
||||
extends Node3D
|
||||
|
||||
@export_group("Luce Direzionale e Ambiente")
|
||||
@export var color_mattina: Color = Color(1.0, 0.9, 0.8, 1.0)
|
||||
@export var color_pomeriggio: Color = Color(1.0, 0.6, 0.2, 1.0)
|
||||
@export var color_notte: Color = Color(0.1, 0.1, 0.3, 1.0)
|
||||
|
||||
@export_group("Colori Cielo - Alto (Top)")
|
||||
@export var sky_top_mattina: Color = Color(0.35, 0.65, 0.9, 1.0)
|
||||
@export var sky_top_pomeriggio: Color = Color(0.7, 0.45, 0.2, 1.0)
|
||||
@export var sky_top_notte: Color = Color(0.05, 0.05, 0.15, 1.0)
|
||||
|
||||
@export_group("Colori Cielo - Orizzonte (Horizon)")
|
||||
@export var sky_horizon_mattina: Color = Color(0.75, 0.85, 0.95, 1.0)
|
||||
@export var sky_horizon_pomeriggio: Color = Color(0.95, 0.65, 0.35, 1.0)
|
||||
@export var sky_horizon_notte: Color = Color(0.15, 0.1, 0.25, 1.0)
|
||||
|
||||
@export_group("Ciclo Temporale")
|
||||
@export_range(1.0, 3.0) var tempo_giorno: float = 1.0
|
||||
@export var durata_transizione: float = 3.0
|
||||
@export_range(0.0, 1.0) var energia_luce_notte: float = 0.3
|
||||
|
||||
@export_group("Materiali Extra")
|
||||
@export var materiale_nebbia: ShaderMaterial
|
||||
@export var materiale_pioggia_gocce: StandardMaterial3D
|
||||
|
||||
@export_group("Meteo (Pioggia & Fulmini)")
|
||||
@export var colore_mood_pioggia: Color = Color(0.5, 0.6, 0.7, 1.0)
|
||||
@export var tempo_min_fulmine: float = 5.0
|
||||
@export var tempo_max_fulmine: float = 15.0
|
||||
@export var colore_fulmine: Color = Color(1.0, 1.0, 0.8, 1.0)
|
||||
@export var fulmini_min: int = 1
|
||||
@export var fulmini_max: int = 3
|
||||
@export var scala_min_fulmine: float = 1.0
|
||||
@export var scala_max_fulmine: float = 3.0
|
||||
|
||||
# --- NUOVI PARAMETRI CINEMATIC MODE ---
|
||||
@export_group("Cinematic Mode Ambiente")
|
||||
@export var intervallo_eventi_meteo: float = 8.0 # Ogni quanti secondi cambia qualcosa da solo
|
||||
|
||||
@onready var sole: DirectionalLight3D = $DirectionalLight3D
|
||||
@onready var ambiente: WorldEnvironment = $WorldEnvironment
|
||||
@onready var menu_giornata: OptionButton = $CanvasLayer/OptionButton
|
||||
@onready var bottone_pioggia: CheckButton = $CanvasLayer/CheckButtonPioggia
|
||||
@onready var particelle_pioggia: GPUParticles3D = $SubViewportContainer/SubViewport/World/Camere/PioggiaCadente
|
||||
|
||||
# --- NODI FULMINE ---
|
||||
@onready var luce_lampo: DirectionalLight3D = $LuceLampo
|
||||
@onready var sprite_fulmine: Sprite3D = $SpriteFulmine
|
||||
|
||||
var tween_giorno: Tween
|
||||
var is_raining: bool = false
|
||||
var intensita_pioggia: float = 0.0
|
||||
var timer_prossimo_fulmine: float = 0.0
|
||||
|
||||
# Variabili Cinematic Mode
|
||||
var is_cinematic_mode: bool = false
|
||||
var timer_cinematic_env: float = 0.0
|
||||
var sto_cambiando_in_cinematic: bool = false # Serve per non disattivare la modalità quando lo script clicca i bottoni da solo
|
||||
|
||||
func _ready() -> void:
|
||||
if menu_giornata:
|
||||
menu_giornata.clear()
|
||||
menu_giornata.add_item("1 - Mattina")
|
||||
menu_giornata.add_item("2 - Pomeriggio")
|
||||
menu_giornata.add_item("3 - Notte")
|
||||
menu_giornata.selected = int(tempo_giorno) - 1
|
||||
menu_giornata.item_selected.connect(_on_giornata_selezionata)
|
||||
|
||||
if bottone_pioggia:
|
||||
bottone_pioggia.toggled.connect(_on_pioggia_toggled)
|
||||
|
||||
if particelle_pioggia:
|
||||
particelle_pioggia.emitting = false
|
||||
|
||||
_resetta_timer_fulmine()
|
||||
|
||||
# --- ASCOLTO TASTIERA PER CINEMATIC MODE ---
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
|
||||
# Attiva/Disattiva con C
|
||||
if event.keycode == KEY_C:
|
||||
is_cinematic_mode = !is_cinematic_mode
|
||||
if is_cinematic_mode:
|
||||
timer_cinematic_env = intervallo_eventi_meteo # Fa scattare un evento quasi subito
|
||||
print("Ambiente Cinematic: ATTIVATA")
|
||||
else:
|
||||
print("Ambiente Cinematic: DISATTIVATA")
|
||||
return
|
||||
|
||||
# Se premi le camere, si ferma la cinematic mode
|
||||
if event.keycode in [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5]:
|
||||
if is_cinematic_mode:
|
||||
is_cinematic_mode = false
|
||||
print("Ambiente Cinematic: DISATTIVATA (Cambio camera manuale)")
|
||||
|
||||
func _on_giornata_selezionata(index: int) -> void:
|
||||
# Se l'utente clicca la tendina a mano, spegni la cinematic mode
|
||||
if not sto_cambiando_in_cinematic and is_cinematic_mode:
|
||||
is_cinematic_mode = false
|
||||
print("Ambiente Cinematic: DISATTIVATA (Orario cambiato manualmente)")
|
||||
|
||||
var target_tempo = float(index + 1)
|
||||
if tween_giorno and tween_giorno.is_valid():
|
||||
tween_giorno.kill()
|
||||
tween_giorno = create_tween()
|
||||
tween_giorno.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
tween_giorno.tween_property(self, "tempo_giorno", target_tempo, durata_transizione)
|
||||
|
||||
func _on_pioggia_toggled(toggled_on: bool) -> void:
|
||||
# Se l'utente clicca il bottone della pioggia a mano, spegni la cinematic mode
|
||||
if not sto_cambiando_in_cinematic and is_cinematic_mode:
|
||||
is_cinematic_mode = false
|
||||
print("Ambiente Cinematic: DISATTIVATA (Pioggia cambiata manualmente)")
|
||||
|
||||
is_raining = toggled_on
|
||||
if particelle_pioggia:
|
||||
particelle_pioggia.emitting = is_raining
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# --- LOGICA EVENTI CASUALI CINEMATIC MODE ---
|
||||
if is_cinematic_mode:
|
||||
timer_cinematic_env -= delta
|
||||
if timer_cinematic_env <= 0.0:
|
||||
_genera_evento_meteo_casuale()
|
||||
# Reset timer con un po' di casualità per non renderlo troppo robotico
|
||||
timer_cinematic_env = randf_range(intervallo_eventi_meteo * 0.8, intervallo_eventi_meteo * 1.5)
|
||||
|
||||
# --- LOGICA NORMALE AMBIENTE ---
|
||||
var base_tint: Color
|
||||
var base_sky_top: Color
|
||||
var base_sky_horizon: Color
|
||||
|
||||
if is_raining:
|
||||
intensita_pioggia = move_toward(intensita_pioggia, 1.0, delta * 0.5)
|
||||
|
||||
timer_prossimo_fulmine -= delta
|
||||
if timer_prossimo_fulmine <= 0.0:
|
||||
_scatena_fulmine()
|
||||
_resetta_timer_fulmine()
|
||||
else:
|
||||
intensita_pioggia = move_toward(intensita_pioggia, 0.0, delta * 0.5)
|
||||
|
||||
if tempo_giorno <= 2.0:
|
||||
var t = tempo_giorno - 1.0
|
||||
base_tint = color_mattina.lerp(color_pomeriggio, t)
|
||||
base_sky_top = sky_top_mattina.lerp(sky_top_pomeriggio, t)
|
||||
base_sky_horizon = sky_horizon_mattina.lerp(sky_horizon_pomeriggio, t)
|
||||
else:
|
||||
var t = tempo_giorno - 2.0
|
||||
base_tint = color_pomeriggio.lerp(color_notte, t)
|
||||
base_sky_top = sky_top_pomeriggio.lerp(sky_top_notte, t)
|
||||
base_sky_horizon = sky_horizon_pomeriggio.lerp(sky_horizon_notte, t)
|
||||
|
||||
var final_tint = base_tint.lerp(base_tint * colore_mood_pioggia, intensita_pioggia)
|
||||
var final_sky_top = base_sky_top.lerp(base_sky_top * colore_mood_pioggia, intensita_pioggia)
|
||||
var final_sky_horizon = base_sky_horizon.lerp(base_sky_horizon * colore_mood_pioggia, intensita_pioggia)
|
||||
|
||||
var night_val = clamp(tempo_giorno - 2.0, 0.0, 1.0)
|
||||
var colore_riflesso = Color(0.4, 0.5, 0.6, 1.0)
|
||||
var colore_gocce = final_sky_horizon.lerp(colore_riflesso, 0.15)
|
||||
colore_gocce = colore_gocce.lerp(colore_gocce * 0.4, night_val)
|
||||
|
||||
if materiale_pioggia_gocce:
|
||||
materiale_pioggia_gocce.albedo_color = colore_gocce
|
||||
|
||||
if sole:
|
||||
sole.light_color = final_tint
|
||||
var target_energy = lerp(1.0, energia_luce_notte, night_val)
|
||||
sole.light_energy = lerp(target_energy, target_energy * 0.4, intensita_pioggia)
|
||||
|
||||
if ambiente and ambiente.environment:
|
||||
ambiente.environment.ambient_light_color = final_tint
|
||||
ambiente.environment.fog_light_color = final_sky_horizon
|
||||
|
||||
var sky_mat = ambiente.environment.sky.sky_material as ShaderMaterial
|
||||
if sky_mat:
|
||||
sky_mat.set_shader_parameter("sky_top_color", final_sky_top)
|
||||
sky_mat.set_shader_parameter("sky_horizon_color", final_sky_horizon)
|
||||
sky_mat.set_shader_parameter("sun_color", final_tint)
|
||||
sky_mat.set_shader_parameter("night_intensity", night_val)
|
||||
|
||||
if materiale_nebbia:
|
||||
materiale_nebbia.set_shader_parameter("night_intensity", night_val)
|
||||
materiale_nebbia.set_shader_parameter("sun_color", final_tint)
|
||||
|
||||
# --- FUNZIONE GENERATORE CASUALE ---
|
||||
func _genera_evento_meteo_casuale() -> void:
|
||||
sto_cambiando_in_cinematic = true # Avvisiamo il codice che stiamo facendo modifiche "automatiche"
|
||||
|
||||
var scelta = randi() % 3
|
||||
|
||||
if scelta == 0:
|
||||
# Cambia orario
|
||||
var nuovo_orario = randi() % 3
|
||||
if menu_giornata:
|
||||
menu_giornata.selected = nuovo_orario
|
||||
_on_giornata_selezionata(nuovo_orario)
|
||||
|
||||
elif scelta == 1:
|
||||
# Cambia meteo (accende o spegne la pioggia)
|
||||
var piove = randf() > 0.5
|
||||
if bottone_pioggia:
|
||||
bottone_pioggia.set_pressed_no_signal(piove)
|
||||
_on_pioggia_toggled(piove)
|
||||
|
||||
elif scelta == 2:
|
||||
# Forza un fulmine (se non piove, fa iniziare a piovere per dare senso al fulmine!)
|
||||
if not is_raining:
|
||||
if bottone_pioggia:
|
||||
bottone_pioggia.set_pressed_no_signal(true)
|
||||
_on_pioggia_toggled(true)
|
||||
_scatena_fulmine()
|
||||
|
||||
sto_cambiando_in_cinematic = false # Fine modifiche automatiche
|
||||
|
||||
# --- FUNZIONI FULMINI ---
|
||||
|
||||
func _resetta_timer_fulmine() -> void:
|
||||
timer_prossimo_fulmine = randf_range(tempo_min_fulmine, tempo_max_fulmine)
|
||||
|
||||
func _scatena_fulmine() -> void:
|
||||
if luce_lampo:
|
||||
var tween_luce = create_tween()
|
||||
luce_lampo.light_energy = 5.0
|
||||
|
||||
tween_luce.tween_interval(0.05)
|
||||
tween_luce.tween_callback(func(): luce_lampo.light_energy = 0.0)
|
||||
tween_luce.tween_interval(0.05)
|
||||
tween_luce.tween_callback(func(): luce_lampo.light_energy = 2.0)
|
||||
tween_luce.tween_method(func(val): luce_lampo.light_energy = val, 2.0, 0.0, 0.2)
|
||||
|
||||
var numero_scariche = randi_range(fulmini_min, fulmini_max)
|
||||
|
||||
for i in numero_scariche:
|
||||
if sprite_fulmine:
|
||||
var angolo_casuale = randf() * PI * 2.0
|
||||
var distanza = 20.0
|
||||
sprite_fulmine.position = Vector3(cos(angolo_casuale) * distanza, 20.0, sin(angolo_casuale) * distanza)
|
||||
|
||||
sprite_fulmine.modulate = colore_fulmine
|
||||
sprite_fulmine.modulate.a = 1.0
|
||||
|
||||
var s = randf_range(scala_min_fulmine, scala_max_fulmine)
|
||||
sprite_fulmine.scale = Vector3(s, s, s)
|
||||
|
||||
var tween_sprite = create_tween()
|
||||
tween_sprite.tween_interval(0.1)
|
||||
tween_sprite.tween_property(sprite_fulmine, "modulate:a", 0.0, 0.2)
|
||||
|
||||
if numero_scariche > 1:
|
||||
await get_tree().create_timer(randf_range(0.05, 0.15)).timeout
|
||||
1
Scripts/Day_night_Cycle.gd.uid
Normal file
1
Scripts/Day_night_Cycle.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dcaxomfkulrig
|
||||
47
Scripts/Leaf_Test1.gd
Normal file
47
Scripts/Leaf_Test1.gd
Normal file
@@ -0,0 +1,47 @@
|
||||
@tool
|
||||
extends MultiMeshInstance3D
|
||||
|
||||
@export var mesh_volume: MeshInstance3D : # Trascina qui il tuo volume low poly
|
||||
set(val):
|
||||
mesh_volume = val
|
||||
generate_leaves()
|
||||
|
||||
@export var scale_multiplier: float = 1.5 # Per rendere i piani grandi come in foto
|
||||
|
||||
func generate_leaves():
|
||||
if not mesh_volume or not mesh_volume.mesh:
|
||||
return
|
||||
|
||||
var mdt = MeshDataTool.new()
|
||||
mdt.create_from_surface(mesh_volume.mesh, 0)
|
||||
|
||||
var face_count = mdt.get_face_count()
|
||||
|
||||
# Inizializza il MultiMesh
|
||||
multimesh.instance_count = face_count
|
||||
|
||||
for i in range(face_count):
|
||||
# 1. Trova il centro della faccia
|
||||
var v1 = mdt.get_vertex(mdt.get_face_vertex(i, 0))
|
||||
var v2 = mdt.get_vertex(mdt.get_face_vertex(i, 1))
|
||||
var v3 = mdt.get_vertex(mdt.get_face_vertex(i, 2))
|
||||
var center = (v1 + v2 + v3) / 3.0
|
||||
|
||||
# 2. Ottieni la normale della faccia per orientare il piano
|
||||
var normal = mdt.get_face_normal(i)
|
||||
|
||||
# 3. Crea la trasformazione
|
||||
# Usiamo 'look_at' per far sì che il piano sia parallelo alla faccia
|
||||
var pos = center
|
||||
var target = pos + normal
|
||||
var xform = Transform3D().looking_at(normal, Vector3.UP)
|
||||
|
||||
# Applichiamo posizione e scala
|
||||
xform.origin = pos
|
||||
xform = xform.scaled(Vector3.ONE * scale_multiplier)
|
||||
|
||||
# 4. Applica al MultiMesh
|
||||
multimesh.set_instance_transform(i, xform)
|
||||
|
||||
# Opzionale: nascondi il volume originale se vuoi vedere solo i piani
|
||||
# mesh_volume.visible = false
|
||||
1
Scripts/Leaf_Test1.gd.uid
Normal file
1
Scripts/Leaf_Test1.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dxgc5kjl1048
|
||||
121
Scripts/binari_treno.gd
Normal file
121
Scripts/binari_treno.gd
Normal file
@@ -0,0 +1,121 @@
|
||||
extends Path3D
|
||||
|
||||
@export_group("Impostazioni Treno")
|
||||
@export var velocita_treno: float = 6.0
|
||||
@export var in_movimento: bool = true
|
||||
|
||||
@export_group("Costruzione Rotaie")
|
||||
@export var distanza_traversine: float = 1.0 # Ogni quanti metri mettere un asse di legno
|
||||
|
||||
var nodo_treno: PathFollow3D
|
||||
|
||||
func _ready() -> void:
|
||||
# Controlla se hai disegnato il percorso
|
||||
if curve == null or curve.get_baked_length() == 0:
|
||||
print("ATTENZIONE: Devi prima disegnare i punti del Path3D!")
|
||||
return
|
||||
|
||||
_costruisci_rotaie_da_zero()
|
||||
_costruisci_treno_da_zero()
|
||||
|
||||
func _costruisci_rotaie_da_zero() -> void:
|
||||
# 1. Creiamo i materiali (Colori) per le rotaie
|
||||
var mat_legno = StandardMaterial3D.new()
|
||||
mat_legno.albedo_color = Color(0.35, 0.2, 0.1) # Marrone scuro
|
||||
|
||||
var mat_ferro = StandardMaterial3D.new()
|
||||
mat_ferro.albedo_color = Color(0.6, 0.6, 0.65) # Grigio metallico
|
||||
mat_ferro.metallic = 0.8
|
||||
|
||||
# 2. Creiamo le forme geometriche base (Box)
|
||||
var mesh_traversina = BoxMesh.new()
|
||||
mesh_traversina.size = Vector3(1.8, 0.1, 0.3)
|
||||
|
||||
var mesh_binario = BoxMesh.new()
|
||||
# La lunghezza del binario è uguale alla distanza tra le traversine per connettersi perfettamente
|
||||
mesh_binario.size = Vector3(0.1, 0.15, distanza_traversine + 0.05)
|
||||
|
||||
# 3. Calcoliamo quanti pezzi servono
|
||||
var lunghezza_totale = curve.get_baked_length()
|
||||
var numero_pezzi = int(lunghezza_totale / distanza_traversine)
|
||||
|
||||
# 4. Piazziamo i pezzi lungo la curva
|
||||
for i in range(numero_pezzi):
|
||||
var distanza = i * distanza_traversine
|
||||
var transform_punto = curve.sample_baked_with_rotation(distanza, true, true)
|
||||
|
||||
# Nodo "Pezzo" che fa da raggruppamento
|
||||
var pezzo_binario = Node3D.new()
|
||||
pezzo_binario.transform = transform_punto
|
||||
add_child(pezzo_binario)
|
||||
|
||||
# Creiamo la traversina in legno
|
||||
var traversina = MeshInstance3D.new()
|
||||
traversina.mesh = mesh_traversina
|
||||
traversina.material_override = mat_legno
|
||||
pezzo_binario.add_child(traversina)
|
||||
|
||||
# Creiamo il binario sinistro
|
||||
var binario_sx = MeshInstance3D.new()
|
||||
binario_sx.mesh = mesh_binario
|
||||
binario_sx.material_override = mat_ferro
|
||||
binario_sx.position = Vector3(-0.6, 0.1, 0) # Spostato a sinistra e leggermente in alto
|
||||
pezzo_binario.add_child(binario_sx)
|
||||
|
||||
# Creiamo il binario destro
|
||||
var binario_dx = MeshInstance3D.new()
|
||||
binario_dx.mesh = mesh_binario
|
||||
binario_dx.material_override = mat_ferro
|
||||
binario_dx.position = Vector3(0.6, 0.1, 0) # Spostato a destra e leggermente in alto
|
||||
pezzo_binario.add_child(binario_dx)
|
||||
|
||||
|
||||
func _costruisci_treno_da_zero() -> void:
|
||||
# 1. Creiamo il PathFollow che gestisce il movimento
|
||||
nodo_treno = PathFollow3D.new()
|
||||
nodo_treno.rotation_mode = PathFollow3D.ROTATION_Y # IMPORTANTE: Evita che il treno si inclini di lato
|
||||
nodo_treno.cubic_interp = false # Rende la velocità costante ed elimina i tremolii
|
||||
nodo_treno.loop = true
|
||||
add_child(nodo_treno)
|
||||
|
||||
# 2. Materiali del treno
|
||||
var mat_carrozzeria = StandardMaterial3D.new()
|
||||
mat_carrozzeria.albedo_color = Color(0.8, 0.15, 0.15) # Rosso acceso
|
||||
|
||||
var mat_vetro = StandardMaterial3D.new()
|
||||
mat_vetro.albedo_color = Color(0.2, 0.8, 1.0) # Azzurro
|
||||
|
||||
# 3. Costruiamo la Base del treno
|
||||
var base_treno = MeshInstance3D.new()
|
||||
var mesh_base = BoxMesh.new()
|
||||
mesh_base.size = Vector3(1.4, 0.8, 3.0)
|
||||
base_treno.mesh = mesh_base
|
||||
base_treno.material_override = mat_carrozzeria
|
||||
base_treno.position = Vector3(0, 0.6, 0) # Alzato per stare sopra le rotaie
|
||||
nodo_treno.add_child(base_treno)
|
||||
|
||||
# 4. Costruiamo la Cabina del macchinista
|
||||
var cabina = MeshInstance3D.new()
|
||||
var mesh_cabina = BoxMesh.new()
|
||||
mesh_cabina.size = Vector3(1.4, 1.0, 1.2)
|
||||
cabina.mesh = mesh_cabina
|
||||
cabina.material_override = mat_vetro
|
||||
cabina.position = Vector3(0, 1.5, -0.8) # Messa dietro e in alto
|
||||
nodo_treno.add_child(cabina)
|
||||
|
||||
# 5. Costruiamo la Ciminiera (davanti)
|
||||
var ciminiera = MeshInstance3D.new()
|
||||
var mesh_ciminiera = CylinderMesh.new()
|
||||
mesh_ciminiera.top_radius = 0.2
|
||||
mesh_ciminiera.bottom_radius = 0.3
|
||||
mesh_ciminiera.height = 0.8
|
||||
ciminiera.mesh = mesh_ciminiera
|
||||
ciminiera.material_override = mat_carrozzeria
|
||||
ciminiera.position = Vector3(0, 1.2, 1.0)
|
||||
nodo_treno.add_child(ciminiera)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# Se in movimento, fa avanzare automaticamente il nodo_treno lungo la curva
|
||||
if in_movimento and nodo_treno:
|
||||
nodo_treno.progress += velocita_treno * delta
|
||||
1
Scripts/binari_treno.gd.uid
Normal file
1
Scripts/binari_treno.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c2ec8k6lbx5cb
|
||||
123
Scripts/edge_detection_compositor.gd
Normal file
123
Scripts/edge_detection_compositor.gd
Normal file
@@ -0,0 +1,123 @@
|
||||
@tool
|
||||
class_name EdgeDectionCompositor
|
||||
extends CompositorEffect
|
||||
|
||||
var rd: RenderingDevice
|
||||
var shader: RID
|
||||
var pipeline: RID
|
||||
var parameter_storage_buffer := RID()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_POST_OPAQUE
|
||||
rd = RenderingServer.get_rendering_device()
|
||||
RenderingServer.call_on_render_thread(_initialize_compute)
|
||||
|
||||
var data := PackedFloat32Array()
|
||||
data.resize(20)
|
||||
data.fill(0)
|
||||
var parameter_data := data.to_byte_array()
|
||||
parameter_storage_buffer = rd.storage_buffer_create(parameter_data.size(), parameter_data)
|
||||
|
||||
|
||||
|
||||
# System notifications, we want to react on the notification that
|
||||
# alerts us we are about to be destroyed.
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
if shader.is_valid():
|
||||
# Freeing our shader will also free any dependents such as the pipeline!
|
||||
rd.free_rid(shader)
|
||||
|
||||
|
||||
#region Code in this region runs on the rendering thread.
|
||||
# Compile our shader at initialization.
|
||||
func _initialize_compute() -> void:
|
||||
rd = RenderingServer.get_rendering_device()
|
||||
if not rd:
|
||||
return
|
||||
|
||||
# Compile our shader.
|
||||
var shader_file := load("res://Shaders/PostProcessing/edge_detection_shader.glsl")
|
||||
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
|
||||
|
||||
shader = rd.shader_create_from_spirv(shader_spirv)
|
||||
if shader.is_valid():
|
||||
pipeline = rd.compute_pipeline_create(shader)
|
||||
|
||||
|
||||
# Called by the rendering thread every frame.
|
||||
func _render_callback(p_effect_callback_type: EffectCallbackType, p_render_data: RenderData) -> void:
|
||||
if rd and p_effect_callback_type == EFFECT_CALLBACK_TYPE_POST_OPAQUE and pipeline.is_valid():
|
||||
# Get our render scene buffers object, this gives us access to our render buffers.
|
||||
# Note that implementation differs per renderer hence the need for the cast.
|
||||
var render_scene_buffers := p_render_data.get_render_scene_buffers()
|
||||
if render_scene_buffers:
|
||||
# Get our render size, this is the 3D render resolution!
|
||||
var size = render_scene_buffers.get_internal_size()
|
||||
if size.x == 0 and size.y == 0:
|
||||
return
|
||||
|
||||
@warning_ignore("integer_division")
|
||||
var x_groups : int = (size.x - 1.0) / 8.0 + 1.0
|
||||
@warning_ignore("integer_division")
|
||||
var y_groups : int = (size.y - 1.0) / 8.0 + 1.0
|
||||
var z_groups := 1.0
|
||||
|
||||
|
||||
# Loop through views just in case we're doing stereo rendering. No extra cost if this is mono.
|
||||
var view_count: int = render_scene_buffers.get_view_count()
|
||||
for view in view_count:
|
||||
# Get the RID for our color image, we will be reading from and writing to it.
|
||||
var input_image: RID = render_scene_buffers.get_color_layer(view)
|
||||
var input_depth: RID = render_scene_buffers.get_depth_layer(view)
|
||||
var input_normal: RID = render_scene_buffers.get_texture("forward_clustered","normal_roughness")
|
||||
|
||||
var texture_sampler = RDSamplerState.new()
|
||||
texture_sampler = rd.sampler_create(texture_sampler)
|
||||
|
||||
|
||||
var parameters := PackedFloat32Array([size.x, size.y, 0.0, 0.0])
|
||||
var inv_proj_mat := p_render_data.get_render_scene_data().get_cam_projection().inverse()
|
||||
var inv_proj_mat_array := PackedVector4Array([inv_proj_mat.x, inv_proj_mat.y, inv_proj_mat.z, inv_proj_mat.w])
|
||||
|
||||
var parameter_data := parameters.to_byte_array()
|
||||
parameter_data.append_array(inv_proj_mat_array.to_byte_array())
|
||||
rd.buffer_update(parameter_storage_buffer, 0, parameter_data.size(), parameter_data)
|
||||
|
||||
var uniform_parameter := RDUniform.new()
|
||||
uniform_parameter.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
|
||||
uniform_parameter.binding = 0
|
||||
uniform_parameter.add_id(parameter_storage_buffer)
|
||||
|
||||
# Create a uniform set, this will be cached, the cache will be cleared if our viewports configuration is changed.
|
||||
var uniform_color := RDUniform.new()
|
||||
uniform_color.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
|
||||
uniform_color.binding = 1
|
||||
uniform_color.add_id(input_image)
|
||||
|
||||
var uniform_depth := RDUniform.new()
|
||||
uniform_depth.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE
|
||||
uniform_depth.binding = 2
|
||||
uniform_depth.add_id(texture_sampler)
|
||||
uniform_depth.add_id(input_depth)
|
||||
|
||||
var uniform_normal := RDUniform.new()
|
||||
uniform_normal.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE
|
||||
uniform_normal.binding = 3
|
||||
uniform_normal.add_id(texture_sampler)
|
||||
uniform_normal.add_id(input_normal)
|
||||
|
||||
|
||||
var uniform_set := UniformSetCacheRD.get_cache(shader, 0, [uniform_parameter, uniform_color, uniform_depth, uniform_normal])
|
||||
|
||||
|
||||
# Run our compute shader.
|
||||
var compute_list := rd.compute_list_begin()
|
||||
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
|
||||
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
|
||||
#rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4)
|
||||
rd.compute_list_dispatch(compute_list, x_groups, y_groups, z_groups)
|
||||
rd.compute_list_end()
|
||||
rd.free_rid(texture_sampler)
|
||||
#endregion
|
||||
1
Scripts/edge_detection_compositor.gd.uid
Normal file
1
Scripts/edge_detection_compositor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://brcimd12tx3dm
|
||||
8
Scripts/fuoco_artificio.gd
Normal file
8
Scripts/fuoco_artificio.gd
Normal file
@@ -0,0 +1,8 @@
|
||||
extends GPUParticles3D
|
||||
|
||||
func _ready() -> void:
|
||||
# Quando spawna, emette le particelle
|
||||
emitting = true
|
||||
# Si autodistrugge dopo che le particelle sono svanite
|
||||
await get_tree().create_timer(lifetime + 0.5).timeout
|
||||
queue_free()
|
||||
1
Scripts/fuoco_artificio.gd.uid
Normal file
1
Scripts/fuoco_artificio.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c1wwiocqnv0qs
|
||||
47
Scripts/fuoco_d'artificio_root.gd
Normal file
47
Scripts/fuoco_d'artificio_root.gd
Normal file
@@ -0,0 +1,47 @@
|
||||
extends Node3D
|
||||
|
||||
# Funzione chiamata dal treno per iniettare il colore scelto
|
||||
func imposta_colore(colore_target: Color) -> void:
|
||||
# Cerchiamo i 3 nodi specifici per nome
|
||||
# Assicurati che i nomi nella tua scena siano ESATTAMENTE questi!
|
||||
var razzo = $"1_RazzoIniziale"
|
||||
var esplosione = $"2_EsplosionePrincipale"
|
||||
var scie = $"3_ScieCadenti"
|
||||
|
||||
# 1. Il Razzo iniziale lo vogliamo BIANCO BRILLANTE (la "testa")
|
||||
# Usiamo un bianco "sovraccarico" (valori > 1) per farlo brillare di più
|
||||
_colora_nodo_particellare(razzo, Color(1.5, 1.5, 1.5))
|
||||
|
||||
# 2. L'esplosione principale la vogliamo BIANCA (i punti centrali dello scoppio)
|
||||
_colora_nodo_particellare(esplosione, Color(1.2, 1.2, 1.2))
|
||||
|
||||
# 3. Le scie che cadono le vogliamo del COLORE SCELTO dal treno
|
||||
_colora_nodo_particellare(scie, colore_target)
|
||||
|
||||
# Funzione helper per duplicare e colorare il materiale in modo sicuro
|
||||
func _colora_nodo_particellare(nodo: GPUParticles3D, colore_da_applicare: Color) -> void:
|
||||
if nodo and nodo.draw_pass_1:
|
||||
var mesh = nodo.draw_pass_1 as QuadMesh
|
||||
if mesh and mesh.material:
|
||||
# --- IL SEGRETO E' QUI! ---
|
||||
# Duplichiamo il materiale. Ora questo nodo ha una sua copia privata
|
||||
# e non influenzerà gli altri fuochi d'artificio.
|
||||
var mat_unico = mesh.material.duplicate() as StandardMaterial3D
|
||||
|
||||
# Impostiamo il colore e ci assicuriamo che brilli
|
||||
mat_unico.albedo_color = colore_da_applicare
|
||||
mat_unico.blend_mode = BaseMaterial3D.BLEND_MODE_ADD # Fondamentale per l'effetto luce
|
||||
mat_unico.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
|
||||
# Assegniamo il nuovo materiale unico alla mesh
|
||||
mesh.material = mat_unico
|
||||
|
||||
# Funzione per far partire l'effetto
|
||||
func accendi() -> void:
|
||||
var razzo = $"1_RazzoIniziale"
|
||||
if razzo:
|
||||
razzo.emitting = true # Accende solo la miccia iniziale
|
||||
|
||||
# Tempo di vita totale aumentato per dare tempo alle scie di cadere
|
||||
await get_tree().create_timer(6.0).timeout
|
||||
queue_free()
|
||||
1
Scripts/fuoco_d'artificio_root.gd.uid
Normal file
1
Scripts/fuoco_d'artificio_root.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://buwbary2rs60d
|
||||
10
Scripts/locomotiva_target.gd
Normal file
10
Scripts/locomotiva_target.gd
Normal file
@@ -0,0 +1,10 @@
|
||||
extends PathFollow3D
|
||||
|
||||
@export var velocita_treno: float = 5.0
|
||||
@export var in_movimento: bool = true
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if in_movimento:
|
||||
# 'progress' è una variabile nativa di PathFollow3D
|
||||
# Aumentandola, il nodo si sposta lungo la curva in automatico!
|
||||
progress += velocita_treno * delta
|
||||
1
Scripts/locomotiva_target.gd.uid
Normal file
1
Scripts/locomotiva_target.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bvn3yskdmvcuj
|
||||
62
Scripts/multi_mesh_instance_3d.gd
Normal file
62
Scripts/multi_mesh_instance_3d.gd
Normal file
@@ -0,0 +1,62 @@
|
||||
@tool
|
||||
extends MultiMeshInstance3D
|
||||
|
||||
# Trascina qui la MeshInstance3D che rappresenta il volume della tua chioma
|
||||
@export var mesh_volume: MeshInstance3D
|
||||
|
||||
# Clicca su questa casella nell'Inspector per generare le foglie
|
||||
@export var genera_foglie: bool = false:
|
||||
set(val):
|
||||
if val:
|
||||
popola_chioma()
|
||||
genera_foglie = false # Resetta il pulsante
|
||||
|
||||
# Scala delle singole foglie
|
||||
@export var scala_foglie: Vector3 = Vector3(1, 1, 1)
|
||||
|
||||
func popola_chioma():
|
||||
if not mesh_volume or not mesh_volume.mesh:
|
||||
print("Errore: Assegna una MeshInstance3D valida a 'Mesh Volume'.")
|
||||
return
|
||||
|
||||
if not multimesh:
|
||||
print("Errore: Il nodo MultiMeshInstance3D non ha una risorsa MultiMesh assegnata.")
|
||||
return
|
||||
|
||||
# Assicurati che il formato sia 3D per posizioni, rotazioni e scale
|
||||
multimesh.transform_format = MultiMesh.TRANSFORM_3D
|
||||
|
||||
var mdt = MeshDataTool.new()
|
||||
# Crea i dati dalla prima superficie della mesh (indice 0)
|
||||
mdt.create_from_surface(mesh_volume.mesh, 0)
|
||||
|
||||
var numero_facce = mdt.get_face_count()
|
||||
multimesh.instance_count = numero_facce
|
||||
|
||||
print("Generazione di ", numero_facce, " foglie...")
|
||||
|
||||
for i in range(numero_facce):
|
||||
# Ottieni i tre vertici che formano la faccia
|
||||
var v1 = mdt.get_vertex(mdt.get_face_vertex(i, 0))
|
||||
var v2 = mdt.get_vertex(mdt.get_face_vertex(i, 1))
|
||||
var v3 = mdt.get_vertex(mdt.get_face_vertex(i, 2))
|
||||
|
||||
# Calcola il centro della faccia
|
||||
var centro_faccia = (v1 + v2 + v3) / 3.0
|
||||
|
||||
# Crea una trasformazione con posizione, rotazione casuale e scala
|
||||
var transform = Transform3D()
|
||||
transform.origin = centro_faccia
|
||||
|
||||
# Rotazione casuale per un look naturale
|
||||
transform = transform.rotated(Vector3.UP, randf() * TAU) # Rotazione attorno all'asse Y
|
||||
transform = transform.rotated(Vector3.RIGHT, randf() * TAU) # Rotazione attorno all'asse X
|
||||
transform = transform.rotated(Vector3.FORWARD, randf() * TAU) # Rotazione attorno all'asse Z
|
||||
|
||||
# Applica la scala
|
||||
transform = transform.scaled(scala_foglie)
|
||||
|
||||
# Imposta la trasformazione per questa istanza nel MultiMesh
|
||||
multimesh.set_instance_transform(i, transform)
|
||||
|
||||
print("Foglie generate con successo!")
|
||||
1
Scripts/multi_mesh_instance_3d.gd.uid
Normal file
1
Scripts/multi_mesh_instance_3d.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bj181l5u1b81x
|
||||
49
Scripts/palette_manager.gd
Normal file
49
Scripts/palette_manager.gd
Normal file
@@ -0,0 +1,49 @@
|
||||
extends Node
|
||||
|
||||
@export_group("Gestione Palette")
|
||||
@export var materiale_da_cambiare: ShaderMaterial # Trascina qui il tuo materiale dall'Inspector
|
||||
@export var tempo_transizione: float = 2.0 # Quanto dura la sfumatura (in secondi)
|
||||
@export var valore_riga_2: float = 0.2 # Il valore target della seconda riga
|
||||
|
||||
var palette_spostata: bool = false
|
||||
var tween_palette: Tween
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
# Usiamo il tasto P (Palette) per innescare l'effetto
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
if event.keycode == KEY_P:
|
||||
_cambia_palette_fluida()
|
||||
|
||||
func _cambia_palette_fluida() -> void:
|
||||
if materiale_da_cambiare == null:
|
||||
print("ERRORE: Non hai assegnato il materiale nell'Inspector!")
|
||||
return
|
||||
|
||||
# Invertiamo lo stato
|
||||
palette_spostata = !palette_spostata
|
||||
|
||||
# Decidiamo dove deve arrivare lo slider
|
||||
var target_shift = valore_riga_2 if palette_spostata else 0.0
|
||||
|
||||
# Se c'è già un'animazione in corso (es. hai premuto due volte velocemente), la fermiamo
|
||||
if tween_palette and tween_palette.is_valid():
|
||||
tween_palette.kill()
|
||||
|
||||
# Creiamo la nuova animazione fluida
|
||||
tween_palette = create_tween()
|
||||
|
||||
# Usiamo SINE e IN_OUT per renderla morbidissima in partenza e in arrivo
|
||||
tween_palette.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
|
||||
# Anima il parametro dello shader
|
||||
tween_palette.tween_property(
|
||||
materiale_da_cambiare,
|
||||
"shader_parameter/palette_shift_y", # Sintassi magica per parlare con lo shader
|
||||
target_shift,
|
||||
tempo_transizione
|
||||
)
|
||||
|
||||
if palette_spostata:
|
||||
print("Transizione verso la Palette 2 iniziata...")
|
||||
else:
|
||||
print("Ritorno alla Palette 1 originale...")
|
||||
1
Scripts/palette_manager.gd.uid
Normal file
1
Scripts/palette_manager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bhdoxc2oak88u
|
||||
Reference in New Issue
Block a user