using AssetRipper.SourceGenerated.Subclasses.PackedBitVector_Single;
using System.Runtime.InteropServices;
namespace AssetRipper.SourceGenerated.Extensions;
public static class PackedFloatVectorExtensions
{
extension(PackedBitVector_Single packedVector)
{
public bool IsSet => packedVector.NumItems > 0;
public float[] Unpack()
{
return packedVector.Unpack(1, 1);
}
public float[] Unpack(int itemCountInChunk, int chunkStride, int start = 0, int numChunks = -1)
{
if (chunkStride < itemCountInChunk)
{
throw new ArgumentException("Chunk stride must be at least as large as the item count in each chunk.");
}
if (numChunks == -1)
{
numChunks = (int)packedVector.NumItems / chunkStride;
}
PackedBitReader reader = new(packedVector, start);
float[] result = new float[numChunks * itemCountInChunk];
for (int chunk = 0; chunk < numChunks; chunk++)
{
for (int i = 0; i < itemCountInChunk; i++)
{
result[chunk * itemCountInChunk + i] = reader.Read();
}
for (int i = chunkStride - itemCountInChunk; i > 0; i--)
{
reader.Read(); // Skip to the next chunk
}
}
return result;
}
///
/// A high accuracy default packing method for any type.
///
///
///
public void Pack(ReadOnlySpan data) where T : unmanaged
{
packedVector.Pack(MemoryMarshal.Cast(data));
}
///
/// A high accuracy default packing method
///
///
///
public void Pack(ReadOnlySpan data)
{
packedVector.Pack(data, 24, false);
}
public void Pack(ReadOnlySpan data, int bitSize, bool adjustBitSize)
{
GetMinimumAndMaximum(data, out float minf, out float maxf);
float range = maxf - minf;
if (adjustBitSize)
{
bitSize += GetBitCount(range);
}
if (bitSize > 32)
{
bitSize = 32;
}
packedVector.Start = minf;
packedVector.Range = range;
packedVector.NumItems = (uint)data.Length;
packedVector.BitSize = (byte)bitSize;
packedVector.Data = new byte[(packedVector.NumItems * bitSize + 7) / 8];
float f = BitConverter.UInt32BitsToSingle((1u << packedVector.BitSize) - 1u);
double scale = 1.0d / packedVector.Range;
int bitIndex = 0;
int byteIndex = 0;
for (int i = 0; i < data.Length; ++i)
{
double scaled = double.Clamp((data[i] - packedVector.Start) * scale, 0, 1);
double d = scaled * f;
uint x = BitConverter.SingleToUInt32Bits((float)d);
int bits = 0;
while (bits < packedVector.BitSize)
{
packedVector.Data[byteIndex] |= unchecked((byte)(x >> bits << bitIndex));
int read = Math.Min(packedVector.BitSize - bits, 8 - bitIndex);
bitIndex += read;
bits += read;
if (bitIndex == 8)
{
byteIndex++;
bitIndex = 0;
}
}
}
}
}
private ref struct PackedBitReader
{
private readonly PackedBitVector_Single packedVector;
private int bitIndex;
private int byteIndex;
private readonly float scale;
private readonly uint mask;
private readonly float halfMaxValue;
public PackedBitReader(PackedBitVector_Single packedVector, int start)
{
this.packedVector = packedVector;
bitIndex = packedVector.BitSize * start;
byteIndex = bitIndex / 8;
bitIndex %= 8;
scale = 1.0f / packedVector.Range;
mask = (1u << packedVector.BitSize) - 1u;
halfMaxValue = scale * mask;
}
public float Read()
{
uint x = 0;
int bits = 0;
int bitSize = packedVector.BitSize;
while (bits < bitSize)
{
x |= (uint)packedVector.Data[byteIndex] >> bitIndex << bits;
int read = Math.Min(bitSize - bits, 8 - bitIndex);
bitIndex += read;
bits += read;
if (bitIndex == 8)
{
byteIndex++;
bitIndex = 0;
}
}
x &= mask;
return x / halfMaxValue + packedVector.Start;
}
}
private static void GetMinimumAndMaximum(ReadOnlySpan data, out float min, out float max)
{
min = float.PositiveInfinity;
max = float.NegativeInfinity;
for (int i = 0; i < data.Length; ++i)
{
if (max < data[i])
{
max = data[i];
}
if (min > data[i])
{
min = data[i];
}
}
}
private static int GetBitCount(double value)
{
double log = Math.Log2(value);
return (int)Math.Ceiling(log);
}
}