WorkInProgress/NanoVDB.cs
using System.IO;
using NativeEngine;

namespace Sandbox;

/// <summary>
/// File structure of .nvdb goes as follows:
///     * Header
///     * MetaData
///     * List of Grids
/// </summary>
public partial class NanoVDB

{
    public Header FileHeader { get; set; }
    public Metadata FileMetadata { get; set; }
    public List<Grid> Grids { get; set; } = new();


    [Category( "VDB Settings" )] public bool IsSequence { get; set; } = false;
	[Category( "VDB Info" )] public BBox BoundingBox { get => FileMetadata.WorldBBox; private set { } }

    public static NanoVDB Load( string filePath )
    {
        
		using var fileStream = FileSystem.Mounted.OpenRead( filePath );

		if ( !filePath.EndsWith( ".nvdb" ) )
            throw new Exception("Only NanoVDB files are supported for now");
            
        using var reader = new BinaryReader( fileStream );
        
        var nanoVDB = new NanoVDB();
        nanoVDB.Read( reader );

        return nanoVDB;
    }

    private void Read( BinaryReader reader )
    {
        FileHeader = new()
        {
            Magic = (Magic)reader.ReadUInt64(),
            Version = reader.ReadUInt32(),
            GridCount = reader.ReadInt16(),
            Compression = (Compression)reader.ReadInt16()
        };

        if( !FileHeader.IsValid() )
            throw new Exception("Invalid NanoVDB file");

        if( FileHeader.Compression != Compression.None )
            throw new Exception("Only uncompressed NanoVDB files are supported");

        FileMetadata = Metadata.Read(reader);

		if( FileMetadata.Codec != Compression.None )
			throw new Exception( "Only uncompressed NanoVDB files are supported" );

		for (int i = 0; i < FileHeader.GridCount; i++)
        {
            Grids.Add(Grid.Read(reader, FileMetadata ) );
        }
    }

    /// <summary>
    /// Represents the header of a NanoVDB file.
    /// </summary>
    public class Header
    {
        public Magic Magic { get; set; } // 8 bytes
        public uint Version { get; set; } // 4 bytes version numbers
        public short GridCount { get; set; } // 2 bytes
        public Compression Compression { get; set; } // 2 bytes
        public bool IsValid()
        {
            return Magic == Magic.Numb || Magic == Magic.File;
        }
    }

    /// <summary>
    /// Data encoded for each of the grids associated with a segment.
    /// </summary>
    public class Metadata
    {
        // Grid size in memory (uint64_t)
        public ulong GridSize { get; set; } // 8 bytes
        // Grid size on disk (uint64_t)
        public ulong FileSize { get; set; } // 8 bytes
        // Grid name hash key (uint64_t)
        public ulong NameKey { get; set; } // 8 bytes
        // Number of active voxels (uint64_t)
        public ulong VoxelCount { get; set; } // 8 bytes
        // Grid type (uint32_t)
        public GridType GridType { get; set; } // 4 bytes
        // Grid class (uint32_t)
        public GridClass GridClass { get; set; } // 4 bytes
        // AABB in world space (2*3*double)
        public BBox WorldBBox { get; set; } // 48 bytes
        // AABB in index space (2*3*int)
        public BBox IndexBBox { get; set; } // 24 bytes
        // Size of a voxel in world units (3*double)
        public Vector3 VoxelSize { get; set; } // 24 bytes
        // Byte size of the grid name (uint32_t)
        public uint NameSize { get; set; } // 4 bytes
        // Number of nodes per level (4*uint32_t)
        public uint[] NodeCount { get; set; } = new uint[4]; // 16 bytes
        // Number of active tiles per level (3*uint32_t)
        public uint[] TileCount { get; set; } = new uint[3]; // 12 bytes
        // Codec for file compression (uint16_t)
        public Compression Codec { get; set; } // 2 bytes
        // Padding due to 8B alignment (uint16_t)
        public ushort Padding { get; set; } // 2 bytes
        // Version number (uint32_t)
        public uint Version { get; set; } // 4 bytes

