|
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <engine/gl/texture.hpp>
|
|
#include <engine/resources/deserialize-context.hpp>
|
|
#include <engine/resources/deserialize-error.hpp>
|
|
#include <engine/resources/resource-loader.hpp>
|
|
#include <engine/resources/resource-manager.hpp>
|
|
|
|
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<gl::texture> 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<std::byte*>(&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<std::byte*>(&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<std::byte*>(&image_path_length), 1);
|
|
std::string image_path(image_path_length, '\0');
|
|
ctx.read8(reinterpret_cast<std::byte*>(image_path.data()), image_path_length);
|
|
|
|
// Load image
|
|
std::shared_ptr<gl::image> image;
|
|
switch (texture_type)
|
|
{
|
|
case texture_type_1d:
|
|
case texture_type_1d_array:
|
|
image = resource_manager.load<gl::image_1d>(image_path);
|
|
break;
|
|
|
|
case texture_type_2d:
|
|
case texture_type_2d_array:
|
|
image = resource_manager.load<gl::image_2d>(image_path);
|
|
break;
|
|
|
|
case texture_type_3d:
|
|
image = resource_manager.load<gl::image_3d>(image_path);
|
|
break;
|
|
|
|
case texture_type_cube:
|
|
case texture_type_cube_array:
|
|
image = resource_manager.load<gl::image_cube>(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<std::byte*>(&format), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&first_mip_level), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&mip_level_count), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&first_array_layer), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&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<float, 4> border_color;
|
|
ctx.read8(reinterpret_cast<std::byte*>(&mag_filter), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&min_filter), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&mipmap_mode), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&address_mode_u), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&address_mode_v), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&address_mode_w), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&mip_lod_bias), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&max_anisotropy), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&compare_enabled), 1);
|
|
ctx.read8(reinterpret_cast<std::byte*>(&compare_op), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&min_lod), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(&max_lod), 1);
|
|
ctx.read32_le(reinterpret_cast<std::byte*>(border_color.data()), 4);
|
|
|
|
// Construct sampler
|
|
std::shared_ptr<gl::sampler> sampler = std::make_shared<gl::sampler>
|
|
(
|
|
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<gl::image_view_1d>(image, format, first_mip_level, mip_level_count, first_array_layer);
|
|
return std::make_unique<gl::texture_1d>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_1d_array:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_1d_array>(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
|
|
return std::make_unique<gl::texture_1d_array>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_2d:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_2d>(image, format, first_mip_level, mip_level_count, first_array_layer);
|
|
return std::make_unique<gl::texture_2d>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_2d_array:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_2d_array>(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
|
|
return std::make_unique<gl::texture_2d_array>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_3d:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_3d>(image, format, first_mip_level, mip_level_count);
|
|
return std::make_unique<gl::texture_3d>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_cube:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_cube>(image, format, first_mip_level, mip_level_count, first_array_layer);
|
|
return std::make_unique<gl::texture_cube>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
case texture_type_cube_array:
|
|
{
|
|
auto image_view = std::make_shared<gl::image_view_cube_array>(image, format, first_mip_level, mip_level_count, first_array_layer, array_layer_count);
|
|
return std::make_unique<gl::texture_cube_array>(std::move(image_view), std::move(sampler));
|
|
}
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_1d> resource_loader<gl::texture_1d>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_1d>(static_cast<gl::texture_1d*>(load_texture(resource_manager, ctx, texture_type_1d).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_1d_array> resource_loader<gl::texture_1d_array>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_1d_array>(static_cast<gl::texture_1d_array*>(load_texture(resource_manager, ctx, texture_type_1d_array).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_2d> resource_loader<gl::texture_2d>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_2d>(static_cast<gl::texture_2d*>(load_texture(resource_manager, ctx, texture_type_2d).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_2d_array> resource_loader<gl::texture_2d_array>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_2d_array>(static_cast<gl::texture_2d_array*>(load_texture(resource_manager, ctx, texture_type_2d_array).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_3d> resource_loader<gl::texture_3d>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_3d>(static_cast<gl::texture_3d*>(load_texture(resource_manager, ctx, texture_type_3d).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_cube> resource_loader<gl::texture_cube>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_cube>(static_cast<gl::texture_cube*>(load_texture(resource_manager, ctx, texture_type_cube).release()));
|
|
}
|
|
|
|
template <>
|
|
std::unique_ptr<gl::texture_cube_array> resource_loader<gl::texture_cube_array>::load(::resource_manager& resource_manager, deserialize_context& ctx)
|
|
{
|
|
return std::unique_ptr<gl::texture_cube_array>(static_cast<gl::texture_cube_array*>(load_texture(resource_manager, ctx, texture_type_cube_array).release()));
|
|
}
|