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;

namespace XFEEDToolBox.Core.Rendering;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly record struct GalaxyOriginalTradeInstance(Vector3 Start, Vector3 End, uint MidOffset, uint WidthTime,
    ulong UvScaleOffset, uint Colour);

public readonly record struct GalaxyOriginalTradeRandom(Vector3 MidOffset, float Phase);
public sealed record GalaxyOriginalTradeMesh(IReadOnlyList<ulong> HalfVertices, IReadOnlyList<ushort> Indices);

/// <summary>Pure source-space values from the original trade publisher and 44-byte instance writers.</summary>
public static class GalaxyOriginalTradeGeometry
{
    public const int RouteSegments = 16;
    public static GalaxyOriginalTradeMesh RouteMesh { get; } = CreateRouteMesh();
    public static GalaxyOriginalTradeMesh HotspotMesh { get; } = new(Array.AsReadOnly(new[] {
        PackHalf4(-.5f,-.5f,0,0), PackHalf4(-.5f,.5f,0,1), PackHalf4(.5f,-.5f,1,0), PackHalf4(.5f,.5f,1,1)
    }), Array.AsReadOnly<ushort>([0,1,2,2,1,3]));

    // 3C05BA0/3C05320 use this SIMD converter: rounded normal values, flushed
    // subnormals and the original finite clamp. This is not an unchecked cast
    // to System.Half, whose subnormal/overflow behaviour differs.
    public static ushort PackHalf(float value)
    {
        if (!float.IsFinite(value)) throw new ArgumentOutOfRangeException(nameof(value));
        var bits = BitConverter.SingleToUInt32Bits(value);
        var absolute = MathF.Min(MathF.Abs(value), 131008f);
        uint converted = 0;
        if (absolute >= 0.00006103515625f)
        {
            var adjusted = unchecked(BitConverter.SingleToUInt32Bits(absolute) + 0x48000000u);
            converted = unchecked(adjusted + 0xFFFu + ((adjusted >> 13) & 1)) >> 13;
        }
        var saturated = Math.Min(converted & 0xFFFFu, 0x7FFFu);
        return (ushort)(saturated | ((bits >> 16) & 0x8000));
    }
    public static ulong PackHalf4(float x, float y, float z, float w) =>
        PackHalf(x) | ((ulong)PackHalf(y) << 16) | ((ulong)PackHalf(z) << 32) | ((ulong)PackHalf(w) << 48);

    public static GalaxyOriginalTradeRandom Random(long from, long to, int category)
    {
        var start = unchecked((ulong)from); var end = unchecked((ulong)to);
        uint hash;
        unchecked
        {
            hash = (uint)(start >> 32) * 0x401;
            hash = ((hash >> 6 ^ hash) + (uint)start) * 0x401;
            hash = ((hash >> 6 ^ hash) + (uint)(end >> 32)) * 0x401;
            hash = ((hash >> 6 ^ hash) + (uint)end) * 0x401;
            hash = ((hash >> 6 ^ hash) + (uint)category) * 0x401;
            hash = (hash >> 6 ^ hash) * 9;
            hash = (hash >> 11 ^ hash) * 0x8001;
        }
        uint state = hash == 0 ? 1 : hash;
        uint Next() { state ^= state << 13; state ^= state >> 17; state ^= state << 5; return state; }
        static float Unit(uint value) => MathF.Min(unchecked(value - 1) * (1f / 4294967296f), .9999999403953552f);
        var first = Unit(Next()); var second = Unit(Next()); var third = Unit(Next()); var phase = Unit(Next());
        return new(new((third + third) - 1, (second + second) - 1, (first + first) - 1), phase);
    }
    public static uint PackMidOffset(Vector3 value)
    {
        // Original cvtps2dq uses the default nearest-even rounding mode.
        static uint Channel(float value) => (uint)Math.Clamp((int)MathF.Round(MathF.Min(value, 1) * 127 + 127), 0, 255);
        return Channel(value.X) | (Channel(value.Y) << 8) | (Channel(value.Z) << 16) | (127u << 24);
    }
    public static GalaxyOriginalTradeInstance Route(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity)
    {
        Validate(start, end, category, amount, opacity);
        var random = Random(from, to, category);
        return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase),
            PackHalf4(1, 1f / 13, 0, category * (1f / 13)), White(opacity));
    }
    public static GalaxyOriginalTradeInstance Hotspot(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity, int phaseIndex)
    {
        Validate(start, end, category, amount, opacity);
        if (phaseIndex < 0 || phaseIndex >= 8) throw new ArgumentOutOfRangeException(nameof(phaseIndex));
        var random = Random(from, to, category);
        return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase + phaseIndex),
            PackHalf4((2f / 3) * .25f, (2f / 3) * .25f, ((category % 4) + .5f) * .25f, ((category / 4) + .5f) * .25f), White(opacity));
    }
    public static int HotspotCount(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp((int)MathF.Ceiling(distance / 3) + 2, 2, 8) : 0;
    public static float FocusFade(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp(1 - Math.Max(0, distance - 25) / 3, 0, 1) : 0;
    private static uint WidthTime(float amount, float phase) => PackHalf(amount * 1.2f + .3f) | ((uint)PackHalf(phase) << 16);
    private static uint White(float opacity) => 0x00FFFFFFu | ((uint)(int)MathF.Round(opacity * 255) << 24);
    private static void Validate(Vector3 start, Vector3 end, int category, float amount, float opacity)
    {
        if (!float.IsFinite(start.X + start.Y + start.Z + end.X + end.Y + end.Z) || start == end ||
            category is < 0 or > 12 || !float.IsFinite(amount) || amount <= 0 || !float.IsFinite(opacity) || opacity is < 0 or > 1)
            throw new ArgumentOutOfRangeException(nameof(amount), "Invalid actual trade-flow geometry.");
    }
    private static GalaxyOriginalTradeMesh CreateRouteMesh()
    {
        var vertices = new ulong[RouteSegments * 3 + 2]; var indices = new ushort[RouteSegments * 12];
        for (var i = 0; i < RouteSegments; i++)
        {
            var t = i * (1f / RouteSegments); var mid = (i + .5f) * (1f / RouteSegments);
            vertices[i * 3] = PackHalf4(t, -1, t, 0);
            vertices[i * 3 + 1] = PackHalf4(t, 1, t, 1);
            vertices[i * 3 + 2] = PackHalf4(mid, 0, mid, .5f);
            var n = i * 3;
            ushort[] segment = [(ushort)n, (ushort)(n + 1), (ushort)(n + 2), (ushort)n, (ushort)(n + 2), (ushort)(n + 3),
                (ushort)(n + 3), (ushort)(n + 2), (ushort)(n + 4), (ushort)(n + 4), (ushort)(n + 2), (ushort)(n + 1)];
            segment.CopyTo(indices, i * 12);
        }
        vertices[^2] = PackHalf4(1, -1, 1, 0); vertices[^1] = PackHalf4(1, 1, 1, 1);
        return new(Array.AsReadOnly(vertices), Array.AsReadOnly(indices));
    }
}