        public static Metadata Read( BinaryReader reader )
        {
            Metadata metadata = new()
            {
                GridSize = reader.ReadUInt64(),
                FileSize = reader.ReadUInt64(),
                NameKey = reader.ReadUInt64(),
                VoxelCount = reader.ReadUInt64(),
                GridType = (GridType)reader.ReadUInt32(),
                GridClass = (GridClass)reader.ReadUInt32(),
                WorldBBox = new BBox( new Vector3( (float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble() ), new Vector3( (float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble() ) ),
                IndexBBox = new BBox( new Vector3( (float)reader.ReadInt32(), (float)reader.ReadInt32(), (float)reader.ReadInt32() ), new Vector3( (float)reader.ReadInt32(), (float)reader.ReadInt32(), (float)reader.ReadInt32() ) ),
				VoxelSize = new Vector3( (float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble() ),
                NameSize = reader.ReadUInt32(),
                NodeCount = new uint[] { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() },
                TileCount = new uint[] { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() },
                Codec = (Compression)reader.ReadUInt16(),
                Padding = reader.ReadUInt16(),
                Version = reader.ReadUInt32()
            };
            return metadata;
        }
    }

    //---------------------------------------------------------------------------------------------------

    public enum Magic : ulong
    {
        Numb = 0x304244566f6e614eUL, // "NanoVDB0" in hex - little endian (uint64_t)
        Grid = 0x314244566f6e614eUL, // "NanoVDB1" in hex - little endian (uint64_t)
        File = 0x324244566f6e614eUL, // "NanoVDB2" in hex - little endian (uint64_t)
        Node = 0x334244566f6e614eUL, // "NanoVDB3" in hex - little endian (uint64_t)
        Frag = 0x344244566f6e614eUL  // "NanoVDB4" in hex - little endian (uint64_t)
    }

    public enum Compression : short
    {
        None = 0,
        Zip = 1,
        Blosc = 2
    }
    public enum GridType : uint 
    { 
        Unknown, //  unknown value type - should rarely be used
        Float, //  single precision floating point value
        Double, //  double precision floating point value
        Int16, //  half precision signed integer value
        Int32, //  single precision signed integer value
        Int64, //  double precision signed integer value
        Vec3f, //  single precision floating 3D vector
        Vec3d, //  double precision floating 3D vector
        Mask, //  no value, just the active state
        Half, //  half precision floating point value (placeholder for IEEE 754 Half)
        UInt32, // single precision unsigned integer value
        Boolean, // boolean value, encoded in bit array
        RGBA8, // RGBA packed into 32bit word in reverse-order, i.e. R is lowest byte.
        Fp4, // 4bit quantization of floating point value
        Fp8, // 8bit quantization of floating point value
        Fp16, // 16bit quantization of floating point value
        FpN, // variable bit quantization of floating point value
        Vec4f, // single precision floating 4D vector
        Vec4d, // double precision floating 4D vector
        Index, // index into an external array of active and inactive values
        OnIndex, // index into an external array of active values
        IndexMask, // like Index but with a mutable mask
        OnIndexMask, // like OnIndex but with a mutable mask
        PointIndex, // voxels encode indices to co-located points
        Vec3u8, // 8bit quantization of floating point 3D vector (only as blind data)
        Vec3u16, // 16bit quantization of floating point 3D vector (only as blind data)
        UInt8, // 8 bit unsigned integer values (eg 0 -> 255 gray scale)
    };

    public enum GridClass 
    {
        Unknown,
        LevelSet, // narrow band level set, e.g. SDF
        FogVolume, // fog volume, e.g. density
        Staggered, // staggered MAC grid, e.g. velocity
        PointIndex, // point index grid
        PointData, // point data grid
        Topology, // grid with active states only (no values)
        VoxelVolume, // volume of geometric cubes, e.g. colors cubes in Minecraft
        IndexGrid, // grid whose values are offsets, e.g. into an external array
        TensorGrid, // Index grid for indexing learnable tensor features
    };

    /// <summary>
    /// Grid flags which indicate what extra information is present in the grid buffer.
    /// </summary>
    [Flags]
    public enum GridFlags {
        HasLongGridName = 1 << 0, // grid name is longer than 256 characters
        HasBBox = 1 << 1, // nodes contain bounding-boxes of active values
        HasMinMax = 1 << 2, // nodes contain min/max of active values
        HasAverage = 1 << 3, // nodes contain averages of active values
        HasStdDeviation = 1 << 4, // nodes contain standard deviations of active values
        IsBreadthFirst = 1 << 5, // nodes are typically arranged breadth-first in memory
        End = 1 << 6, // use End - 1 as a mask for the 5 lower bit flags
    };
}