namespace XFEEDToolBox.Core.Rendering;
public readonly record struct GalaxyOriginalDustLayerFactors(float Alpha,float Size);
/// <summary>Explicit current producer settings, rather than constructor defaults.
/// This does not supply an effective material tint or enable a Dust draw pass.</summary>
public sealed record GalaxyOriginalDustInputs(uint Step,uint Height,IReadOnlyList<GalaxyOriginalDustLayerFactors> Layers,
float AlphaMultiplier,float SizeMultiplier,float TransitionPerStep,float MinimumDistance);
/// <summary>A completed publication of the cache being rendered. Game-camera
/// completion cannot be substituted for the tool's independently generated cache.</summary>
public readonly record struct GalaxyOriginalDustPublication(int Ordinal,uint PublishedRevision,uint RequestedRevision,float Radius,ulong RecordCount)
{
public bool Published=>PublishedRevision!=0;
}
public readonly record struct GalaxyOriginalDustLayerFrame(float LowerDistance,float UpperDistance,float TransitionWidth,
float AlphaMultiplier,float SizeMultiplier,bool HeightMultiplierEnabled);
/// <summary>Actual3A21BC0 record parameters and original Dust VS distance math.
/// Distances retain the source's absolute-coordinate domain.</summary>
public static class GalaxyOriginalDustFrame
{
public const float CoarseUpperDistance=300000;
public static bool TryCreate(GalaxyOriginalDustInputs? inputs,IReadOnlyList<GalaxyOriginalDustPublication>? publications,int ordinal,
out GalaxyOriginalDustLayerFrame frame)
{
frame=default;
static bool Nonnegative(float value)=>float.IsFinite(value)&&value>=0;
if(inputs is null||inputs.Step==0||inputs.Height==0||inputs.Layers is not{Count:3}||publications is not{Count:3}||ordinal is <0 or >2||
!Nonnegative(inputs.AlphaMultiplier)||!Nonnegative(inputs.SizeMultiplier)||!Nonnegative(inputs.TransitionPerStep)||!Nonnegative(inputs.MinimumDistance))return false;
for(var i=0;i<3;i++)
if(publications[i].Ordinal!=i||!Nonnegative(inputs.Layers[i].Alpha)||!Nonnegative(inputs.Layers[i].Size)||
publications[i].Published&&!Nonnegative(publications[i].Radius))return false;
var current=publications[ordinal];if(!current.Published)return false;
var width=(float)inputs.Step;for(var i=0;i<ordinal;i++)width*=.5f;
width*=inputs.TransitionPerStep;
var lower=inputs.MinimumDistance;
if(ordinal!=1&&ordinal+1<3&&publications[ordinal+1].Published)lower=publications[ordinal+1].Radius-width*.5f;
var upper=ordinal==0?CoarseUpperDistance:current.Radius+width;
var alpha=inputs.Layers[ordinal].Alpha*inputs.AlphaMultiplier;
var size=inputs.Layers[ordinal].Size*inputs.SizeMultiplier;
if(!float.IsFinite(width)||!float.IsFinite(lower)||!float.IsFinite(upper)||!float.IsFinite(alpha)||!float.IsFinite(size))return false;
frame=new(lower,upper,width,alpha,size,ordinal==0);return true;
}
public static float? DistanceFade(float distance,GalaxyOriginalDustLayerFrame frame)
{
if(!float.IsFinite(distance)||distance<0||!float.IsFinite(frame.LowerDistance)||!float.IsFinite(frame.UpperDistance)||
!float.IsFinite(frame.TransitionWidth)||frame.TransitionWidth<0)return null;
var denominator=frame.TransitionWidth+.001f;
return Math.Clamp((distance-frame.LowerDistance)/denominator,0,1)*Math.Clamp((frame.UpperDistance-distance)/denominator,0,1);
}
}
namespace XFEEDToolBox.Core.Rendering;
public readonly record struct GalaxyOriginalDustLayerFactors(float Alpha,float Size);
/// <summary>Explicit current producer settings, rather than constructor defaults.
/// This does not supply an effective material tint or enable a Dust draw pass.</summary>
public sealed record GalaxyOriginalDustInputs(uint Step,uint Height,IReadOnlyList<GalaxyOriginalDustLayerFactors> Layers,
float AlphaMultiplier,float SizeMultiplier,float TransitionPerStep,float MinimumDistance);
/// <summary>A completed publication of the cache being rendered. Game-camera
/// completion cannot be substituted for the tool's independently generated cache.</summary>
public readonly record struct GalaxyOriginalDustPublication(int Ordinal,uint PublishedRevision,uint RequestedRevision,float Radius,ulong RecordCount)
{
public bool Published=>PublishedRevision!=0;
}
public readonly record struct GalaxyOriginalDustLayerFrame(float LowerDistance,float UpperDistance,float TransitionWidth,
float AlphaMultiplier,float SizeMultiplier,bool HeightMultiplierEnabled);
/// <summary>Actual3A21BC0 record parameters and original Dust VS distance math.
/// Distances retain the source's absolute-coordinate domain.</summary>
public static class GalaxyOriginalDustFrame
{
public const float CoarseUpperDistance=300000;
public static bool TryCreate(GalaxyOriginalDustInputs? inputs,IReadOnlyList<GalaxyOriginalDustPublication>? publications,int ordinal,
out GalaxyOriginalDustLayerFrame frame)
{
frame=default;
static bool Nonnegative(float value)=>float.IsFinite(value)&&value>=0;
if(inputs is null||inputs.Step==0||inputs.Height==0||inputs.Layers is not{Count:3}||publications is not{Count:3}||ordinal is <0 or >2||
!Nonnegative(inputs.AlphaMultiplier)||!Nonnegative(inputs.SizeMultiplier)||!Nonnegative(inputs.TransitionPerStep)||!Nonnegative(inputs.MinimumDistance))return false;
for(var i=0;i<3;i++)
if(publications[i].Ordinal!=i||!Nonnegative(inputs.Layers[i].Alpha)||!Nonnegative(inputs.Layers[i].Size)||
publications[i].Published&&!Nonnegative(publications[i].Radius))return false;
var current=publications[ordinal];if(!current.Published)return false;
var width=(float)inputs.Step;for(var i=0;i<ordinal;i++)width*=.5f;
width*=inputs.TransitionPerStep;
var lower=inputs.MinimumDistance;
if(ordinal!=1&&ordinal+1<3&&publications[ordinal+1].Published)lower=publications[ordinal+1].Radius-width*.5f;
var upper=ordinal==0?CoarseUpperDistance:current.Radius+width;
var alpha=inputs.Layers[ordinal].Alpha*inputs.AlphaMultiplier;
var size=inputs.Layers[ordinal].Size*inputs.SizeMultiplier;
if(!float.IsFinite(width)||!float.IsFinite(lower)||!float.IsFinite(upper)||!float.IsFinite(alpha)||!float.IsFinite(size))return false;
frame=new(lower,upper,width,alpha,size,ordinal==0);return true;
}
public static float? DistanceFade(float distance,GalaxyOriginalDustLayerFrame frame)
{
if(!float.IsFinite(distance)||distance<0||!float.IsFinite(frame.LowerDistance)||!float.IsFinite(frame.UpperDistance)||
!float.IsFinite(frame.TransitionWidth)||frame.TransitionWidth<0)return null;
var denominator=frame.TransitionWidth+.001f;
return Math.Clamp((distance-frame.LowerDistance)/denominator,0,1)*Math.Clamp((frame.UpperDistance-distance)/denominator,0,1);
}
}