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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using Vortice.D3DCompiler;
using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.DXGI;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Rendering;

namespace XFEEDToolBox.Rendering;

internal sealed partial class GalaxyGraphics
{
    private ID3D11VertexShader currentTradeRouteVs=null!,currentTradeHotspotVs=null!;
    private ID3D11PixelShader currentTradeRoutePs=null!,currentTradeHotspotPs=null!;
    private ID3D11InputLayout currentTradeLayout=null!;
    private ID3D11Buffer currentTradeConstants=null!,currentTradeRouteMesh=null!,currentTradeRouteIndices=null!,currentTradeHotspotMesh=null!,currentTradeHotspotIndices=null!;
    private ID3D11Buffer? currentTradeRouteBuffer,currentTradeHotspotBuffer;
    private ID3D11Texture2D currentTradeRouteTexture=null!,currentTradeTrailTexture=null!,currentTradeHotspotTexture=null!;
    private ID3D11ShaderResourceView currentTradeRouteView=null!,currentTradeTrailView=null!,currentTradeHotspotView=null!;
    private ID3D11SamplerState currentTradeSampler=null!;
    private ID3D11BlendState currentTradeBlend=null!;
    private GalaxyOriginalTradeInstance[] currentTradeRoutes=[],currentTradeHotspots=[];
    private IReadOnlyList<GalaxyCurrentTradeRenderFlow> currentTradeFlows=[];
    private readonly Dictionary<(long From,long To,int Category),float> currentTradeFade=[];
    private Vector4 currentTradeTimers0,currentTradeTimers1;
    private long currentTradeLastFrame;
    internal int SubmittedCurrentTradeRoutes {get;private set;}
    internal int SubmittedCurrentTradeHotspots {get;private set;}
    internal bool HasOriginalCurrentTradeAnimation=>SubmittedCurrentTradeRoutes>0;
    internal bool OriginalCurrentTradeGeometryRestored=>true;
    // Source geometry/material math and exact sRGB atlases are restored. The
    // original frame's cell-load fade, absolute animation phase and complete
    // shared final output are not asserted pixel-identical.
    internal bool OriginalCurrentTradeFullRestored=>false;

