using System.Buffers.Binary;
using System.IO.Compression;
using System.Numerics;
using System.Text.Json;
using BCnEncoder.Decoder;
using BCnEncoder.Shared;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Rendering;
public sealed record NebulaTextureReference(string Name,string ResourceHash,string SourceSha256,int DeflateOffset,int InflatedBytes,int VolumeOffset,int RangeOffset,int Size,int RowPitch,int SlicePitch);
public sealed record GalacticNebula(string Name,Coordinates Position,float HalfSize,float[] Rotation,string Template,bool Dark,bool ExactTemplate)
{
public Quaternion Orientation=>Quaternion.CreateFromYawPitchRoll(Rotation[1],Rotation[0],Rotation[2]);
public Vector3 Centre=>new((float)Position.X,(float)Position.Y,(float)Position.Z);
}
public sealed record NebulaReferenceIndex(string GameSha256,string CatalogSha256,NebulaTextureReference[] Templates,GalacticNebula[] Nebulae);
public sealed record NebulaVolume(int Size,byte[] Rgba,Vector4[] Transfer)
{
public float Density(Vector3 uv)
{
if(uv.X<0||uv.Y<0||uv.Z<0||uv.X>1||uv.Y>1||uv.Z>1)return 0;
var x=Math.Clamp((int)(uv.X*(Size-1)+.5f),0,Size-1);var y=Math.Clamp((int)(uv.Y*(Size-1)+.5f),0,Size-1);var z=Math.Clamp((int)(uv.Z*(Size-1)+.5f),0,Size-1);
var alpha=Rgba[((z*Size+y)*Size+x)*4+3]/255f;
return alpha*Math.Max(0,Transfer[Math.Clamp((int)(alpha*255),0,255)].W);
}
public float Transmission(Vector3 localStar,Vector3 towardViewer,float maximumDistance=float.PositiveInfinity)
{
if(!Intersect(localStar,towardViewer,out var near,out var far)||far<=0)return 1;
near=Math.Max(0,near);far=Math.Min(far,maximumDistance);if(far<=near)return 1;
const int samples=12;var step=(far-near)/samples;var depth=0f;
for(var i=0;i<samples;i++)depth+=Density((localStar+towardViewer*(near+(i+.5f)*step))*.5f+new Vector3(.5f))*step;
return MathF.Exp(-depth*1.8f);
}
public static bool Intersect(Vector3 origin,Vector3 direction,out float near,out float far)
{
near=float.NegativeInfinity;far=float.PositiveInfinity;
for(var i=0;i<3;i++)
{
var o=i==0?origin.X:i==1?origin.Y:origin.Z;var d=i==0?direction.X:i==1?direction.Y:direction.Z;
if(Math.Abs(d)<1e-7f){if(Math.Abs(o)>1)return false;continue;}
var a=(-1-o)/d;var b=(1-o)/d;if(a>b)(a,b)=(b,a);
near=Math.Max(near,a);far=Math.Min(far,b);if(near>=far)return false;
}
return float.IsFinite(near)&&float.IsFinite(far);
}
}
/// <summary>Original 64³ BC7 volume slices and HDR transfer functions; no screenshots or generated cloud textures.</summary>
public static class EliteNebulae
{
public static NebulaReferenceIndex Reference {get;}=ReadReference();
private static NebulaReferenceIndex ReadReference()
{
using var stream=typeof(EliteNebulae).Assembly.GetManifestResourceStream("XFEEDToolBox.Core.Rendering.local-nebula-reference.json")!;
return JsonSerializer.Deserialize<NebulaReferenceIndex>(stream,ToolboxSettings.JsonOptions)!;
}
public static NebulaVolume Read(EliteLocalModels resources,string template)
{
var entry=Reference.Templates.Single(r=>r.Name.Equals(template,StringComparison.OrdinalIgnoreCase));
var raw=resources.ReadDisplayResource(entry.ResourceHash,entry.SourceSha256);
if(entry.DeflateOffset<144||entry.DeflateOffset>=raw.Length||entry.InflatedBytes!=361512||!raw.AsSpan(0,4).SequenceEqual("FRES"u8))throw new InvalidDataException("星云资源封装无效。");
using var compressed=new ZLibStream(new MemoryStream(raw,entry.DeflateOffset,raw.Length-entry.DeflateOffset,false),CompressionMode.Decompress);
var bytes=new byte[entry.InflatedBytes];compressed.ReadExactly(bytes);
if(compressed.ReadByte()!=-1)throw new InvalidDataException("星云资源长度与索引不符。");
return Decode(bytes,entry);
}
public static NebulaVolume Decode(byte[] bytes,NebulaTextureReference entry)
{
if(bytes.Length!=entry.InflatedBytes||entry.Size!=64||entry.RowPitch!=256||entry.SlicePitch!=4096||entry.VolumeOffset<1064||entry.RangeOffset<1064||
(long)entry.VolumeOffset+entry.SlicePitch*entry.Size>bytes.Length||(long)entry.RangeOffset+256*16>bytes.Length)throw new InvalidDataException("星云体积纹理越界。");
var transfer=new Vector4[256];
for(var i=0;i<transfer.Length;i++)
{
float F(int c)=>BinaryPrimitives.ReadSingleLittleEndian(bytes.AsSpan(entry.RangeOffset+i*16+c*4,4));
var v=new Vector4(F(0),F(1),F(2),F(3));
if(!float.IsFinite(v.X+v.Y+v.Z+v.W)||Math.Abs(v.X)>10000||Math.Abs(v.Y)>10000||Math.Abs(v.Z)>10000||v.W is <0 or >10000)throw new InvalidDataException("星云传递函数无效。");
transfer[i]=v;
}
var pixels=new byte[entry.Size*entry.Size*entry.Size*4];var decoder=new BcDecoder();
for(var z=0;z<entry.Size;z++)
{
var slice=decoder.DecodeRaw(bytes.AsSpan(entry.VolumeOffset+z*entry.SlicePitch,entry.SlicePitch).ToArray(),entry.Size,entry.Size,CompressionFormat.Bc7);
for(var i=0;i<slice.Length;i++){var at=(z*entry.Size*entry.Size+i)*4;var p=slice[i];pixels[at]=p.r;pixels[at+1]=p.g;pixels[at+2]=p.b;pixels[at+3]=p.a;}
}
return new(entry.Size,pixels,transfer);
}
}
using System.Buffers.Binary;
using System.IO.Compression;
using System.Numerics;
using System.Text.Json;
using BCnEncoder.Decoder;
using BCnEncoder.Shared;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Rendering;
public sealed record NebulaTextureReference(string Name,string ResourceHash,string SourceSha256,int DeflateOffset,int InflatedBytes,int VolumeOffset,int RangeOffset,int Size,int RowPitch,int SlicePitch);
public sealed record GalacticNebula(string Name,Coordinates Position,float HalfSize,float[] Rotation,string Template,bool Dark,bool ExactTemplate)
{
public Quaternion Orientation=>Quaternion.CreateFromYawPitchRoll(Rotation[1],Rotation[0],Rotation[2]);
public Vector3 Centre=>new((float)Position.X,(float)Position.Y,(float)Position.Z);
}
public sealed record NebulaReferenceIndex(string GameSha256,string CatalogSha256,NebulaTextureReference[] Templates,GalacticNebula[] Nebulae);
public sealed record NebulaVolume(int Size,byte[] Rgba,Vector4[] Transfer)
{
public float Density(Vector3 uv)
{
if(uv.X<0||uv.Y<0||uv.Z<0||uv.X>1||uv.Y>1||uv.Z>1)return 0;
var x=Math.Clamp((int)(uv.X*(Size-1)+.5f),0,Size-1);var y=Math.Clamp((int)(uv.Y*(Size-1)+.5f),0,Size-1);var z=Math.Clamp((int)(uv.Z*(Size-1)+.5f),0,Size-1);
var alpha=Rgba[((z*Size+y)*Size+x)*4+3]/255f;
return alpha*Math.Max(0,Transfer[Math.Clamp((int)(alpha*255),0,255)].W);
}
public float Transmission(Vector3 localStar,Vector3 towardViewer,float maximumDistance=float.PositiveInfinity)
{
if(!Intersect(localStar,towardViewer,out var near,out var far)||far<=0)return 1;
near=Math.Max(0,near);far=Math.Min(far,maximumDistance);if(far<=near)return 1;
const int samples=12;var step=(far-near)/samples;var depth=0f;
for(var i=0;i<samples;i++)depth+=Density((localStar+towardViewer*(near+(i+.5f)*step))*.5f+new Vector3(.5f))*step;
return MathF.Exp(-depth*1.8f);
}
public static bool Intersect(Vector3 origin,Vector3 direction,out float near,out float far)
{
near=float.NegativeInfinity;far=float.PositiveInfinity;
for(var i=0;i<3;i++)
{
var o=i==0?origin.X:i==1?origin.Y:origin.Z;var d=i==0?direction.X:i==1?direction.Y:direction.Z;
if(Math.Abs(d)<1e-7f){if(Math.Abs(o)>1)return false;continue;}
var a=(-1-o)/d;var b=(1-o)/d;if(a>b)(a,b)=(b,a);
near=Math.Max(near,a);far=Math.Min(far,b);if(near>=far)return false;
}
return float.IsFinite(near)&&float.IsFinite(far);
}
}
/// <summary>Original 64³ BC7 volume slices and HDR transfer functions; no screenshots or generated cloud textures.</summary>
public static class EliteNebulae
{
public static NebulaReferenceIndex Reference {get;}=ReadReference();
private static NebulaReferenceIndex ReadReference()
{
using var stream=typeof(EliteNebulae).Assembly.GetManifestResourceStream("XFEEDToolBox.Core.Rendering.local-nebula-reference.json")!;
return JsonSerializer.Deserialize<NebulaReferenceIndex>(stream,ToolboxSettings.JsonOptions)!;
}
public static NebulaVolume Read(EliteLocalModels resources,string template)
{
var entry=Reference.Templates.Single(r=>r.Name.Equals(template,StringComparison.OrdinalIgnoreCase));
var raw=resources.ReadDisplayResource(entry.ResourceHash,entry.SourceSha256);
if(entry.DeflateOffset<144||entry.DeflateOffset>=raw.Length||entry.InflatedBytes!=361512||!raw.AsSpan(0,4).SequenceEqual("FRES"u8))throw new InvalidDataException("星云资源封装无效。");
using var compressed=new ZLibStream(new MemoryStream(raw,entry.DeflateOffset,raw.Length-entry.DeflateOffset,false),CompressionMode.Decompress);
var bytes=new byte[entry.InflatedBytes];compressed.ReadExactly(bytes);
if(compressed.ReadByte()!=-1)throw new InvalidDataException("星云资源长度与索引不符。");
return Decode(bytes,entry);
}
public static NebulaVolume Decode(byte[] bytes,NebulaTextureReference entry)
{
if(bytes.Length!=entry.InflatedBytes||entry.Size!=64||entry.RowPitch!=256||entry.SlicePitch!=4096||entry.VolumeOffset<1064||entry.RangeOffset<1064||
(long)entry.VolumeOffset+entry.SlicePitch*entry.Size>bytes.Length||(long)entry.RangeOffset+256*16>bytes.Length)throw new InvalidDataException("星云体积纹理越界。");
var transfer=new Vector4[256];
for(var i=0;i<transfer.Length;i++)
{
float F(int c)=>BinaryPrimitives.ReadSingleLittleEndian(bytes.AsSpan(entry.RangeOffset+i*16+c*4,4));
var v=new Vector4(F(0),F(1),F(2),F(3));
if(!float.IsFinite(v.X+v.Y+v.Z+v.W)||Math.Abs(v.X)>10000||Math.Abs(v.Y)>10000||Math.Abs(v.Z)>10000||v.W is <0 or >10000)throw new InvalidDataException("星云传递函数无效。");
transfer[i]=v;
}
var pixels=new byte[entry.Size*entry.Size*entry.Size*4];var decoder=new BcDecoder();
for(var z=0;z<entry.Size;z++)
{
var slice=decoder.DecodeRaw(bytes.AsSpan(entry.VolumeOffset+z*entry.SlicePitch,entry.SlicePitch).ToArray(),entry.Size,entry.Size,CompressionFormat.Bc7);
for(var i=0;i<slice.Length;i++){var at=(z*entry.Size*entry.Size+i)*4;var p=slice[i];pixels[at]=p.r;pixels[at+1]=p.g;pixels[at+2]=p.b;pixels[at+3]=p.a;}
}
return new(entry.Size,pixels,transfer);
}
}