fix(vfx): classify hardwareless particle emitters once
This commit is contained in:
parent
921712f412
commit
5b3fb17775
8 changed files with 374 additions and 18 deletions
|
|
@ -13,6 +13,21 @@ using DatGfxObjFlags = DatReaderWriter.Enums.GfxObjFlags;
|
|||
|
||||
namespace AcDream.Core.Vfx;
|
||||
|
||||
public enum EmitterDescResolutionFailureKind
|
||||
{
|
||||
None,
|
||||
MissingEmitterInfo,
|
||||
InvalidHardwareGfxObjId,
|
||||
MissingHardwareGfxObj,
|
||||
}
|
||||
|
||||
public readonly record struct EmitterDescResolutionFailure(
|
||||
EmitterDescResolutionFailureKind Kind,
|
||||
uint RelatedDatId = 0u)
|
||||
{
|
||||
public static EmitterDescResolutionFailure None => default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves retail <c>ParticleEmitterInfo</c> dat records
|
||||
/// (<c>0x32xxxxxx</c>) into acdream runtime descriptors.
|
||||
|
|
@ -20,8 +35,11 @@ namespace AcDream.Core.Vfx;
|
|||
public sealed class EmitterDescRegistry
|
||||
{
|
||||
private readonly Func<uint, DatParticleEmitter?>? _resolver;
|
||||
private readonly Func<DatParticleEmitter, float?>? _degradeDistanceResolver;
|
||||
private readonly Func<DatParticleEmitter, EmitterDatResolution>?
|
||||
_degradeDistanceResolver;
|
||||
private readonly ConcurrentDictionary<uint, EmitterDesc> _byId = new();
|
||||
private readonly ConcurrentDictionary<uint, EmitterDescResolutionFailure>
|
||||
_failures = new();
|
||||
|
||||
public EmitterDescRegistry()
|
||||
: this((Func<uint, DatParticleEmitter?>?)null)
|
||||
|
|
@ -44,6 +62,7 @@ public sealed class EmitterDescRegistry
|
|||
{
|
||||
ArgumentNullException.ThrowIfNull(desc);
|
||||
_byId[desc.DatId] = desc;
|
||||
_failures.TryRemove(desc.DatId, out _);
|
||||
}
|
||||
|
||||
public EmitterDesc Get(uint emitterId)
|
||||
|
|
@ -55,35 +74,59 @@ public sealed class EmitterDescRegistry
|
|||
}
|
||||
|
||||
public bool TryGet(uint emitterId, out EmitterDesc desc)
|
||||
=> TryGet(emitterId, out desc, out _);
|
||||
|
||||
public bool TryGet(
|
||||
uint emitterId,
|
||||
out EmitterDesc desc,
|
||||
out EmitterDescResolutionFailure failure)
|
||||
{
|
||||
if (_byId.TryGetValue(emitterId, out desc!))
|
||||
{
|
||||
failure = EmitterDescResolutionFailure.None;
|
||||
return true;
|
||||
}
|
||||
if (_failures.TryGetValue(emitterId, out failure))
|
||||
{
|
||||
desc = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_resolver is not null)
|
||||
{
|
||||
var dat = _resolver(emitterId);
|
||||
if (dat is not null)
|
||||
{
|
||||
float? resolvedDistance = _degradeDistanceResolver?.Invoke(dat);
|
||||
if (_degradeDistanceResolver is not null && !resolvedDistance.HasValue)
|
||||
EmitterDatResolution resolved = _degradeDistanceResolver?.Invoke(dat)
|
||||
?? EmitterDatResolution.Success(
|
||||
RetailParticleDegradeDistance.DefaultDistance);
|
||||
if (!resolved.Succeeded)
|
||||
{
|
||||
// ParticleEmitter::SetInfo (0x0051CE90) fails when the
|
||||
// authored hardware GfxObj cannot be loaded. It never
|
||||
// substitutes the software GfxObj.
|
||||
failure = resolved.Failure;
|
||||
_failures.TryAdd(emitterId, failure);
|
||||
desc = null!;
|
||||
return false;
|
||||
}
|
||||
float maxDegradeDistance = resolvedDistance
|
||||
?? RetailParticleDegradeDistance.DefaultDistance;
|
||||
desc = FromDat(emitterId, dat, maxDegradeDistance);
|
||||
desc = FromDat(emitterId, dat, resolved.MaxDegradeDistance);
|
||||
_byId[emitterId] = desc;
|
||||
failure = EmitterDescResolutionFailure.None;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
failure = new EmitterDescResolutionFailure(
|
||||
EmitterDescResolutionFailureKind.MissingEmitterInfo,
|
||||
emitterId);
|
||||
_failures.TryAdd(emitterId, failure);
|
||||
desc = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public int Count => _byId.Count;
|
||||
public int FailureCount => _failures.Count;
|
||||
|
||||
public static EmitterDesc FromDat(
|
||||
uint emitterId,
|
||||
|
|
@ -155,25 +198,34 @@ public sealed class EmitterDescRegistry
|
|||
};
|
||||
}
|
||||
|
||||
private static float? ResolveMaxDegradeDistance(
|
||||
private static EmitterDatResolution ResolveMaxDegradeDistance(
|
||||
IDatObjectSource dats,
|
||||
DatParticleEmitter emitter)
|
||||
{
|
||||
uint gfxObjId = GetRetailHardwareGfxObjId(emitter);
|
||||
if (gfxObjId == 0)
|
||||
return null;
|
||||
{
|
||||
return EmitterDatResolution.Fail(
|
||||
EmitterDescResolutionFailureKind.InvalidHardwareGfxObjId);
|
||||
}
|
||||
|
||||
DatGfxObj? gfx = SafeGet<DatGfxObj>(dats, gfxObjId);
|
||||
if (gfx is null)
|
||||
return null;
|
||||
{
|
||||
return EmitterDatResolution.Fail(
|
||||
EmitterDescResolutionFailureKind.MissingHardwareGfxObj,
|
||||
gfxObjId);
|
||||
}
|
||||
if (!gfx.Flags.HasFlag(DatGfxObjFlags.HasDIDDegrade)
|
||||
|| gfx.DIDDegrade == 0)
|
||||
{
|
||||
return RetailParticleDegradeDistance.DefaultDistance;
|
||||
return EmitterDatResolution.Success(
|
||||
RetailParticleDegradeDistance.DefaultDistance);
|
||||
}
|
||||
|
||||
DatGfxObjDegradeInfo? degrade = SafeGet<DatGfxObjDegradeInfo>(dats, gfx.DIDDegrade);
|
||||
return RetailParticleDegradeDistance.FromEntries(degrade?.Degrades);
|
||||
return EmitterDatResolution.Success(
|
||||
RetailParticleDegradeDistance.FromEntries(degrade?.Degrades));
|
||||
}
|
||||
|
||||
internal static uint GetRetailHardwareGfxObjId(DatParticleEmitter emitter)
|
||||
|
|
@ -216,6 +268,26 @@ public sealed class EmitterDescRegistry
|
|||
DatParticleType.GlobalVelocity => ParticleType.GlobalVelocity,
|
||||
_ => ParticleType.Unknown,
|
||||
};
|
||||
|
||||
private readonly record struct EmitterDatResolution(
|
||||
bool Succeeded,
|
||||
float MaxDegradeDistance,
|
||||
EmitterDescResolutionFailure Failure)
|
||||
{
|
||||
public static EmitterDatResolution Success(float maxDegradeDistance) =>
|
||||
new(
|
||||
true,
|
||||
maxDegradeDistance,
|
||||
EmitterDescResolutionFailure.None);
|
||||
|
||||
public static EmitterDatResolution Fail(
|
||||
EmitterDescResolutionFailureKind kind,
|
||||
uint relatedDatId = 0u) =>
|
||||
new(
|
||||
false,
|
||||
0f,
|
||||
new EmitterDescResolutionFailure(kind, relatedDatId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue