using System.Numerics;
using System.Runtime.InteropServices;
namespace XFEEDToolBox.Core.Rendering;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly record struct GalaxyOriginalTradeInstance(Vector3 Start, Vector3 End, uint MidOffset, uint WidthTime,
ulong UvScaleOffset, uint Colour);
public readonly record struct GalaxyOriginalTradeRandom(Vector3 MidOffset, float Phase);
public sealed record GalaxyOriginalTradeMesh(IReadOnlyList<ulong> HalfVertices, IReadOnlyList<ushort> Indices);
/// <summary>Pure source-space values from the original trade publisher and 44-byte instance writers.</summary>
public static class GalaxyOriginalTradeGeometry
{
public const int RouteSegments = 16;
public static GalaxyOriginalTradeMesh RouteMesh { get; } = CreateRouteMesh();
public static GalaxyOriginalTradeMesh HotspotMesh { get; } = new(Array.AsReadOnly(new[] {
PackHalf4(-.5f,-.5f,0,0), PackHalf4(-.5f,.5f,0,1), PackHalf4(.5f,-.5f,1,0), PackHalf4(.5f,.5f,1,1)
}), Array.AsReadOnly<ushort>([0,1,2,2,1,3]));
// 3C05BA0/3C05320 use this SIMD converter: rounded normal values, flushed
// subnormals and the original finite clamp. This is not an unchecked cast
// to System.Half, whose subnormal/overflow behaviour differs.
public static ushort PackHalf(float value)
{
if (!float.IsFinite(value)) throw new ArgumentOutOfRangeException(nameof(value));
var bits = BitConverter.SingleToUInt32Bits(value);
var absolute = MathF.Min(MathF.Abs(value), 131008f);
uint converted = 0;
if (absolute >= 0.00006103515625f)
{
var adjusted = unchecked(BitConverter.SingleToUInt32Bits(absolute) + 0x48000000u);
converted = unchecked(adjusted + 0xFFFu + ((adjusted >> 13) & 1)) >> 13;
}
var saturated = Math.Min(converted & 0xFFFFu, 0x7FFFu);
return (ushort)(saturated | ((bits >> 16) & 0x8000));
}
public static ulong PackHalf4(float x, float y, float z, float w) =>
PackHalf(x) | ((ulong)PackHalf(y) << 16) | ((ulong)PackHalf(z) << 32) | ((ulong)PackHalf(w) << 48);
public static GalaxyOriginalTradeRandom Random(long from, long to, int category)
{
var start = unchecked((ulong)from); var end = unchecked((ulong)to);
uint hash;
unchecked
{
hash = (uint)(start >> 32) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)start) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)(end >> 32)) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)end) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)category) * 0x401;
hash = (hash >> 6 ^ hash) * 9;
hash = (hash >> 11 ^ hash) * 0x8001;
}
uint state = hash == 0 ? 1 : hash;
uint Next() { state ^= state << 13; state ^= state >> 17; state ^= state << 5; return state; }
static float Unit(uint value) => MathF.Min(unchecked(value - 1) * (1f / 4294967296f), .9999999403953552f);
var first = Unit(Next()); var second = Unit(Next()); var third = Unit(Next()); var phase = Unit(Next());
return new(new((third + third) - 1, (second + second) - 1, (first + first) - 1), phase);
}
public static uint PackMidOffset(Vector3 value)
{
// Original cvtps2dq uses the default nearest-even rounding mode.
static uint Channel(float value) => (uint)Math.Clamp((int)MathF.Round(MathF.Min(value, 1) * 127 + 127), 0, 255);
return Channel(value.X) | (Channel(value.Y) << 8) | (Channel(value.Z) << 16) | (127u << 24);
}
public static GalaxyOriginalTradeInstance Route(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity)
{
Validate(start, end, category, amount, opacity);
var random = Random(from, to, category);
return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase),
PackHalf4(1, 1f / 13, 0, category * (1f / 13)), White(opacity));
}
public static GalaxyOriginalTradeInstance Hotspot(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity, int phaseIndex)
{
Validate(start, end, category, amount, opacity);
if (phaseIndex < 0 || phaseIndex >= 8) throw new ArgumentOutOfRangeException(nameof(phaseIndex));
var random = Random(from, to, category);
return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase + phaseIndex),
PackHalf4((2f / 3) * .25f, (2f / 3) * .25f, ((category % 4) + .5f) * .25f, ((category / 4) + .5f) * .25f), White(opacity));
}
public static int HotspotCount(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp((int)MathF.Ceiling(distance / 3) + 2, 2, 8) : 0;
public static float FocusFade(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp(1 - Math.Max(0, distance - 25) / 3, 0, 1) : 0;
private static uint WidthTime(float amount, float phase) => PackHalf(amount * 1.2f + .3f) | ((uint)PackHalf(phase) << 16);
private static uint White(float opacity) => 0x00FFFFFFu | ((uint)(int)MathF.Round(opacity * 255) << 24);
private static void Validate(Vector3 start, Vector3 end, int category, float amount, float opacity)
{
if (!float.IsFinite(start.X + start.Y + start.Z + end.X + end.Y + end.Z) || start == end ||
category is < 0 or > 12 || !float.IsFinite(amount) || amount <= 0 || !float.IsFinite(opacity) || opacity is < 0 or > 1)
throw new ArgumentOutOfRangeException(nameof(amount), "Invalid actual trade-flow geometry.");
}
private static GalaxyOriginalTradeMesh CreateRouteMesh()
{
var vertices = new ulong[RouteSegments * 3 + 2]; var indices = new ushort[RouteSegments * 12];
for (var i = 0; i < RouteSegments; i++)
{
var t = i * (1f / RouteSegments); var mid = (i + .5f) * (1f / RouteSegments);
vertices[i * 3] = PackHalf4(t, -1, t, 0);
vertices[i * 3 + 1] = PackHalf4(t, 1, t, 1);
vertices[i * 3 + 2] = PackHalf4(mid, 0, mid, .5f);
var n = i * 3;
ushort[] segment = [(ushort)n, (ushort)(n + 1), (ushort)(n + 2), (ushort)n, (ushort)(n + 2), (ushort)(n + 3),
(ushort)(n + 3), (ushort)(n + 2), (ushort)(n + 4), (ushort)(n + 4), (ushort)(n + 2), (ushort)(n + 1)];
segment.CopyTo(indices, i * 12);
}
vertices[^2] = PackHalf4(1, -1, 1, 0); vertices[^1] = PackHalf4(1, 1, 1, 1);
return new(Array.AsReadOnly(vertices), Array.AsReadOnly(indices));
}
}
using System.Numerics;
using System.Runtime.InteropServices;
namespace XFEEDToolBox.Core.Rendering;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly record struct GalaxyOriginalTradeInstance(Vector3 Start, Vector3 End, uint MidOffset, uint WidthTime,
ulong UvScaleOffset, uint Colour);
public readonly record struct GalaxyOriginalTradeRandom(Vector3 MidOffset, float Phase);
public sealed record GalaxyOriginalTradeMesh(IReadOnlyList<ulong> HalfVertices, IReadOnlyList<ushort> Indices);
/// <summary>Pure source-space values from the original trade publisher and 44-byte instance writers.</summary>
public static class GalaxyOriginalTradeGeometry
{
public const int RouteSegments = 16;
public static GalaxyOriginalTradeMesh RouteMesh { get; } = CreateRouteMesh();
public static GalaxyOriginalTradeMesh HotspotMesh { get; } = new(Array.AsReadOnly(new[] {
PackHalf4(-.5f,-.5f,0,0), PackHalf4(-.5f,.5f,0,1), PackHalf4(.5f,-.5f,1,0), PackHalf4(.5f,.5f,1,1)
}), Array.AsReadOnly<ushort>([0,1,2,2,1,3]));
// 3C05BA0/3C05320 use this SIMD converter: rounded normal values, flushed
// subnormals and the original finite clamp. This is not an unchecked cast
// to System.Half, whose subnormal/overflow behaviour differs.
public static ushort PackHalf(float value)
{
if (!float.IsFinite(value)) throw new ArgumentOutOfRangeException(nameof(value));
var bits = BitConverter.SingleToUInt32Bits(value);
var absolute = MathF.Min(MathF.Abs(value), 131008f);
uint converted = 0;
if (absolute >= 0.00006103515625f)
{
var adjusted = unchecked(BitConverter.SingleToUInt32Bits(absolute) + 0x48000000u);
converted = unchecked(adjusted + 0xFFFu + ((adjusted >> 13) & 1)) >> 13;
}
var saturated = Math.Min(converted & 0xFFFFu, 0x7FFFu);
return (ushort)(saturated | ((bits >> 16) & 0x8000));
}
public static ulong PackHalf4(float x, float y, float z, float w) =>
PackHalf(x) | ((ulong)PackHalf(y) << 16) | ((ulong)PackHalf(z) << 32) | ((ulong)PackHalf(w) << 48);
public static GalaxyOriginalTradeRandom Random(long from, long to, int category)
{
var start = unchecked((ulong)from); var end = unchecked((ulong)to);
uint hash;
unchecked
{
hash = (uint)(start >> 32) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)start) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)(end >> 32)) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)end) * 0x401;
hash = ((hash >> 6 ^ hash) + (uint)category) * 0x401;
hash = (hash >> 6 ^ hash) * 9;
hash = (hash >> 11 ^ hash) * 0x8001;
}
uint state = hash == 0 ? 1 : hash;
uint Next() { state ^= state << 13; state ^= state >> 17; state ^= state << 5; return state; }
static float Unit(uint value) => MathF.Min(unchecked(value - 1) * (1f / 4294967296f), .9999999403953552f);
var first = Unit(Next()); var second = Unit(Next()); var third = Unit(Next()); var phase = Unit(Next());
return new(new((third + third) - 1, (second + second) - 1, (first + first) - 1), phase);
}
public static uint PackMidOffset(Vector3 value)
{
// Original cvtps2dq uses the default nearest-even rounding mode.
static uint Channel(float value) => (uint)Math.Clamp((int)MathF.Round(MathF.Min(value, 1) * 127 + 127), 0, 255);
return Channel(value.X) | (Channel(value.Y) << 8) | (Channel(value.Z) << 16) | (127u << 24);
}
public static GalaxyOriginalTradeInstance Route(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity)
{
Validate(start, end, category, amount, opacity);
var random = Random(from, to, category);
return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase),
PackHalf4(1, 1f / 13, 0, category * (1f / 13)), White(opacity));
}
public static GalaxyOriginalTradeInstance Hotspot(Vector3 start, Vector3 end, long from, long to, int category, float amount, float opacity, int phaseIndex)
{
Validate(start, end, category, amount, opacity);
if (phaseIndex < 0 || phaseIndex >= 8) throw new ArgumentOutOfRangeException(nameof(phaseIndex));
var random = Random(from, to, category);
return new(start, end, PackMidOffset(random.MidOffset), WidthTime(amount, random.Phase + phaseIndex),
PackHalf4((2f / 3) * .25f, (2f / 3) * .25f, ((category % 4) + .5f) * .25f, ((category / 4) + .5f) * .25f), White(opacity));
}
public static int HotspotCount(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp((int)MathF.Ceiling(distance / 3) + 2, 2, 8) : 0;
public static float FocusFade(float distance) => float.IsFinite(distance) && distance >= 0 ? Math.Clamp(1 - Math.Max(0, distance - 25) / 3, 0, 1) : 0;
private static uint WidthTime(float amount, float phase) => PackHalf(amount * 1.2f + .3f) | ((uint)PackHalf(phase) << 16);
private static uint White(float opacity) => 0x00FFFFFFu | ((uint)(int)MathF.Round(opacity * 255) << 24);
private static void Validate(Vector3 start, Vector3 end, int category, float amount, float opacity)
{
if (!float.IsFinite(start.X + start.Y + start.Z + end.X + end.Y + end.Z) || start == end ||
category is < 0 or > 12 || !float.IsFinite(amount) || amount <= 0 || !float.IsFinite(opacity) || opacity is < 0 or > 1)
throw new ArgumentOutOfRangeException(nameof(amount), "Invalid actual trade-flow geometry.");
}
private static GalaxyOriginalTradeMesh CreateRouteMesh()
{
var vertices = new ulong[RouteSegments * 3 + 2]; var indices = new ushort[RouteSegments * 12];
for (var i = 0; i < RouteSegments; i++)
{
var t = i * (1f / RouteSegments); var mid = (i + .5f) * (1f / RouteSegments);
vertices[i * 3] = PackHalf4(t, -1, t, 0);
vertices[i * 3 + 1] = PackHalf4(t, 1, t, 1);
vertices[i * 3 + 2] = PackHalf4(mid, 0, mid, .5f);
var n = i * 3;
ushort[] segment = [(ushort)n, (ushort)(n + 1), (ushort)(n + 2), (ushort)n, (ushort)(n + 2), (ushort)(n + 3),
(ushort)(n + 3), (ushort)(n + 2), (ushort)(n + 4), (ushort)(n + 4), (ushort)(n + 2), (ushort)(n + 1)];
segment.CopyTo(indices, i * 12);
}
vertices[^2] = PackHalf4(1, -1, 1, 0); vertices[^1] = PackHalf4(1, 1, 1, 1);
return new(Array.AsReadOnly(vertices), Array.AsReadOnly(indices));
}
}