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> 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 milkyWayTextures=[]; private readonly List 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 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>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(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); }