BaseVoxelVolume/Importer/VoxelImporter.cs
namespace Boxfish;
/// <summary>
/// Derive from this to add your own importers.
/// </summary>
public abstract class VoxelFormat
{
/// <summary>
/// Array of all supported extensions for this format.
/// </summary>
public abstract string[] Extensions { get; }
/// <summary>
/// This method is executed during the BuildAsync process of <see cref="VoxelImporter"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="U"></typeparam>
/// <param name="rawData"></param>
/// <param name="importer"></param>
/// <returns></returns>
public abstract Task<Dictionary<Vector3Int, BaseVoxelVolume<T, U>.Chunk>> Parse<T, U>( byte[] rawData, VoxelImporter importer )
where T : struct
where U : unmanaged;
}
/// <summary>
/// Implement this struct on your own voxel struct if you want to import with the Boxfish importers.
/// </summary>
/// <typeparam name="T">Same types as your Voxel.</typeparam>
public interface IColorImporter<T>
where T : struct
{
virtual T Create( Color32 color ) { return default ( T ); }
}
/// <summary>
/// Our builder struct for importing voxel models.
/// </summary>
public struct VoxelImporter
{
#pragma warning disable SB3000 // Hotloading not supported
private static List<VoxelFormat> _formats { get; set; }
#pragma warning restore SB3000 // Hotloading not supported
public string Path { get; set; }
public VoxelFormat Format { get; set; }
public string Extension => Path.Length > 1 ? System.IO.Path.GetExtension( Path )[1..] : string.Empty;
private static void LoadFormats()
{
_formats = TypeLibrary.GetTypes<VoxelFormat>()?
.Where( type => !type.IsAbstract )
.Select( type => type.Create<VoxelFormat>() )
.ToList();
}
/// <summary>
/// Begin building map data from a file path.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static VoxelImporter FromPath( string path )
{
var builder = new VoxelImporter()
{
Path = path,
};
// Tried to input empty path?
if ( string.IsNullOrEmpty( path ) )
{
Logger.Warning( $"Cannot create MapBuilder with empty path." );
return builder;
}
// Make sure path exists.
if ( !FileSystem.Mounted.FileExists( path ) )
{
Logger.Warning( $"File at \"{path}\" does not exist." );
return builder;
}
// Try fetch format.
LoadFormats();
var format = _formats?.FirstOrDefault( format => format.Extensions?.Contains( builder.Extension ) ?? false );
if ( format is null )
{
Logger.Warning( $"No valid voxel format for file extension \"{builder.Extension}\" found." );
return builder;
}
builder.Format = format;
return builder;
}
/// <summary>
/// Override the format of this builder.
/// </summary>
/// <typeparam name="U"></typeparam>
/// <returns></returns>
public VoxelImporter WithFormat<U>()
where U : VoxelFormat
{
return this with
{
Format = TypeLibrary.Create<U>()
};
}
/// <summary>
/// Build voxel data from the map data asynchronously.
/// </summary>
/// <returns></returns>
public async Task<Dictionary<Vector3Int, BaseVoxelVolume<T, U>.Chunk>> BuildAsync<T, U>()
where T : struct
where U : unmanaged
{
// Format was null?
if ( Format is null )
{
Logger.Warning( $"Tried to build map with no valid voxel format? [ext: {Extension}]" );
return null;
}
// Read file and parse.
var result = (Dictionary<Vector3Int, BaseVoxelVolume<T, U>.Chunk>)null;
var content = await FileSystem.Mounted.ReadAllBytesAsync( Path );
// No binary content found?
if ( content is null )
{
return null;
}
result = await Format.Parse<T, U>( content, this );
// Result was null?
if ( result is null )
{
Logger.Warning( $"Voxel importer failed to import, result was null?" );
}
return result;
}
}