/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see .
*/
#include
#include
#include
#include
#include
namespace {
enum texture_type: std::uint8_t
{
texture_type_1d,
texture_type_1d_array,
texture_type_2d,
texture_type_2d_array,
texture_type_3d,
texture_type_cube,
texture_type_cube_array
};
[[nodiscard]] std::unique_ptr load_texture(::resource_manager& resource_manager, deserialize_context& ctx, std::uint8_t texture_type)
{
// Read file format identifier
std::uint32_t format_identifier{0};
ctx.read32_le(reinterpret_cast(&format_identifier), 1);
// Validate file format identifier (U+1F5BC = framed picture)
if (format_identifier != 0xbc969ff0)
{
throw deserialize_error("Invalid texture file.");
}
// Read file format version
std::uint32_t format_version{0};
ctx.read32_le(reinterpret_cast(&format_version), 1);
// Validate file format version
if (format_version != 1)
{
throw deserialize_error("Unsupported texture file version.");
}
// Read image path
std::uint32_t image_path_length{0};
ctx.read32_le(reinterpret_cast(&image_path_length), 1);
std::string image_path(image_path_length, '\0');
ctx.read8(reinterpret_cast(image_path.data()), image_path_length);
// Load image
std::shared_ptr image;
switch (texture_type)
{
case texture_type_1d:
case texture_type_1d_array:
image = resource_manager.load(image_path);
break;
case texture_type_2d:
case texture_type_2d_array:
image = resource_manager.load(image_path);
break;
case texture_type_3d:
image = resource_manager.load(image_path);
break;
case texture_type_cube:
case texture_type_cube_array:
image = resource_manager.load(image_path);
break;
default:
throw deserialize_error("Invalid texture type.");
}
// Read image view parameters
gl::format format;
std::uint32_t first_mip_level;
std::uint32_t mip_level_count;
std::uint32_t first_array_layer;
std::uint32_t array_layer_count;
ctx.read32_le(reinterpret_cast(&format), 1);
ctx.read32_le(reinterpret_cast(&first_mip_level), 1);
ctx.read32_le(reinterpret_cast(&mip_level_count), 1);
ctx.read32_le(reinterpret_cast(&first_array_layer), 1);
ctx.read32_le(reinterpret_cast(&array_layer_count), 1);
// Handle automatic mip level count (`0`)
if (!mip_level_count)
{
mip_level_count = image->get_mip_levels();
}
// Handle automatic array layer count (`0`)
if (!array_layer_count)
{
array_layer_count = image->get_array_layers();
}
// Read sampler parameters
gl::sampler_filter mag_filter;
gl::sampler_filter min_filter;
gl::sampler_mipmap_mode mipmap_mode;
gl::sampler_address_mode address_mode_u;
gl::sampler_address_mode address_mode_v;
gl::sampler_address_mode address_mode_w;
float mip_lod_bias;
float max_anisotropy;
std::uint8_t compare_enabled;
gl::compare_op compare_op;
float min_lod;
float max_lod;
std::array border_color;
ctx.read8(reinterpret_cast(&mag_filter), 1);
ctx.read8(reinterpret_cast(&min_filter), 1);
ctx.read8(reinterpret_cast(&mipmap_mode), 1);
ctx.read8(reinterpret_cast(&address_mode_u), 1);
ctx.read8(reinterpret_cast(&address_mode_v), 1);
ctx.read8(reinterpret_cast(&address_mode_w), 1);
ctx.read32_le(reinterpret_cast(&mip_lod_bias), 1);
ctx.read32_le(reinterpret_cast(&max_anisotropy), 1);
ctx.read8(reinterpret_cast(&compare_enabled), 1);
ctx.read8(reinterpret_cast(&compare_op), 1);
ctx.read32_le(reinterpret_cast(&min_lod), 1);
ctx.read32_le(reinterpret_cast(&max_lod), 1);
ctx.read32_le(reinterpret_cast(border_color.data()), 4);
// Construct sampler
std::shared_ptr sampler = std::make_shared
(
mag_filter,
min_filter,
mipmap_mode,
address_mode_u,
address_mode_v,
address_mode_w,
mip_lod_bias,
max_anisotropy,
compare_enabled,
compare_op,
min_lod,
max_lod,
border_color
);
// Construct image view and texture
switch (texture_type)
{
case texture_type_1d:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_1d_array:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_2d:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_2d_array:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_3d:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_cube:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer);
return std::make_unique(std::move(image_view), std::move(sampler));
}
case texture_type_cube_array:
{
auto image_view = std::make_shared(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
return std::make_unique(std::move(image_view), std::move(sampler));
}
default:
return nullptr;
}
}
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_1d).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_1d_array).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_2d).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_2d_array).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_3d).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_cube).release()));
}
template <>
std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
return std::unique_ptr(static_cast(load_texture(resource_manager, ctx, texture_type_cube_array).release()));
}