using System.Numerics;
using System.Runtime.InteropServices;
using Microsoft.UI.Composition;
using Vortice.D3DCompiler;
using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.DXGI;
using Vortice.Mathematics;
using Vortice.WinUI.Composition;
using XFEEDToolBox.Core.Rendering;
using XFEEDToolBox.Core.Galaxy;
using static Vortice.Direct3D11.D3D11;
namespace XFEEDToolBox.Rendering;
[StructLayout(LayoutKind.Sequential)]
internal readonly record struct GalaxyStar(Vector2 Position,float Radius,float Kind,Vector4 Colour,Vector4 World=default,float OriginalSize=0,bool PowerTerritorySize=false,int OriginalSprite=13,Vector4? OriginalColour=null,bool OriginalBlackHole=false);
[StructLayout(LayoutKind.Sequential)]
internal readonly record struct GalaxyStellarSprite(Vector2 Position,float Radius,float Kind,Vector4 Colour,Vector4 Appearance);
[StructLayout(LayoutKind.Sequential)]
internal readonly record struct GalaxyLineVertex(Vector2 Position,float Distance,float Dashed,Vector4 Colour);
/// One instanced star draw and one line draw, independent of the number of retained XAML/Composition nodes.
internal sealed partial class GalaxyGraphics : IDisposable
{
private readonly ID3D11Device device;
private readonly ID3D11DeviceContext context;
private readonly ID3D11Buffer starBuffer,lineBuffer,constants;
private readonly ID3D11VertexShader starsVs,linesVs,skyVs,overviewVs;
private readonly ID3D11PixelShader starsPs,linesPs,skyPs,overviewPs,inspectSkyPs;
private readonly ID3D11InputLayout starsLayout,linesLayout;
private readonly ID3D11BlendState additive;
private readonly ID3D11SamplerState sampler;
private readonly ID3D11RasterizerState rasterizer;
private readonly ID3D11DepthStencilState noDepth;
private readonly CompositionGraphicsDevice compositionDevice;
private readonly ICompositionDrawingSurfaceInterop interop;
private readonly BeginDraw beginDraw;
private readonly ResizeDraw resizeDraw;
private ID3D11Texture2D? pixels,skyTexture;
private ID3D11RenderTargetView? target;
private ID3D11ShaderResourceView? sky;
private ID3D11Texture2D? densityTexture;
private ID3D11ShaderResourceView? density;
private GalaxyDensityMap? densityMap;
private int width,height;
private long nextSkyCheck;
private readonly GalaxyStellarSprite[] stellarSprites=new GalaxyStellarSprite[StarCapacity];
public CompositionDrawingSurface Surface { get; }
public const int StarCapacity=65536,LineVertexCapacity=52000;
public long Frames { get; private set; }
public int DrawCalls { get; private set; }
internal bool UnresolvedStarBackgroundEnabled {get;private set;}
public string SkyboxSource { get; private set; }="";
public GalaxyDensityMap? Overview=>densityMap;
internal (int Width,int Height,byte[] Pixels) ReadPixels()
{
if(pixels is null)throw new InvalidOperationException("星图尚未绘制。");
var description=pixels.Description;description.BindFlags=BindFlags.None;description.CPUAccessFlags=CpuAccessFlags.Read;
description.Usage=ResourceUsage.Staging;description.MiscFlags=ResourceOptionFlags.None;
using var staging=device.CreateTexture2D(description);context.CopyResource(staging,pixels);
var mapped=context.Map(staging,0,MapMode.Read);
try
{
var result=new byte[width*height*4];
for(var y=0;y.FromManaged(compositor)))
using(var api=unknown.QueryInterface())
{api.CreateGraphicsDevice(device,out var created).CheckError();compositionDevice=created!;}
Surface=compositionDevice.CreateDrawingSurface(new Windows.Foundation.Size(1,1),
Microsoft.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized,Microsoft.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
using(var unknown=new SharpGen.Runtime.ComObject(WinRT.MarshalInspectable.FromManaged(Surface)))
interop=unknown.QueryInterface();
beginDraw=Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(Marshal.ReadIntPtr(interop.NativePointer),3*IntPtr.Size));
resizeDraw=Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(Marshal.ReadIntPtr(interop.NativePointer),5*IntPtr.Size));
starBuffer=device.CreateBuffer(StarCapacity*48,BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write);
lineBuffer=device.CreateBuffer(LineVertexCapacity*32,BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write);
constants=device.CreateBuffer(80,BindFlags.ConstantBuffer);
var shader=Path.Combine(AppContext.BaseDirectory,"Assets","GalaxyMap.hlsl");
InitializeNebulae(shader);
InitializeGalacticZones(shader);
InitializePowerContours(shader);
InitializeOriginalSprites();
InitializeOriginalBlackHoles(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyBlackHole.hlsl"));
InitializeOriginalPowerActivityRing(shader);
InitializeOriginalPowerActivityBillboard(shader);
InitializeOriginalMilkyWay(shader);
InitializeOriginalGrid(shader);
InitializeOriginalCurrentTrade(shader);
InitializeOriginalCore(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyCore.hlsl"));
InitializeOriginalHdr(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyHdr.hlsl"));
InitializeOriginalExposure(shader);
InitializeOriginalPlane(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyPlane.hlsl"));
var a=Compiler.CompileFromFile(shader,"VSStar","vs_5_0");starsVs=device.CreateVertexShader(a.Span);
starsLayout=device.CreateInputLayout([
new InputElementDescription("POSITION",0,Format.R32G32_Float,0,0,InputClassification.PerInstanceData,1),
new InputElementDescription("RADIUS",0,Format.R32G32_Float,8,0,InputClassification.PerInstanceData,1),
new InputElementDescription("COLOR",0,Format.R32G32B32A32_Float,16,0,InputClassification.PerInstanceData,1),
new InputElementDescription("APPEARANCE",0,Format.R32G32B32A32_Float,32,0,InputClassification.PerInstanceData,1)],a.Span);
var b=Compiler.CompileFromFile(shader,"VSLine","vs_5_0");linesVs=device.CreateVertexShader(b.Span);
linesLayout=device.CreateInputLayout([
new InputElementDescription("POSITION",0,Format.R32G32_Float,0,0),
new InputElementDescription("TEXCOORD",0,Format.R32G32_Float,8,0),
new InputElementDescription("COLOR",0,Format.R32G32B32A32_Float,16,0)],b.Span);
starsPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSStar","ps_5_0").Span);
linesPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSLine","ps_5_0").Span);
skyVs=device.CreateVertexShader(Compiler.CompileFromFile(shader,"VSSky","vs_5_0").Span);
skyPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSSky","ps_5_0").Span);
inspectSkyPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSInspectSky","ps_5_0").Span);
overviewVs=device.CreateVertexShader(Compiler.CompileFromFile(shader,"VSOverview","vs_5_0").Span);
overviewPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSOverview","ps_5_0").Span);
additive=device.CreateBlendState(new BlendDescription(Blend.One,Blend.InverseSourceAlpha));
sampler=device.CreateSamplerState(new SamplerDescription(Filter.MinMagMipLinear,TextureAddressMode.Clamp,TextureAddressMode.Clamp,TextureAddressMode.Clamp));
rasterizer=device.CreateRasterizerState(new RasterizerDescription(CullMode.None,FillMode.Solid));
noDepth=device.CreateDepthStencilState(new DepthStencilDescription(false,DepthWriteMask.Zero,ComparisonFunction.Always));
}
private void Resize(int w,int h)
{
if(width==w&&height==h)return;
context.OMSetRenderTargets(Array.Empty());target?.Dispose();pixels?.Dispose();
Marshal.ThrowExceptionForHR(resizeDraw(interop.NativePointer,new(w,h)));width=w;height=h;
pixels=device.CreateTexture2D(Format.B8G8R8A8_UNorm,(uint)w,(uint)h,mipLevels:1,bindFlags:BindFlags.RenderTarget);
target=device.CreateRenderTargetView(pixels);
}
private unsafe void Upload(ID3D11Buffer buffer,ReadOnlySpan data) where T:unmanaged
{
if(data.Length==0)return;
var mapped=context.Map(buffer,0,MapMode.WriteDiscard);
try{var bytes=MemoryMarshal.AsBytes(data);bytes.CopyTo(new Span((void*)mapped.DataPointer,bytes.Length));}
finally{context.Unmap(buffer,0);}
}
public void Draw(ReadOnlySpan stars,ReadOnlySpan lines,double w,double h,double raster,double yaw,double pitch,Vector3 centre,float scale,float panX,float panY,bool realistic=true,bool showBackground=true,bool showUnresolvedStars=true,bool navigationMarkers=true)
{
UnresolvedStarBackgroundEnabled=showBackground&&showUnresolvedStars;
Resize(Math.Clamp((int)Math.Ceiling(w*raster),1,7680),Math.Clamp((int)Math.Ceiling(h*raster),1,4320));
RefreshSky();
PrepareNebulae(w,h,yaw,pitch,centre,scale,panX,panY,showBackground);
var backgroundCamera=GalaxyCamera.Create(new(centre.X,centre.Y,centre.Z),w,h,yaw,pitch,scale,panX,panY);
// Pass the same recovered Core lens/clip values to every world pass.
// optionsZW and densityExtentZW were unused in the normal map b0.
var camera=new Camera(new((float)w,(float)h,(float)yaw,(float)pitch),new(sky is null?0:1,density is null?0:1,(float)backgroundCamera.FocalLength,(float)backgroundCamera.Distance),
new(centre,scale),new(panX,panY,(float)(densityMap?.OriginX??0),(float)(densityMap?.OriginZ??0)),
new((float)(densityMap?.ExtentX??1),(float)(densityMap?.ExtentZ??1),(float)backgroundCamera.Near,(float)backgroundCamera.ProjectionFar));
context.UpdateSubresource(in camera,constants);context.VSSetConstantBuffer(0,constants);context.PSSetConstantBuffer(0,constants);
context.OMSetRenderTargets(target!);context.RSSetViewport(new Viewport(width,height));context.RSSetState(rasterizer);context.OMSetDepthStencilState(noDepth);context.OMSetBlendState(null);
context.ClearRenderTargetView(target!,new Color4(.011f,.014f,.019f,1));DrawCalls=0;
// The captured cockpit cube is only 256px/face and includes the local
// resolved star field. Magnifying it here creates ten-pixel square stars
// and duplicates catalogue stars. Keep it available for resource
// inspection, but draw map stars exclusively from the real catalogue.
context.OMSetBlendState(additive);
OriginalSceneHdrDrawn=false;
OriginalBackgroundHdrDrawn=false;
BeginOriginalHdr(width,height,new Color4(0,0,0,1));
BindOriginalSourceOutput(GalaxyOriginalHdr.WriteScale,false);
if(showBackground)
{
DrawOriginalPlane(backgroundCamera,true,GalaxyOriginalHdr.WriteScale);
DrawOriginalCore(backgroundCamera,true,GalaxyOriginalHdr.WriteScale,GalaxyOriginalHdr.ReadScale);
DrawOriginalMilkyWay(backgroundCamera,true,GalaxyOriginalHdr.WriteScale);
OriginalBackgroundHdrDrawn=true;
}
if(showBackground&&showUnresolvedStars&&!OriginalMilkyWayReady&&density is not null&&scale<.2f)
{
context.IASetInputLayout(null);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
context.VSSetShader(overviewVs);context.PSSetShader(overviewPs);context.PSSetShaderResource(1,density);context.PSSetSampler(0,sampler);context.Draw(6,0);
context.PSSetShaderResource(1,null!);DrawCalls++;
}
// The legacy nebula/zone paths explicitly adapt RGB to scene storage;
// their source materials remain incomplete. Recovered passes below
// use their actual RGB-only Write and independent alpha/blend state.
DrawNebulae(originalHdrTarget);
DrawGalacticZones();
DrawPowerContours();
ComputeOriginalManualExposure();
DrawOriginalPowerActivityBillboard(backgroundCamera,GalaxyOriginalHdr.WriteScale);
DrawOriginalPowerActivityRing(backgroundCamera,GalaxyOriginalHdr.WriteScale);
DrawOriginalGrid(backgroundCamera,GalaxyOriginalHdr.WriteScale);
DrawOriginalCurrentTrade(backgroundCamera,GalaxyOriginalHdr.WriteScale);
if(stars.Length>0)
{
BindOriginalSourceOutput(GalaxyOriginalHdr.WriteScale);
Upload(starBuffer,PrepareStellarSprites(ApplyNebulaTransmission(stars),backgroundCamera,realistic));context.IASetInputLayout(starsLayout);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
context.IASetVertexBuffer(0,starBuffer,48);context.VSSetShader(starsVs);context.PSSetShader(starsPs);
context.PSSetShaderResource(7,originalSprites!);context.PSSetSampler(0,sampler);
context.DrawInstanced(6,(uint)stars.Length,0,0);context.PSSetShaderResource(7,null!);DrawCalls++;
}
DrawOriginalBlackHoles(stars,backgroundCamera,realistic,navigationMarkers,GalaxyOriginalHdr.WriteScale);
EndOriginalHdr(target!);context.OMSetBlendState(additive);OriginalSceneHdrDrawn=true;
// Tool navigation strokes are screen overlays, after scene tone mapping.
if(lines.Length>0)
{
Upload(lineBuffer,lines);context.IASetInputLayout(linesLayout);context.IASetPrimitiveTopology(PrimitiveTopology.LineList);
context.IASetVertexBuffer(0,lineBuffer,32);context.VSSetShader(linesVs);context.PSSetShader(linesPs);context.Draw((uint)lines.Length,0);DrawCalls++;
}
context.OMSetRenderTargets(Array.Empty());
var iid=typeof(ID3D11Texture2D).GUID;Marshal.ThrowExceptionForHR(beginDraw(interop.NativePointer,IntPtr.Zero,in iid,out var texture,out var offset));
using(var destination=new ID3D11Texture2D(texture))
{
try{context.CopySubresourceRegion(destination,0,(uint)offset.X,(uint)offset.Y,0,pixels!,0,new Box(0,0,0,width,height,1));context.Flush();}
finally{interop.EndDraw().CheckError();}
}
Frames++;
}
private ReadOnlySpan PrepareStellarSprites(ReadOnlySpan stars,GalaxyCamera camera,bool realistic)
{
var focus=camera.Focus;var forward=camera.Forward;var eye=camera.Eye;
for(var i=0;i=2?26:0,star.Kind,Vector4.Zero,Vector4.Zero);continue;}
var dx=star.World.X-focus.X;var dy=star.World.Y-focus.Y;var dz=star.World.Z-focus.Z;
var spectral=star.World.W>0?(int)MathF.Round(star.World.W)-1:13;
var projectedScale=camera.FocalLength/Math.Max(camera.Near,camera.Distance+dx*forward.X+dy*forward.Y+dz*forward.Z);
var originalPhysical=realistic&&float.IsFinite(star.OriginalSize)&&star.OriginalSize>0&&spectral!=12;
if(!realistic||originalPhysical)
{
// Actual Galaxy chain3A441A0→39FB140→3C01470→3C10980
// supplies six UV/corner vertices with cornerZW=±1. Producer
// 3A431C0 supplies half(styleSize); original VS multiplies them.
// No estimated profile, size floor, zoom curve or4x expansion.
var size=originalPhysical?star.OriginalSize:star.Radius;
if(star.PowerTerritorySize&&!realistic)
{
var ex=star.World.X-eye.X;var ey=star.World.Y-eye.Y;var ez=star.World.Z-eye.Z;
size*=StellarAppearance.PowerTerritoryDistanceScale(Math.Sqrt(ex*ex+ey*ey+ez*ez));
}
var halfExtent=StellarAppearance.OriginalProjectedHalfExtent(size,projectedScale);
stellarSprites[i]=new(star.Position,halfExtent>0?Math.Max(halfExtent,star.Kind>=2?26:0):0,star.Kind,colour,
new(halfExtent,Math.Clamp(star.OriginalSprite,0,27),originalPhysical?2:0,0));
}
else
{
// Public catalogue rows without native physical appearance
// retain an explicit estimate; they cannot claim a native branch.
var profile=StellarAppearance.Sprite(spectral,Math.Sqrt(dx*dx+dy*dy+dz*dz),projectedScale,(int)star.Kind);
var relative=float.IsFinite(star.Radius)?Math.Clamp(star.Radius,.01f,8):1;
profile=profile with{CoreRadius=profile.CoreRadius*relative,Radius=Math.Max(profile.Radius*relative,star.Kind>=2?26:0)};
stellarSprites[i]=new(star.Position,profile.Radius,star.Kind,colour with{W=colour.W*profile.Opacity},
new(profile.CoreRadius,(float)profile.Morphology,1,profile.Corona));
}
}
return stellarSprites.AsSpan(0,stars.Length);
}
private void RefreshSky()
{
if(Environment.TickCount64());return measurements;
}
public void Dispose()
{
context.ClearState();context.Flush();target?.Dispose();pixels?.Dispose();sky?.Dispose();skyTexture?.Dispose();
DisposeNebulae();
DisposeGalacticZones();
DisposePowerContours();
DisposeOriginalSprites();
DisposeOriginalBlackHoles();
DisposeOriginalExposure();
DisposeOriginalPowerActivityRing();
DisposeOriginalPowerActivityBillboard();
DisposeOriginalMilkyWay();
DisposeOriginalGrid();
DisposeOriginalCurrentTrade();
DisposeOriginalCore();
DisposeOriginalHdr();
DisposeOriginalPlane();
interop.Dispose();Surface.Dispose();compositionDevice.Dispose();sampler.Dispose();rasterizer.Dispose();noDepth.Dispose();additive.Dispose();starsLayout.Dispose();linesLayout.Dispose();
starsVs.Dispose();linesVs.Dispose();skyVs.Dispose();overviewVs.Dispose();starsPs.Dispose();linesPs.Dispose();skyPs.Dispose();overviewPs.Dispose();inspectSkyPs.Dispose();density?.Dispose();densityTexture?.Dispose();starBuffer.Dispose();lineBuffer.Dispose();constants.Dispose();context.Dispose();device.Dispose();
}
[StructLayout(LayoutKind.Sequential)] private readonly record struct Camera(Vector4 Viewport,Vector4 Options,Vector4 CentreScale,Vector4 PanOrigin,Vector4 DensityExtent);
[StructLayout(LayoutKind.Sequential)] private readonly record struct PixelSize(int Width,int Height);
[UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int BeginDraw(nint surface,nint rectangle,in Guid iid,out nint texture,out Int2 offset);
[UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int ResizeDraw(nint surface,PixelSize size);
}