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 Vortice.D3DCompiler;
using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.DXGI;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Rendering;

namespace XFEEDToolBox.Rendering;

[StructLayout(LayoutKind.Sequential)]
internal readonly record struct GalaxyMilkyWaySprite(Vector4 WorldAlpha,Vector4 Shape,Vector4 Atlas,Vector4 Layer);

internal sealed partial class GalaxyGraphics
{
    private static readonly Lazy<Task<GalaxyOriginalCloudLayer>> originalMilkyWayCpu=new(()=>Task.Run(()=>
        GalaxyOriginalClouds.Generate(GalaxyOriginalClouds.Window(new(0,0,0)))));
    private ID3D11VertexShader milkyWayVs=null!;
    private ID3D11PixelShader milkyWayPs=null!;
    private ID3D11InputLayout milkyWayLayout=null!;
    private ID3D11Buffer milkyWayBuffer=null!,milkyWayConstants=null!;
    private ID3D11BlendState milkyWayBlend=null!;
    private readonly List<ID3D11Texture2D> milkyWayTextures=[];
    private readonly List<ID3D11ShaderResourceView> milkyWayViews=[];
    private int milkyWayInstances;
    internal bool OriginalMilkyWayReady=>milkyWayInstances>0;
    internal int OriginalMilkyWayInstances=>milkyWayInstances;
    internal bool PendingOriginalMilkyWay=>!originalMilkyWayCpu.Value.IsCompleted;
    internal bool OriginalMilkyWayFullRestored=>false;
    internal bool OriginalDustEnabled=>false;

    private void InitializeOriginalMilkyWay(string shader)
    {
        var vertex=Compiler.CompileFromFile(shader,"VSMilkyWay","vs_5_0");milkyWayVs=device.CreateVertexShader(vertex.Span);
        milkyWayPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSMilkyWay","ps_5_0").Span);
        milkyWayLayout=device.CreateInputLayout([
            new InputElementDescription("POSITION",0,Format.R32G32B32A32_Float,0,0,InputClassification.PerInstanceData,1),
            new InputElementDescription("SHAPE",0,Format.R32G32B32A32_Float,16,0,InputClassification.PerInstanceData,1),
            new InputElementDescription("ATLAS",0,Format.R32G32B32A32_Float,32,0,InputClassification.PerInstanceData,1),
            new InputElementDescription("LAYER",0,Format.R32G32B32A32_Float,48,0,InputClassification.PerInstanceData,1)],vertex.Span);
        milkyWayConstants=device.CreateBuffer(96,BindFlags.ConstantBuffer);
        milkyWayBlend=device.CreateBlendState(new BlendDescription(Blend.SourceAlpha,Blend.One));
        var source=GalaxyOriginalMilkyWay.Reference;
        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
        {
            foreach(var texture in source.Textures)
            {
                var slices=texture.Mips.Select(m=>new SubresourceData(pin.AddrOfPinnedObject()+bytes.Offset+texture.PayloadOffset+m.Offset,(uint)m.RowPitch,(uint)m.DepthPitch)).ToArray();
                var gpu=device.CreateTexture2D(new Texture2DDescription{Width=(uint)texture.Width,Height=(uint)texture.Height,ArraySize=1,
                    MipLevels=(uint)texture.Mips.Count,Format=Format.BC1_UNorm,SampleDescription=new(1,0),Usage=ResourceUsage.Immutable,BindFlags=BindFlags.ShaderResource},slices);
                milkyWayTextures.Add(gpu);milkyWayViews.Add(device.CreateShaderResourceView(gpu));
            }
        }
        finally{pin.Free();}
        _=originalMilkyWayCpu.Value.ContinueWith(task=>
        {
            if(disposed)return;
            App.MainWindow.DispatcherQueue.TryEnqueue(()=>
            {
                if(disposed)return;
                if(task.IsFaulted)App.Runtime.Log("银河背景",task.Exception!.GetBaseException().Message);
                ResourcesChanged?.Invoke();
            });
        },TaskScheduler.Default);
    }

    private void DrawOriginalMilkyWay(GalaxyCamera camera,bool showBackground,float hdrWrite=1)
    {
        if(!showBackground||!originalMilkyWayCpu.Value.IsCompletedSuccessfully)return;
        if(milkyWayInstances==0)
        {
            var layer=originalMilkyWayCpu.Value.Result;var sprites=new GalaxyMilkyWaySprite[layer.Clouds.Count];
            var atlas=GalaxyOriginalMilkyWay.Reference.AtlasTiles;
            // Original renderer constructor 3A06740/3B118B0 defaults:
            // alpha=min(raw,.046)*10; world half-size=rawSize*31*32;
            // near200, far300000 and cell transition width1066*100.
            for(var i=0;i<sprites.Length;i++)
            {
                var cloud=layer.Clouds[i];var tile=atlas[cloud.AtlasIndex];
                sprites[i]=new(new(cloud.AbsolutePosition,Math.Clamp(Math.Min(cloud.Alpha,.046f)*10,0,1)),
                    new((float)(Half)cloud.Rotation,cloud.Size*31*32,200,300000),
                    new(tile>>8,tile&255,GalaxyOriginalMilkyWayResources.AtlasColumns,GalaxyOriginalMilkyWayResources.AtlasRows),new(106600,1,0,0));
            }
            milkyWayBuffer=device.CreateBuffer((uint)Math.Max(64,sprites.Length*64),BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write);
            Upload<GalaxyMilkyWaySprite>(milkyWayBuffer,sprites);milkyWayInstances=sprites.Length;
        }
        var eye=camera.Eye;var right=camera.Right;var up=camera.Up;var forward=camera.Forward;
        var frame=new MilkyWayCamera(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),
            // FGM material values; the original PS's shared Write multiplier
            // is explicit so its SourceAlpha/One blend and final HDR Read
            // remain a linear reciprocal pair, unlike Core's square blend.
            new(.122f,1.96f,44902.80078125f,0),new(0,0,0,hdrWrite));
        context.UpdateSubresource(in frame,milkyWayConstants);context.VSSetConstantBuffer(3,milkyWayConstants);context.PSSetConstantBuffer(3,milkyWayConstants);
        context.VSSetShaderResource(10,milkyWayViews[1]);context.VSSetShaderResource(11,milkyWayViews[2]);context.VSSetSampler(0,sampler);
        context.PSSetShaderResource(9,milkyWayViews[0]);context.PSSetSampler(0,sampler);context.OMSetBlendState(milkyWayBlend);
        context.IASetInputLayout(milkyWayLayout);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList);context.IASetVertexBuffer(0,milkyWayBuffer,64);
        context.VSSetShader(milkyWayVs);context.PSSetShader(milkyWayPs);context.DrawInstanced(6,(uint)milkyWayInstances,0,0);DrawCalls++;
        context.VSSetShaderResource(10,null!);context.VSSetShaderResource(11,null!);context.PSSetShaderResource(9,null!);context.OMSetBlendState(additive);
    }
    private void DisposeOriginalMilkyWay()
    {
        milkyWayVs.Dispose();milkyWayPs.Dispose();milkyWayLayout.Dispose();milkyWayBuffer?.Dispose();milkyWayConstants.Dispose();milkyWayBlend.Dispose();
        foreach(var view in milkyWayViews)view.Dispose();foreach(var texture in milkyWayTextures)texture.Dispose();
    }
    [StructLayout(LayoutKind.Sequential)]
    private readonly record struct MilkyWayCamera(Vector4 EyeFocal,Vector4 RightWidth,Vector4 UpHeight,Vector4 ForwardNear,Vector4 Material,Vector4 Output);
}