XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Numerics;
using System.Runtime.InteropServices;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;

namespace XFEEDToolBox.Core.Rendering;

/// <summary>Original FocusRing64-byte instance, not a Territory billboard.</summary>
[StructLayout(LayoutKind.Sequential)]
public readonly record struct GalaxyOriginalPowerActivityRing(Vector4 Position,Vector4 UAxisInner,Vector4 VAxisOuter,Vector4 Colour);
public readonly record struct GalaxyOriginalPowerActivityRingVertex(Vector3 World,Vector4 AnnulusRadii,Vector4 Colour);

public static class GalaxyOriginalPowerActivityRingGeometry
{
    public const int QuadCount=16,VertexCount=34;
    public const float SegmentCount=200,RadialFeather=.800000011920929f,SegmentFeather=.300000011920929f,SolidRatio=.850000023841858f;

    // Original3B33690's range record. Its separate extent/FocusBillboard and
    // per-system influence links require other original inputs, not this R.
    public static bool TryCreate(GalaxyNativePowerOuter? bound,Coordinates eye,out GalaxyOriginalPowerActivityRing ring)
    {
        ring=default;
        if(bound is not{CentreSystemAddress:>0,CentrePosition:{} centre,Radius:>0 and <=1000000}||!centre.IsValid||!eye.IsValid)return false;
        var radius=bound.Radius.Value;var position=new Vector3((float)centre.X,(float)centre.Y,(float)centre.Z);
        var relative=new Vector3((float)(eye.X-centre.X),(float)(eye.Y-centre.Y),(float)(eye.Z-centre.Z));
        var distance=relative.Length();if(!float.IsFinite(distance)||!float.IsFinite(radius))return false;
        var width=.05f*Math.Clamp(distance/radius,1,6);
        ring=new(new(position,1),new(1,0,0,radius-width),new(0,0,1,radius+width),new(1,1,1,0));return true;
    }

    public static bool IsValid(GalaxyOriginalPowerActivityRing ring)=>Finite(ring.Position)&&Finite(ring.UAxisInner)&&Finite(ring.VAxisOuter)&&
        Finite(ring.Colour)&&ring.VAxisOuter.W>ring.UAxisInner.W&&ring.VAxisOuter.W>0;
    private static bool Finite(Vector4 v)=>float.IsFinite(v.X)&&float.IsFinite(v.Y)&&float.IsFinite(v.Z)&&float.IsFinite(v.W);

    //3C01F30 stores32 primitives.51BE30 uses original type5 conversion(1,2)
    // to submit34 vertices;5101A0 passes mapped D3D topology5/TriangleStrip.
    public static GalaxyOriginalPowerActivityRingVertex Vertex(GalaxyOriginalPowerActivityRing ring,int vertex)
    {
        if(!IsValid(ring)||vertex is <0 or >=VertexCount)throw new ArgumentOutOfRangeException(nameof(vertex));
        var radius=(vertex&1)!=0?ring.UAxisInner.W:ring.VAxisOuter.W/MathF.Cos(MathF.PI/QuadCount);
        var angle=(vertex>>1)*(2*MathF.PI/QuadCount);var(sine,cosine)=MathF.SinCos(angle);
        var local=new Vector2(cosine*radius,sine*radius);
        var world=new Vector3(ring.Position.X,ring.Position.Y,ring.Position.Z)+
            new Vector3(ring.UAxisInner.X,ring.UAxisInner.Y,ring.UAxisInner.Z)*local.X+
            new Vector3(ring.VAxisOuter.X,ring.VAxisOuter.Y,ring.VAxisOuter.Z)*local.Y;
        return new(world,new(ring.UAxisInner.W,ring.VAxisOuter.W,local.X,local.Y),ring.Colour);
    }

    private static float Fraction(float value)=>value-MathF.Floor(value);
    private static float Smooth(float value){value=Math.Clamp(value,0,1);return value*value*(3-2*value);}
    public static float Coverage(Vector4 radii)
    {
        var x=radii.Z;var y=radii.W;var r=MathF.Sqrt(x*x+y*y);
        if(!float.IsFinite(r)||r<=radii.X||r>=radii.Y||radii.Y<=radii.X)return 0;
        var radial=Smooth((1-2*MathF.Abs((r-radii.X)/(radii.Y-radii.X)-.5f))/RadialFeather);
        var ax=MathF.Abs(x);var ay=MathF.Abs(y);var q=MathF.Min(ax,ay)/MathF.Max(ax,ay);var q2=q*q;
        // Original FocusRing PS atan approximation, including quadrant signs.
        var angle=((((.02083509974181652f*q2-.08513300120830536f)*q2+.18014100193977356f)*q2-.3302994966506958f)*q2+.9998660087585449f)*q;
        if(ax<ay)angle=MathF.PI*.5f-angle;if(x<0)angle-=MathF.PI;
        if(MathF.Min(x,y)<0&&MathF.Max(x,y)>=0)angle=-angle;
        var phase=Fraction(Fraction(angle*.159154943091895f)*SegmentCount);
        var angular=Smooth((2*MathF.Abs(phase-.5f)-(1-SolidRatio))/(SolidRatio*SegmentFeather));
        return radial*angular;
    }

    /// <summary>Source-space shader reference. Emissive is an explicit known
    /// input; this helper does not infer the game's dynamic exposure texture.</summary>
    public static Vector4 Shade(Vector4 radii,Vector4 colour,float emissiveRescaling,float hdrWrite=1)
    {
        if(!float.IsFinite(emissiveRescaling)||emissiveRescaling<0||!float.IsFinite(hdrWrite)||hdrWrite<=0)throw new ArgumentOutOfRangeException(nameof(emissiveRescaling));
        return new(new Vector3(colour.X,colour.Y,colour.Z)*Coverage(radii)*emissiveRescaling*hdrWrite,colour.W);
    }
}