133 lines
5.0 KiB
GDScript
133 lines
5.0 KiB
GDScript
extends AudioStreamPlayer3D
|
|
class_name SpatialAudioPlayer
|
|
|
|
@export var maxRaycastDistance: float = 30.0
|
|
@export var updateFrequencySeconds: float = 0.5
|
|
@export var maxReverbWetness: float = 0.5
|
|
@export var wallLowpassCutoffAmount: int = 600
|
|
|
|
var raycastArray: Array = []
|
|
var distanceArray: Array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
var lastUpdateTime: float = 0.0
|
|
var updateDistances: bool = true
|
|
var currentRaycastIndex: int = 0
|
|
|
|
var audioBusIndex = null
|
|
var audioBusName: String = ""
|
|
|
|
var reverbEffect: AudioEffectReverb
|
|
var lowpassFilterEffect: AudioEffectLowPassFilter
|
|
|
|
var targetLowpassCutoff: float = 20000
|
|
var targetReverbRoomSize: float = 0.0
|
|
var targetReverbWetness: float = 0.0
|
|
var targetVolumeDb: float = 0.0
|
|
|
|
func _ready() -> void:
|
|
audioBusIndex = AudioServer.bus_count
|
|
audioBusName = "SpatialBus#"+str(audioBusIndex)
|
|
AudioServer.add_bus(audioBusIndex)
|
|
AudioServer.set_bus_name(audioBusIndex, audioBusName)
|
|
AudioServer.set_bus_send(audioBusIndex, bus)
|
|
self.bus = audioBusName
|
|
|
|
AudioServer.add_bus_effect(audioBusIndex, AudioEffectReverb.new(), 0)
|
|
reverbEffect = AudioServer.get_bus_effect(audioBusIndex, 0)
|
|
AudioServer.add_bus_effect(audioBusIndex, AudioEffectLowPassFilter.new(), 1)
|
|
lowpassFilterEffect = AudioServer.get_bus_effect(audioBusIndex, 1)
|
|
|
|
targetVolumeDb = volume_db
|
|
volume_db = -60
|
|
|
|
$RayCastDown.target_position = Vector3(0, -maxRaycastDistance, 0)
|
|
$RayCastLeft.target_position = Vector3(maxRaycastDistance, 0, 0)
|
|
$RayCastRight.target_position = Vector3(-maxRaycastDistance, 0, 0)
|
|
$RayCastForward.target_position = Vector3(0, 0, maxRaycastDistance)
|
|
$RayCastForwardLeft.target_position = Vector3(0, 0, maxRaycastDistance)
|
|
$RayCastForwardRight.target_position = Vector3(0, 0, maxRaycastDistance)
|
|
$RayCastBackwardRight.target_position = Vector3(0, 0, -maxRaycastDistance)
|
|
$RayCastBackwardLeft.target_position = Vector3(0, 0, -maxRaycastDistance)
|
|
$RayCastBackward.target_position = Vector3(0, 0, -maxRaycastDistance)
|
|
$RayCastUp.target_position = Vector3(0, maxRaycastDistance, 0)
|
|
|
|
raycastArray.append($RayCastDown)
|
|
raycastArray.append($RayCastLeft)
|
|
raycastArray.append($RayCastRight)
|
|
raycastArray.append($RayCastForward)
|
|
raycastArray.append($RayCastForwardLeft)
|
|
raycastArray.append($RayCastForwardRight)
|
|
raycastArray.append($RayCastBackwardRight)
|
|
raycastArray.append($RayCastBackwardLeft)
|
|
raycastArray.append($RayCastBackward)
|
|
raycastArray.append($RayCastUp)
|
|
|
|
play()
|
|
|
|
func onUpdateRaycastDistance(raycast: RayCast3D, raycastIndex: int) -> void:
|
|
raycast.force_raycast_update()
|
|
var collider = raycast.get_collider()
|
|
if collider != null:
|
|
distanceArray[raycastIndex] = self.global_position.distance_to(raycast.get_collision_point())
|
|
else:
|
|
distanceArray[raycastIndex] = -1
|
|
raycast.enabled = false
|
|
|
|
func onUpdateSpatialAudio(player: Node3D) -> void:
|
|
onUpdateReverb(player)
|
|
onUpdateLowpassFilter(player)
|
|
|
|
func onUpdateReverb(player: Node3D) -> void:
|
|
if reverbEffect != null:
|
|
var roomSize: float = 0.0
|
|
var wetness: float = 0.0
|
|
for distance in distanceArray:
|
|
if distance >= 0:
|
|
roomSize += (distance / maxRaycastDistance) / (float(distanceArray.size()))
|
|
roomSize = min(roomSize, 1.0)
|
|
else:
|
|
wetness -= 1.0 / float(distanceArray.size())
|
|
wetness = max(wetness, 0.0)
|
|
targetReverbWetness = wetness
|
|
targetReverbRoomSize = roomSize
|
|
|
|
func onUpdateLowpassFilter(player : Node3D):
|
|
if lowpassFilterEffect != null:
|
|
var query = PhysicsRayQueryParameters3D.create(self.global_position,self.global_position + (player.global_position - self.global_position).normalized() * maxRaycastDistance, $RayCastForward.get_collision_mask())
|
|
var spaceState = get_world_3d().direct_space_state
|
|
var result = spaceState.intersect_ray(query)
|
|
var lowpassCutoff = 20000
|
|
if !result.is_empty():
|
|
var rayDistance = self.global_position.distance_to(result["position"])
|
|
var distanceToPlayer = self.global_position.distance_to(player.global_position)
|
|
var wallToPlayerRatio = rayDistance / max(distanceToPlayer, 0.001)
|
|
if rayDistance < distanceToPlayer:
|
|
lowpassCutoff = wallLowpassCutoffAmount * wallToPlayerRatio
|
|
targetLowpassCutoff = lowpassCutoff
|
|
|
|
func lerpParameters(delta: float) -> void:
|
|
volume_db = lerp(volume_db, targetVolumeDb, delta)
|
|
lowpassFilterEffect.cutoff_hz = lerp(lowpassFilterEffect.cutoff_hz, targetLowpassCutoff, delta * 5.0)
|
|
reverbEffect.wet = lerp(reverbEffect.wet, targetReverbRoomSize * maxReverbWetness, delta * 5.0)
|
|
reverbEffect.room_size = lerp(reverbEffect.room_size, targetReverbRoomSize, delta * 5.0)
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
lastUpdateTime += delta
|
|
|
|
if updateDistances:
|
|
onUpdateRaycastDistance(raycastArray[currentRaycastIndex], currentRaycastIndex)
|
|
currentRaycastIndex += 1
|
|
if currentRaycastIndex >= distanceArray.size():
|
|
currentRaycastIndex = 0
|
|
updateDistances = false
|
|
|
|
if lastUpdateTime > updateFrequencySeconds:
|
|
var playerCamera = get_viewport().get_camera_3d()
|
|
if playerCamera != null:
|
|
onUpdateSpatialAudio(playerCamera)
|
|
updateDistances = true
|
|
lastUpdateTime = 0.0
|
|
|
|
lerpParameters(delta)
|
|
print("Audio:", global_position)
|
|
print("Camera:", get_viewport().get_camera_3d().global_position)
|