CodeWalker/CodeWalker.Core/World/AudioZones.cs

315 lines
10 KiB
C#
Raw Normal View History

using CodeWalker.Core.Utils;
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
2022-01-13 02:07:07 +08:00
public class AudioZones
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<AudioPlacement> Zones = new List<AudioPlacement>();
public List<AudioPlacement> Emitters = new List<AudioPlacement>();
public List<AudioPlacement> AllItems = new List<AudioPlacement>();
2018-12-26 21:20:39 +08:00
public Dictionary<RelFile, AudioPlacement[]> PlacementsDict = new Dictionary<RelFile, AudioPlacement[]>();
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
using var _ = new DisposableTimer("AudioZones Init");
Inited = false;
GameFileCache = gameFileCache;
Zones.Clear();
Emitters.Clear();
AllItems.Clear();
if (GameFileCache.AudioDatRelFiles is not null && GameFileCache.AudioDatRelFiles.Count > 0)
{
List<AudioPlacement> placements = new List<AudioPlacement>();
2023-11-12 01:59:17 +08:00
foreach (var relfile in GameFileCache.AudioDatRelFiles)
{
if (relfile is null)
continue;
2018-12-26 21:20:39 +08:00
2023-11-12 01:59:17 +08:00
placements.Clear();
2018-12-26 21:20:39 +08:00
2023-11-12 01:59:17 +08:00
CreatePlacements(relfile, placements, true);
2018-12-26 21:20:39 +08:00
2023-11-12 01:59:17 +08:00
PlacementsDict[relfile] = placements.ToArray();
}
}
AllItems.AddRange(Zones);
AllItems.AddRange(Emitters);
Inited = true;
}
2018-12-26 21:20:39 +08:00
private void CreatePlacements(RelFile relfile, List<AudioPlacement> placements, bool addtoLists = false)
{
foreach (var reldata in relfile.RelDatas)
{
AudioPlacement? placement = null;
if (reldata is Dat151AmbientZone dat151AmbientZone)
2018-12-26 21:20:39 +08:00
{
placement = new AudioPlacement(relfile, dat151AmbientZone);
if (addtoLists)
Zones.Add(placement);
2018-12-26 21:20:39 +08:00
}
else if (reldata is Dat151AmbientRule dat151AmbientRule)
2018-12-26 21:20:39 +08:00
{
placement = new AudioPlacement(relfile, dat151AmbientRule);
if (addtoLists)
Emitters.Add(placement);
2018-12-26 21:20:39 +08:00
}
if (placement is not null)
2018-12-26 21:20:39 +08:00
{
placements.Add(placement);
}
}
}
public void GetPlacements(List<RelFile> relfiles, List<AudioPlacement> placements)
{
foreach (var relfile in relfiles)
{
AudioPlacement[]? fileplacements = null;
2018-12-26 21:20:39 +08:00
if (!PlacementsDict.TryGetValue(relfile, out fileplacements))
{
List<AudioPlacement> newplacements = new List<AudioPlacement>();
CreatePlacements(relfile, newplacements);
fileplacements = newplacements.ToArray();
PlacementsDict[relfile] = fileplacements;
}
if (fileplacements is not null)
2018-12-26 21:20:39 +08:00
{
placements.AddRange(fileplacements);
}
}
}
}
public class AudioPlacement
{
public string Name { get; set; }
public MetaHash NameHash { get; set; }
public RelFile RelFile { get; set; }
public Dat151AmbientZone AudioZone { get; set; }
2021-11-06 18:37:58 +08:00
public Dat151AmbientRule AudioEmitter { get; set; }
public Dat151ZoneShape Shape { get; set; }
public string ShortTypeName { get; set; }
public string FullTypeName { get; set; }
public Vector3 InnerPos { get; set; }
public Vector3 InnerMin { get; set; }
public Vector3 InnerMax { get; set; }
public float InnerRadius { get; set; }
public Quaternion InnerOri { get; set; }
public Vector3 OuterPos { get; set; }
public Vector3 OuterMin { get; set; }
public Vector3 OuterMax { get; set; }
public float OuterRadius { get; set; }
public Quaternion OuterOri { get; set; }
public Vector3 Position { get; set; }
public Vector3 HitboxMin { get; set; }
public Vector3 HitboxMax { get; set; }
public Quaternion Orientation { get; set; }
public Quaternion OrientationInv { get; set; }
public float HitSphereRad { get; set; }
public AudioPlacement(RelFile rel, Dat151AmbientZone zone)
{
RelFile = rel;
AudioZone = zone;
ShortTypeName = "AudioZone";
FullTypeName = "Audio Zone";
2018-12-26 21:20:39 +08:00
UpdateFromZone();
}
2021-11-06 18:37:58 +08:00
public AudioPlacement(RelFile rel, Dat151AmbientRule emitter)
2018-12-26 21:20:39 +08:00
{
RelFile = rel;
AudioEmitter = emitter;
ShortTypeName = "AudioEmitter";
FullTypeName = "Audio Emitter";
UpdateFromEmitter();
}
public void UpdateFromZone()
{
if (AudioZone is null)
return;
2018-12-26 21:20:39 +08:00
var zone = AudioZone;
Name = zone.Name;
NameHash = zone.NameHash;
2018-12-26 21:20:39 +08:00
Shape = zone.Shape;
float deg2rad = (float)(Math.PI / 180.0);
switch (zone.Shape)
{
case Dat151ZoneShape.Box:
2021-11-06 18:37:58 +08:00
InnerPos = zone.PlaybackZonePosition;
InnerMax = zone.PlaybackZoneSize * 0.5f;
InnerMin = -InnerMax;
2021-11-06 18:37:58 +08:00
InnerOri = Quaternion.RotationAxis(Vector3.UnitZ, zone.PlaybackZoneAngle * deg2rad);
break;
case Dat151ZoneShape.Sphere:
2021-11-06 18:37:58 +08:00
InnerPos = zone.PlaybackZonePosition;
InnerOri = Quaternion.Identity;
InnerRadius = zone.PlaybackZoneSize.X;
OuterRadius = zone.ActivationZoneSize.X;
break;
case Dat151ZoneShape.Line:
2021-11-06 18:37:58 +08:00
InnerPos = zone.PlaybackZonePosition;
InnerMin = new Vector3(-1.0f, -1.0f, 0.0f);
2021-11-06 18:37:58 +08:00
InnerMax = new Vector3(1.0f, 1.0f, (zone.PlaybackZoneSize - zone.PlaybackZonePosition).Length());
InnerOri = Quaternion.Invert(Quaternion.LookAtLH(zone.PlaybackZonePosition, zone.PlaybackZoneSize, Vector3.UnitZ));
break;
}
2021-11-06 18:37:58 +08:00
OuterPos = zone.ActivationZonePosition;
OuterMax = zone.ActivationZoneSize * 0.5f;
OuterMin = -OuterMax;
2021-11-06 18:37:58 +08:00
OuterOri = Quaternion.RotationAxis(Vector3.UnitZ, zone.ActivationZoneAngle * deg2rad);
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
if (useouter && (zone.Shape != Dat151ZoneShape.Sphere))
{ } //not sure what these are yet!
Position = useouter ? OuterPos : InnerPos;
HitboxMax = useouter ? OuterMax : InnerMax;
HitboxMin = useouter ? OuterMin : InnerMin;
Orientation = useouter ? OuterOri : InnerOri;
OrientationInv = Quaternion.Invert(Orientation);
HitSphereRad = InnerRadius;
if (zone.Shape == Dat151ZoneShape.Sphere)
{
Position = InnerPos;
}
2018-12-26 21:20:39 +08:00
}
2018-12-26 21:20:39 +08:00
public void UpdateFromEmitter()
{
if (AudioEmitter is null)
return;
2018-12-26 21:20:39 +08:00
var emitter = AudioEmitter;
Name = emitter.Name;
NameHash = emitter.NameHash;
2018-12-26 21:20:39 +08:00
Shape = Dat151ZoneShape.Sphere;
Orientation = Quaternion.Identity;
OrientationInv = Quaternion.Identity;
InnerPos = emitter.Position;
OuterPos = InnerPos;
InnerRadius = emitter.InnerRadius;
OuterRadius = emitter.OuterRadius;
bool useouter = (InnerRadius == 0);
if (useouter)
{
InnerRadius = 1;
}
Position = InnerPos;
HitSphereRad = InnerRadius;// useouter ? OuterRadius : InnerRadius;
2018-12-26 21:20:39 +08:00
}
public void SetPosition(Vector3 pos)
{
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
Vector3 delta = pos - InnerPos;
InnerPos = pos;
OuterPos += delta;
Position = useouter ? OuterPos : InnerPos;
2018-12-26 21:20:39 +08:00
if (AudioZone is not null)
2018-12-26 21:20:39 +08:00
{
2021-11-06 18:37:58 +08:00
AudioZone.PlaybackZonePosition = InnerPos;
AudioZone.ActivationZonePosition = OuterPos;
2018-12-26 21:20:39 +08:00
}
if (AudioEmitter is not null)
2018-12-26 21:20:39 +08:00
{
AudioEmitter.Position = InnerPos;
}
}
public void SetOrientation(Quaternion ori)
{
Orientation = ori;
OrientationInv = Quaternion.Invert(ori);
Vector3 t = ori.Multiply(in Vector3.UnitX);
2018-12-26 21:20:39 +08:00
float angl = (float)Math.Atan2(t.Y, t.X);
while (angl < 0) angl += ((float)Math.PI * 2.0f);
float rad2deg = (float)(180.0 / Math.PI);
float dangl = angl * rad2deg;
uint uangl = (uint)dangl;
if (InnerOri == OuterOri)
{
InnerOri = Orientation;
OuterOri = Orientation;
2018-12-26 21:20:39 +08:00
if (AudioZone != null)
{
2021-11-06 18:37:58 +08:00
AudioZone.PlaybackZoneAngle = uangl;
AudioZone.ActivationZoneAngle = uangl;
2018-12-26 21:20:39 +08:00
}
}
else
{
//not sure yet how to allow independent rotation of inner & outer boxes...
//maybe only in project window?
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
if (useouter)
{
OuterOri = Orientation;
2018-12-26 21:20:39 +08:00
if (AudioZone != null)
{
2021-11-06 18:37:58 +08:00
AudioZone.ActivationZoneAngle = uangl;
2018-12-26 21:20:39 +08:00
}
}
else
{
InnerOri = Orientation;
2018-12-26 21:20:39 +08:00
if (AudioZone != null)
{
2021-11-06 18:37:58 +08:00
AudioZone.PlaybackZoneAngle = uangl;
2018-12-26 21:20:39 +08:00
}
}
}
}
public string GetNameString()
{
if (!string.IsNullOrEmpty(Name)) return Name;
return NameHash.ToString();
}
}
}