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);
}
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);
}