    internal void SetCurrentTradeFlows(IReadOnlyList<GalaxyCurrentTradeRenderFlow>? flows)
    {
        flows??=[];if(ReferenceEquals(flows,currentTradeFlows))return;
        currentTradeFlows=flows;
        var keep=flows.Select(f=>(f.Route.FromSystemAddress,f.Route.ToSystemAddress,f.Route.OriginalCategoryId)).ToHashSet();
        foreach(var key in currentTradeFade.Keys.Where(k=>!keep.Contains(k)).ToArray())currentTradeFade.Remove(key);
        if(flows.Count==0){SubmittedCurrentTradeRoutes=0;SubmittedCurrentTradeHotspots=0;currentTradeLastFrame=0;}
    }
    private void InitializeOriginalCurrentTrade(string shader)
    {
        if(Marshal.SizeOf<GalaxyOriginalTradeInstance>()!=44)throw new InvalidDataException("原贸易流实例格式无效。");
        var path=Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyTrade.hlsl");
        var route=Compiler.CompileFromFile(path,"VSOriginalTradeRoute","vs_5_0");
        currentTradeRouteVs=device.CreateVertexShader(route.Span);
        currentTradeHotspotVs=device.CreateVertexShader(Compiler.CompileFromFile(path,"VSOriginalTradeHotspot","vs_5_0").Span);
        currentTradeRoutePs=device.CreatePixelShader(Compiler.CompileFromFile(path,"PSOriginalTradeRoute","ps_5_0").Span);
        currentTradeHotspotPs=device.CreatePixelShader(Compiler.CompileFromFile(path,"PSOriginalTradeHotspot","ps_5_0").Span);
        currentTradeLayout=device.CreateInputLayout([
            new InputElementDescription("STUV",0,Format.R16G16B16A16_Float,0,0),
            new InputElementDescription("START",0,Format.R32G32B32_Float,0,1,InputClassification.PerInstanceData,1),
            new InputElementDescription("END",0,Format.R32G32B32_Float,12,1,InputClassification.PerInstanceData,1),
            new InputElementDescription("MIDOFFSET",0,Format.R8G8B8A8_UNorm,24,1,InputClassification.PerInstanceData,1),
            new InputElementDescription("WIDTHTIMEOFFSET",0,Format.R16G16_Float,28,1,InputClassification.PerInstanceData,1),
            new InputElementDescription("UVSCALEOFFSET",0,Format.R16G16B16A16_Float,32,1,InputClassification.PerInstanceData,1),
            new InputElementDescription("COLOUR",0,Format.R8G8B8A8_UNorm,40,1,InputClassification.PerInstanceData,1)],route.Span);
        currentTradeConstants=device.CreateBuffer(160,BindFlags.ConstantBuffer);
        currentTradeRouteMesh=device.CreateBuffer<ulong>(GalaxyOriginalTradeGeometry.RouteMesh.HalfVertices.ToArray(),BindFlags.VertexBuffer);
        currentTradeRouteIndices=device.CreateBuffer<ushort>(GalaxyOriginalTradeGeometry.RouteMesh.Indices.ToArray(),BindFlags.IndexBuffer);
        currentTradeHotspotMesh=device.CreateBuffer<ulong>(GalaxyOriginalTradeGeometry.HotspotMesh.HalfVertices.ToArray(),BindFlags.VertexBuffer);
        currentTradeHotspotIndices=device.CreateBuffer<ushort>(GalaxyOriginalTradeGeometry.HotspotMesh.Indices.ToArray(),BindFlags.IndexBuffer);
        currentTradeSampler=device.CreateSamplerState(new SamplerDescription(Filter.MinMagMipLinear,TextureAddressMode.Wrap,TextureAddressMode.Wrap,TextureAddressMode.Wrap));
        // Actual Route/Hotspot kind-5 effect programs copy literal bank1
        // SrcAlpha/One/Add. No historical arrow colour/width is substituted.
        currentTradeBlend=device.CreateBlendState(new BlendDescription(Blend.SourceAlpha,Blend.One));
        var assets=GalaxyOriginalTradeAssets.Reference;
        (currentTradeRouteTexture,currentTradeRouteView)=CreateCurrentTradeTexture(assets.Route);
        (currentTradeTrailTexture,currentTradeTrailView)=CreateCurrentTradeTexture(assets.Trail);
        (currentTradeHotspotTexture,currentTradeHotspotView)=CreateCurrentTradeTexture(assets.Hotspot);
    }
    private (ID3D11Texture2D,ID3D11ShaderResourceView) CreateCurrentTradeTexture(GalaxyOriginalTradeTexture source)
    {
        if(!MemoryMarshal.TryGetArray(source.Payload,out ArraySegment<byte> bytes)||bytes.Array is null)throw new InvalidDataException("原贸易流纹理缓冲区不可用。");
        var pin=GCHandle.Alloc(bytes.Array,GCHandleType.Pinned);
        try
        {
            var texture=device.CreateTexture2D(new Texture2DDescription{Width=(uint)source.Width,Height=(uint)source.Height,
                ArraySize=1,MipLevels=1,Format=Format.BC1_UNorm_SRgb,SampleDescription=new(1,0),Usage=ResourceUsage.Immutable,BindFlags=BindFlags.ShaderResource},
                [new SubresourceData(pin.AddrOfPinnedObject()+bytes.Offset,(uint)source.RowPitch,(uint)source.Payload.Length)]);
            return(texture,device.CreateShaderResourceView(texture));
        }
        finally{pin.Free();}
    }
    private void EnsureCurrentTradeCapacity(int count)
    {
        if(count<=currentTradeRoutes.Length)return;
        var capacity=checked((int)BitOperations.RoundUpToPowerOf2((uint)Math.Max(16,count)));
        Array.Resize(ref currentTradeRoutes,capacity);Array.Resize(ref currentTradeHotspots,checked(capacity*8));
        currentTradeRouteBuffer?.Dispose();currentTradeHotspotBuffer?.Dispose();
        currentTradeRouteBuffer=device.CreateBuffer(checked((uint)(capacity*44)),BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write);
        currentTradeHotspotBuffer=device.CreateBuffer(checked((uint)(capacity*8*44)),BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write);
    }
    private void DrawOriginalCurrentTrade(GalaxyCamera camera,float hdrWrite=1)
    {
        SubmittedCurrentTradeRoutes=0;SubmittedCurrentTradeHotspots=0;
        if(currentTradeFlows.Count==0){currentTradeLastFrame=0;return;}
        var now=Stopwatch.GetTimestamp();var delta=currentTradeLastFrame==0?0:(float)Stopwatch.GetElapsedTime(currentTradeLastFrame,now).TotalSeconds;currentTradeLastFrame=now;
        // 3AB9B00 dt*configuration5C(.6) ->3C074B0 dt*.5; retain
        // float32 per-frame accumulation and frac. Epoch is tool-local.
        if(float.IsFinite(delta)&&delta>=0)
        {
            var step=(delta*.6f)*.5f;
            currentTradeTimers0+=new Vector4(.21875f,.1875f,.15625f,.125f)*step;
            currentTradeTimers1+=new Vector4(.09375f)*step;
            static Vector4 Frac(Vector4 v)=>v-new Vector4(MathF.Floor(v.X),MathF.Floor(v.Y),MathF.Floor(v.Z),MathF.Floor(v.W));
            currentTradeTimers0=Frac(currentTradeTimers0);currentTradeTimers1=Frac(currentTradeTimers1);
        }
        EnsureCurrentTradeCapacity(currentTradeFlows.Count);
        var focus=camera.Focus;var absoluteFocus=new Vector3((float)(focus.X+49985),(float)(focus.Y+40985),(float)(focus.Z+24105));
        foreach(var flow in currentTradeFlows)
        {
            var route=flow.Route;if(route.OriginalCategoryId is <0 or >12||!float.IsFinite(route.Amount)||route.Amount<=0||!flow.From.Position.IsValid||!flow.To.Position.IsValid)continue;
            var start=new Vector3((float)(flow.From.Position.X+49985),(float)(flow.From.Position.Y+40985),(float)(flow.From.Position.Z+24105));
            var end=new Vector3((float)(flow.To.Position.X+49985),(float)(flow.To.Position.Y+40985),(float)(flow.To.Position.Z+24105));
            if(start==end)continue;
            var key=(route.FromSystemAddress,route.ToSystemAddress,route.OriginalCategoryId);
            var target=GalaxyOriginalTradeGeometry.FocusFade(Vector3.Distance(start,absoluteFocus));
            if(target<=0){currentTradeFade.Remove(key);continue;}
            currentTradeFade.TryGetValue(key,out var oldFade);var fade=(target+oldFade)*.5f;currentTradeFade[key]=fade;
            var item=GalaxyOriginalTradeGeometry.Route(start,end,route.FromSystemAddress,route.ToSystemAddress,route.OriginalCategoryId,route.Amount,fade);
            if(!Half.IsFinite(BitConverter.UInt16BitsToHalf((ushort)item.WidthTime)))continue;
            currentTradeRoutes[SubmittedCurrentTradeRoutes++]=item;
            var hotspots=GalaxyOriginalTradeGeometry.HotspotCount(Vector3.Distance(start,end));
            for(var i=0;i<hotspots;i++)currentTradeHotspots[SubmittedCurrentTradeHotspots++]=GalaxyOriginalTradeGeometry.Hotspot(start,end,route.FromSystemAddress,route.ToSystemAddress,route.OriginalCategoryId,route.Amount,fade,i);
        }
        if(SubmittedCurrentTradeRoutes==0)return;
        var eye=camera.Eye;var right=camera.Right;var up=camera.Up;var forward=camera.Forward;
        var material=GalaxyOriginalTradeAssets.Reference.RouteMaterial;var hotspot=GalaxyOriginalTradeAssets.Reference.HotspotMaterial;
        var frame=new OriginalTradeFrame(new((float)(eye.X+49985),(float)(eye.Y+40985),(float)(eye.Z+24105),(float)camera.FocalLength),
            new((float)right.X,(float)right.Y,(float)right.Z,(float)camera.Width),new((float)up.X,(float)up.Y,(float)up.Z,(float)camera.Height),
            new((float)forward.X,(float)forward.Y,(float)forward.Z,(float)camera.Near),new((float)camera.ProjectionFar,hdrWrite,2,-1),currentTradeTimers0,currentTradeTimers1,
            new(material.StandardMapScales,material.Curvature),new(material.EaseInOut,material.HotspotTileLength,material.HotspotTimeScale,material.Pinch),
            new(material.TileLength!.Value,material.WidthScale,hotspot.WidthScale,0));
        context.UpdateSubresource(in frame,currentTradeConstants);context.VSSetConstantBuffer(5,currentTradeConstants);context.PSSetConstantBuffer(5,currentTradeConstants);
        context.IASetInputLayout(currentTradeLayout);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList);context.OMSetBlendState(currentTradeBlend);
        context.PSSetShaderResource(13,currentTradeRouteView);context.PSSetShaderResource(14,currentTradeTrailView);context.PSSetShaderResource(15,currentTradeHotspotView);context.PSSetSampler(0,currentTradeSampler);
        Upload<GalaxyOriginalTradeInstance>(currentTradeRouteBuffer!,currentTradeRoutes.AsSpan(0,SubmittedCurrentTradeRoutes));
        context.IASetVertexBuffer(0,currentTradeRouteMesh,8);context.IASetVertexBuffer(1,currentTradeRouteBuffer!,44);context.IASetIndexBuffer(currentTradeRouteIndices,Format.R16_UInt,0);
        context.VSSetShader(currentTradeRouteVs);context.PSSetShader(currentTradeRoutePs);context.DrawIndexedInstanced((uint)GalaxyOriginalTradeGeometry.RouteMesh.Indices.Count,(uint)SubmittedCurrentTradeRoutes,0,0,0);DrawCalls++;
        Upload<GalaxyOriginalTradeInstance>(currentTradeHotspotBuffer!,currentTradeHotspots.AsSpan(0,SubmittedCurrentTradeHotspots));
        context.IASetVertexBuffer(0,currentTradeHotspotMesh,8);context.IASetVertexBuffer(1,currentTradeHotspotBuffer!,44);context.IASetIndexBuffer(currentTradeHotspotIndices,Format.R16_UInt,0);
        context.VSSetShader(currentTradeHotspotVs);context.PSSetShader(currentTradeHotspotPs);context.DrawIndexedInstanced(6,(uint)SubmittedCurrentTradeHotspots,0,0,0);DrawCalls++;
        context.IASetVertexBuffer(1,null!,0);context.IASetIndexBuffer(null!,Format.Unknown,0);context.OMSetBlendState(additive);
        context.PSSetShaderResource(13,null!);context.PSSetShaderResource(14,null!);context.PSSetShaderResource(15,null!);context.PSSetSampler(0,sampler);
    }
    private void DisposeOriginalCurrentTrade()
    {
        currentTradeRouteVs.Dispose();currentTradeHotspotVs.Dispose();currentTradeRoutePs.Dispose();currentTradeHotspotPs.Dispose();currentTradeLayout.Dispose();
        currentTradeConstants.Dispose();currentTradeRouteMesh.Dispose();currentTradeRouteIndices.Dispose();currentTradeHotspotMesh.Dispose();currentTradeHotspotIndices.Dispose();
        currentTradeRouteBuffer?.Dispose();currentTradeHotspotBuffer?.Dispose();currentTradeRouteTexture.Dispose();currentTradeTrailTexture.Dispose();currentTradeHotspotTexture.Dispose();
        currentTradeRouteView.Dispose();currentTradeTrailView.Dispose();currentTradeHotspotView.Dispose();currentTradeSampler.Dispose();currentTradeBlend.Dispose();
    }
    [StructLayout(LayoutKind.Sequential)]
    private readonly record struct OriginalTradeFrame(Vector4 EyeFocal,Vector4 RightWidth,Vector4 UpHeight,Vector4 ForwardNear,Vector4 Projection,
        Vector4 Timers0,Vector4 Timers1,Vector4 Material0,Vector4 Material1,Vector4 Material2);
}