feat: asset baking
Only supported .png textures at the moment
This commit is contained in:
parent
71d08cbe9f
commit
38997b3908
23 changed files with 594 additions and 393 deletions
BIN
data/assets/textures/awesomeface.asset
Normal file
BIN
data/assets/textures/awesomeface.asset
Normal file
Binary file not shown.
BIN
data/engine/icons/asset/dir.asset
Normal file
BIN
data/engine/icons/asset/dir.asset
Normal file
Binary file not shown.
BIN
data/engine/icons/asset/img.asset
Normal file
BIN
data/engine/icons/asset/img.asset
Normal file
Binary file not shown.
BIN
data/engine/icons/asset/scene.asset
Normal file
BIN
data/engine/icons/asset/scene.asset
Normal file
Binary file not shown.
BIN
data/engine/icons/asset/txt.asset
Normal file
BIN
data/engine/icons/asset/txt.asset
Normal file
Binary file not shown.
|
@ -5,6 +5,7 @@ Layout
|
|||
{version} | 4 bytes, ie. uint32_t
|
||||
{general metadata} | sizeof(AssetMetadata)
|
||||
{specialized metadata} | sizeof(XXXAssetMetadata), eg. TextureAssetMetadata
|
||||
{n} | 4 bytes, ie. uint32_t
|
||||
{blob_0...n metadata} | n * sizeof(BlobMetadata)
|
||||
{blob_0...n data} | variable size based on actual data
|
||||
{end marker} | 8 byte, ie size_t for marking the END
|
||||
|
@ -13,7 +14,8 @@ Sections
|
|||
---------------------------------------------------------------------------------------------------
|
||||
version -> The version of the asset for forward compatibility
|
||||
general metadata -> Common asset metadata such as file-path, asset-type, creator, etc.
|
||||
specialized metadata -> Metadata specific to the asset, eg. texture dimensions for Textures
|
||||
specialized metadata -> Metadata specific to the asset, eg. texture dimensions for Textures.
|
||||
n -> The number of blobs.
|
||||
blob_0...n metadata -> Metadata specifying how the actual data is packed, required for unpacking.
|
||||
blob_0...n data -> The actual data, packed and compressed to be reacdy for direct engine consumption.
|
||||
|
||||
|
|
4
docs/architecture/resource.rst
Normal file
4
docs/architecture/resource.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Resource Management
|
||||
|
||||
===================================================================================================
|
||||
|
|
@ -6,4 +6,5 @@ target_link_libraries(
|
|||
asset_baker
|
||||
PRIVATE asset_parser
|
||||
PRIVATE stb::stb
|
||||
PRIVATE logger
|
||||
)
|
||||
|
|
120
modules/asset_baker/include/asset_baker/bakers.hpp
Normal file
120
modules/asset_baker/include/asset_baker/bakers.hpp
Normal file
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include <asset_parser/assets/texture.hpp>
|
||||
#include <filesystem>
|
||||
#include <logger/logger.hpp>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class Loader
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] virtual auto get_name() const -> std::string_view = 0;
|
||||
|
||||
Loader() = default;
|
||||
|
||||
Loader(Loader &&) = default;
|
||||
|
||||
Loader(const Loader &) = delete;
|
||||
|
||||
auto operator=(Loader &&) -> Loader & = default;
|
||||
|
||||
auto operator=(const Loader &) -> Loader & = delete;
|
||||
|
||||
virtual ~Loader() = default;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class TextureLoader: public Loader
|
||||
{
|
||||
public:
|
||||
TextureLoader() = default;
|
||||
|
||||
[[nodiscard]] virtual auto load(std::filesystem::path file_path) const
|
||||
-> Assets::TextureAsset::PackageData
|
||||
= 0;
|
||||
};
|
||||
|
||||
|
||||
class StbLoader: public TextureLoader
|
||||
{
|
||||
public:
|
||||
StbLoader() = default;
|
||||
|
||||
void load(std::filesystem::path path);
|
||||
|
||||
[[nodiscard]] static auto get_supported_extensions() -> std::unordered_set<std::string_view>
|
||||
{
|
||||
return { ".png" };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_name() const -> std::string_view override
|
||||
{
|
||||
return "StbLoader";
|
||||
}
|
||||
|
||||
[[nodiscard]] auto load(std::filesystem::path file_path) const
|
||||
-> Assets::TextureAsset::PackageData override
|
||||
{
|
||||
auto width = int {};
|
||||
auto height = int {};
|
||||
auto channels = int {};
|
||||
|
||||
auto *pixels = stbi_load(file_path.string().c_str(), &width, &height, &channels, 4);
|
||||
if (!pixels)
|
||||
{
|
||||
throw std::runtime_error {
|
||||
std::format("Failed to load image file at: {} using stbi_load", file_path.string()),
|
||||
};
|
||||
}
|
||||
|
||||
const auto metadata = Assets::Asset::Metadata {
|
||||
.type = Assets::Asset::Type::Texture,
|
||||
};
|
||||
|
||||
const auto texture_metadata = Assets::TextureAsset::Metadata {
|
||||
.format = Assets::TextureAsset::Format::RGBA8,
|
||||
.num_components = static_cast<uint32_t>(channels),
|
||||
.pixel_size = {
|
||||
static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height),
|
||||
{},
|
||||
},
|
||||
};
|
||||
|
||||
auto pixels_blob = Assets::Blob {};
|
||||
pixels_blob.resize(static_cast<size_t>(width) * height * channels);
|
||||
|
||||
// TODO(Light): figure out if it's possible to directly populate a blob with stbi functions
|
||||
memcpy(pixels_blob.data(), pixels, pixels_blob.size());
|
||||
stbi_image_free(pixels);
|
||||
|
||||
return Assets::TextureAsset::PackageData {
|
||||
.metadata = metadata,
|
||||
.texture_metadata = texture_metadata,
|
||||
.pixels = std::move(pixels_blob),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class TextureLoaderFactory
|
||||
{
|
||||
public:
|
||||
static auto create(std::string_view file_extension) -> std::unique_ptr<TextureLoader>
|
||||
{
|
||||
if (StbLoader::get_supported_extensions().contains(file_extension))
|
||||
{
|
||||
return std::make_unique<StbLoader>();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace lt
|
|
@ -1,85 +1,70 @@
|
|||
#include <asset_baker/bakers.hpp>
|
||||
#include <asset_parser/assets/texture.hpp>
|
||||
#include <asset_parser/parser.hpp>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
#define ASSERT(x, ...) \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
log(__VA_ARGS__); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
void log(Args &&...args)
|
||||
void try_packing_texture(
|
||||
const std::filesystem::path &in_path,
|
||||
const std::filesystem::path &out_path
|
||||
)
|
||||
{
|
||||
(std::cout << ... << args);
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
auto convert_image(const std::filesystem::path &input, const std::filesystem::path &output) -> bool
|
||||
{
|
||||
auto width = int {};
|
||||
auto height = int {};
|
||||
auto channels = int {};
|
||||
|
||||
auto *pixels = stbi_load(input.string().c_str(), &width, &height, &channels, 4);
|
||||
|
||||
if (!pixels)
|
||||
return false;
|
||||
|
||||
auto texture_info = Assets::TextureInfo {
|
||||
.size = static_cast<size_t>(width * height * 4),
|
||||
.format = Assets::TextureFormat::RGBA8,
|
||||
.pixel_size = {
|
||||
static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height),
|
||||
0ul,
|
||||
},
|
||||
.original_file = input.string(),
|
||||
};
|
||||
|
||||
auto file = Assets::pack_texture(&texture_info, pixels);
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
Assets::save_binary_file(output.string().c_str(), file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
|
||||
ASSERT(
|
||||
argc == 3,
|
||||
"Argc MUST be 3, 1: execution-path(implicit), 2: input-directory, 3: output-directory"
|
||||
);
|
||||
|
||||
for (const auto &p : std::filesystem::directory_iterator(argv[1]))
|
||||
auto texture_loader = lt::TextureLoaderFactory::create(in_path.extension().string());
|
||||
if (!texture_loader)
|
||||
{
|
||||
if (p.path().extension() == ".png")
|
||||
{
|
||||
log("Found a texture: ", p);
|
||||
|
||||
auto newp = p.path();
|
||||
newp.replace_extension(".asset_texture");
|
||||
convert_image(p.path(), newp);
|
||||
}
|
||||
else if (p.path().extension() == ".obj")
|
||||
{
|
||||
log("Found a mesh -> ", p, " (unsupported)");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Unknown -> ", p);
|
||||
}
|
||||
// Don't log anything; this is expected.
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
try
|
||||
{
|
||||
Assets::TextureAsset::pack(texture_loader->load(in_path), out_path);
|
||||
|
||||
log_inf("Packed a texture:");
|
||||
log_inf("\tloader : {}", texture_loader->get_name());
|
||||
log_inf("\tin path: {}", in_path.string());
|
||||
log_inf("\tout path: {}", out_path.string());
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_err("Failed to pack texture:");
|
||||
log_err("\tloader : {}", texture_loader->get_name());
|
||||
log_err("\tin path : {}", in_path.string());
|
||||
log_err("\tout path: {}", out_path.string());
|
||||
log_err("\texp.what: {}", exp.what());
|
||||
}
|
||||
}
|
||||
|
||||
auto main(int argc, char *argv[]) -> int32_t
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
throw std::logic_error("Argc should be 2 -- exe dir (implicit) and target dir");
|
||||
}
|
||||
|
||||
for (const auto &directory_iterator :
|
||||
std::filesystem::recursive_directory_iterator(argv[1])) // NOLINT
|
||||
{
|
||||
if (directory_iterator.is_directory())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &in_path = directory_iterator.path();
|
||||
|
||||
auto out_path = in_path;
|
||||
out_path.replace_extension(".asset");
|
||||
|
||||
try_packing_texture(in_path, out_path);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_crt("Terminating due to uncaught exception:");
|
||||
log_crt("\texception.what: {}:", exp.what());
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
@ -7,4 +7,5 @@ target_link_libraries(
|
|||
asset_parser
|
||||
PRIVATE LZ4::lz4_static
|
||||
PRIVATE nlohmann_json::nlohmann_json
|
||||
PRIVATE logger
|
||||
)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <asset_parser/parser.hpp>
|
||||
|
||||
namespace Assets {
|
||||
|
||||
enum class TextureFormat : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
RGBA8,
|
||||
};
|
||||
|
||||
struct TextureInfo
|
||||
{
|
||||
size_t size;
|
||||
CompressionMode compression_mode;
|
||||
TextureFormat format;
|
||||
std::array<uint32_t, 3> pixel_size;
|
||||
std::string original_file;
|
||||
};
|
||||
|
||||
auto read_texture_info(AssetFile *file) -> TextureInfo;
|
||||
|
||||
void unpack_texture(
|
||||
TextureInfo *info,
|
||||
const void *source_buffer,
|
||||
size_t source_size,
|
||||
void *destination
|
||||
);
|
||||
|
||||
auto pack_texture(TextureInfo *info, void *pixel_data) -> AssetFile;
|
||||
|
||||
} // namespace Assets
|
|
@ -1,34 +1,276 @@
|
|||
#pragma once
|
||||
|
||||
#include <compressors/compressors.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <logger/logger.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Assets {
|
||||
|
||||
struct AssetFile
|
||||
{
|
||||
uint32_t version;
|
||||
constexpr auto current_version = uint32_t { 1 };
|
||||
|
||||
struct BlobMetadata
|
||||
{
|
||||
enum class Tag : uint8_t
|
||||
{
|
||||
color,
|
||||
depth,
|
||||
vertices,
|
||||
indices,
|
||||
};
|
||||
|
||||
Tag tag;
|
||||
|
||||
size_t offset;
|
||||
|
||||
CompressionType compression_type;
|
||||
|
||||
size_t compressed_size;
|
||||
|
||||
size_t uncompressed_size;
|
||||
};
|
||||
|
||||
|
||||
using Blob = std::vector<std::byte>;
|
||||
|
||||
class Asset
|
||||
{
|
||||
public:
|
||||
enum class Type : uint32_t // NOLINT(performance-enum-size)
|
||||
{
|
||||
Texture,
|
||||
Mesh,
|
||||
Material,
|
||||
} type;
|
||||
};
|
||||
|
||||
std::string json;
|
||||
std::vector<uint8_t> blob;
|
||||
struct Metadata
|
||||
{
|
||||
Type type;
|
||||
};
|
||||
|
||||
Asset() = default;
|
||||
|
||||
Asset(Metadata metadata, std::filesystem::path path, std::ifstream stream)
|
||||
: m_metadata(metadata)
|
||||
, m_file_path(std::move(path))
|
||||
, m_stream(std::move(stream))
|
||||
{
|
||||
}
|
||||
|
||||
/** Directly unpacks from disk to the destination.
|
||||
*
|
||||
* @note The destination MUST have at least blob_metadata.unpacked_size bytes available for
|
||||
* writing, otherwise segfault could occur!
|
||||
*/
|
||||
void unpack_blob(BlobMetadata::Tag blob_tag, std::byte *destination);
|
||||
|
||||
[[nodiscard]] auto get_metadata() const -> const Metadata &
|
||||
{
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_file_path() const -> std::filesystem::path
|
||||
{
|
||||
return m_file_path;
|
||||
}
|
||||
|
||||
private:
|
||||
Metadata m_metadata;
|
||||
|
||||
std::filesystem::path m_file_path;
|
||||
|
||||
std::ifstream m_stream;
|
||||
};
|
||||
|
||||
enum class CompressionMode : uint32_t // NOLINT(performance-enum-size)
|
||||
class TextureAsset: public Asset
|
||||
{
|
||||
None,
|
||||
LZ4,
|
||||
LZ4HC,
|
||||
};
|
||||
public:
|
||||
enum class Format : uint32_t // NOLINT(performance-enum-size)
|
||||
{
|
||||
None = 0,
|
||||
RGBA8,
|
||||
};
|
||||
|
||||
auto save_binary_file(const char *path, const AssetFile &in_file) -> bool;
|
||||
auto load_binary_file(const char *path, AssetFile &out_file) -> bool;
|
||||
struct Metadata
|
||||
{
|
||||
Format format;
|
||||
|
||||
uint32_t num_components;
|
||||
|
||||
std::array<uint32_t, 3> pixel_size;
|
||||
};
|
||||
|
||||
/** Data required to pack a texture */
|
||||
struct PackageData
|
||||
{
|
||||
Asset::Metadata metadata;
|
||||
|
||||
Metadata texture_metadata;
|
||||
|
||||
Blob pixels;
|
||||
};
|
||||
|
||||
TextureAsset(const std::filesystem::path &path)
|
||||
{
|
||||
m_stream = std::ifstream { path, std::ios::binary };
|
||||
if (!m_stream.is_open())
|
||||
{
|
||||
throw std::runtime_error { std::format(
|
||||
"Failed to open ifm_stream for loading texture asset at: {}",
|
||||
path.string()
|
||||
) };
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
m_stream.read((char *)&version, sizeof(version));
|
||||
m_stream.read((char *)&m_asset_metadata, sizeof(m_asset_metadata));
|
||||
m_stream.read((char *)&m_metadata, sizeof(m_metadata));
|
||||
|
||||
auto num_blobs = uint32_t {};
|
||||
m_stream.read((char *)&num_blobs, sizeof(num_blobs));
|
||||
if (num_blobs != 1)
|
||||
{
|
||||
throw std::runtime_error {
|
||||
std::format("Failed to load texture asset: invalid number of blobs: {}", num_blobs)
|
||||
};
|
||||
}
|
||||
|
||||
m_stream.read((char *)&m_pixel_blob_metadata, sizeof(m_pixel_blob_metadata));
|
||||
if (m_pixel_blob_metadata.tag != BlobMetadata::Tag::color)
|
||||
{
|
||||
throw std::runtime_error {
|
||||
std::format(
|
||||
"Failed to load texture asset: invalid blob tag, expected {}, got {}",
|
||||
std::to_underlying(BlobMetadata::Tag::color),
|
||||
std::to_underlying(m_pixel_blob_metadata.tag)
|
||||
),
|
||||
};
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
}
|
||||
|
||||
void unpack_blob(BlobMetadata::Tag tag, std::byte *destination, size_t destination_capacity)
|
||||
{
|
||||
if (tag != BlobMetadata::Tag::color)
|
||||
{
|
||||
throw std::runtime_error { std::format(
|
||||
"Invalid tag for unpack_blob of TextureAsset: {}",
|
||||
std::to_underlying(tag)
|
||||
) };
|
||||
}
|
||||
|
||||
m_stream.seekg(static_cast<long>(m_pixel_blob_metadata.offset));
|
||||
switch (m_pixel_blob_metadata.compression_type)
|
||||
{
|
||||
case Assets::CompressionType::None:
|
||||
if (m_pixel_blob_metadata.uncompressed_size != m_pixel_blob_metadata.compressed_size)
|
||||
{
|
||||
throw std::runtime_error("Failed to unpack blob from TextureAsset: "
|
||||
"compressed/uncompressed size mismatch for no compression "
|
||||
"type");
|
||||
}
|
||||
|
||||
if (m_pixel_blob_metadata.uncompressed_size > destination_capacity)
|
||||
{
|
||||
throw std::runtime_error("Failed to unpack blob from TextureAsset: "
|
||||
"uncompressed_size > destination_capacity, unpacking "
|
||||
"would result in segfault");
|
||||
}
|
||||
|
||||
if (!m_stream.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to unpack blob from TextureAsset: ifstream is "
|
||||
"closed");
|
||||
}
|
||||
|
||||
m_stream.read(
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
(char *)destination,
|
||||
static_cast<long>(m_pixel_blob_metadata.uncompressed_size)
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(std::format(
|
||||
"Failed to unpack blob from TextureAsset: unsupported "
|
||||
"compression type: {}",
|
||||
std::to_underlying(m_pixel_blob_metadata.compression_type)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
static void pack(const PackageData &data, const std::filesystem::path &out_path)
|
||||
{
|
||||
const auto &[metadata, texture_metadata, pixels] = data;
|
||||
|
||||
auto stream = std::ofstream { out_path, std::ios::binary | std::ios::trunc };
|
||||
if (!stream.is_open())
|
||||
{
|
||||
throw std::runtime_error {
|
||||
std::format("Failed to open ofstream for packing texture at: {}", out_path.string())
|
||||
};
|
||||
}
|
||||
stream.seekp(0);
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
stream.write((char *)¤t_version, sizeof(current_version));
|
||||
|
||||
stream.write((char *)&metadata, sizeof(metadata));
|
||||
stream.write((char *)&texture_metadata, sizeof(texture_metadata));
|
||||
|
||||
constexpr auto number_of_blobs = uint32_t { 1 };
|
||||
stream.write((char *)&number_of_blobs, sizeof(number_of_blobs));
|
||||
|
||||
auto pixels_metadata = BlobMetadata {
|
||||
.tag = BlobMetadata::Tag::color,
|
||||
.offset = static_cast<size_t>(stream.tellp()),
|
||||
.compression_type = CompressionType::None,
|
||||
.compressed_size = pixels.size(),
|
||||
.uncompressed_size = pixels.size(),
|
||||
};
|
||||
|
||||
stream.write((char *)&pixels_metadata, sizeof(pixels_metadata));
|
||||
stream.write((char *)&pixels[0], static_cast<long>(pixels.size()));
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_asset_metadata() const -> const Asset::Metadata &
|
||||
{
|
||||
return m_asset_metadata;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_metadata() const -> const Metadata &
|
||||
{
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_blob_metadata(BlobMetadata::Tag tag) const -> const BlobMetadata &
|
||||
{
|
||||
if (tag != BlobMetadata::Tag::color)
|
||||
{
|
||||
throw std::runtime_error { std::format(
|
||||
"Invalid tag for get_blob_metadata of TextureAsset: {}",
|
||||
std::to_underlying(tag)
|
||||
) };
|
||||
}
|
||||
|
||||
return m_pixel_blob_metadata;
|
||||
}
|
||||
|
||||
private:
|
||||
Asset::Metadata m_asset_metadata {};
|
||||
|
||||
Metadata m_metadata {};
|
||||
|
||||
BlobMetadata m_pixel_blob_metadata {};
|
||||
|
||||
uint32_t version {};
|
||||
|
||||
std::ifstream m_stream;
|
||||
};
|
||||
|
||||
} // namespace Assets
|
||||
|
|
14
modules/asset_parser/include/compressors/compressors.hpp
Normal file
14
modules/asset_parser/include/compressors/compressors.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Assets {
|
||||
|
||||
enum class CompressionType : uint32_t // NOLINT(performance-enum-size)
|
||||
{
|
||||
None,
|
||||
LZ4,
|
||||
LZ4HC,
|
||||
};
|
||||
|
||||
}
|
|
@ -2,79 +2,11 @@
|
|||
#include <lz4.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
// [version], [type] --
|
||||
// [asset_metadata], [blob_metadata] --
|
||||
// [blob] --
|
||||
|
||||
namespace Assets {
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
auto read_texture_info(AssetFile *file) -> TextureInfo
|
||||
{
|
||||
json texture_meta_data = json::parse(file->json);
|
||||
|
||||
return TextureInfo {
|
||||
.size = texture_meta_data["bufferSize"],
|
||||
.compression_mode = texture_meta_data["compression"],
|
||||
.format = texture_meta_data["format"],
|
||||
.pixel_size = {
|
||||
texture_meta_data["width"],
|
||||
texture_meta_data["height"],
|
||||
0,
|
||||
},
|
||||
.original_file = texture_meta_data["originalFile"],
|
||||
};
|
||||
}
|
||||
|
||||
void unpack_texture(
|
||||
TextureInfo *info,
|
||||
const void *source_buffer,
|
||||
size_t source_size,
|
||||
void *destination
|
||||
)
|
||||
{
|
||||
if (info->compression_mode == CompressionMode::LZ4)
|
||||
{
|
||||
LZ4_decompress_safe(
|
||||
(const char *)source_buffer,
|
||||
(char *)destination,
|
||||
source_size,
|
||||
info->size
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(destination, source_buffer, source_size);
|
||||
}
|
||||
}
|
||||
|
||||
auto pack_texture(TextureInfo *info, void *pixel_data) -> AssetFile
|
||||
{
|
||||
json metadata;
|
||||
metadata["format"] = info->format;
|
||||
metadata["width"] = info->pixel_size[0];
|
||||
metadata["height"] = info->pixel_size[1];
|
||||
metadata["bufferSize"] = info->size;
|
||||
metadata["originalFile"] = info->original_file;
|
||||
metadata["compression"] = CompressionMode::LZ4;
|
||||
|
||||
AssetFile file;
|
||||
file.type = AssetFile::Type::Texture;
|
||||
file.version = 1u;
|
||||
|
||||
const int compress_staging = LZ4_compressBound(info->size);
|
||||
file.blob.resize(compress_staging);
|
||||
const int compression_size = LZ4_compress_default(
|
||||
(const char *)pixel_data,
|
||||
(char *)file.blob.data(),
|
||||
info->size,
|
||||
compress_staging
|
||||
);
|
||||
file.blob.resize(compression_size);
|
||||
|
||||
|
||||
metadata["compression"] = CompressionMode::LZ4;
|
||||
|
||||
file.json = metadata.dump();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
} // namespace Assets
|
||||
|
|
|
@ -1,56 +1,62 @@
|
|||
#include <asset_parser/parser.hpp>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
namespace Assets {
|
||||
|
||||
auto save_binary_file(const char *path, const AssetFile &file) -> bool
|
||||
{
|
||||
std::ofstream outstream(path, std::ios::binary | std::ios::out);
|
||||
|
||||
outstream.write((const char *)&file.version, sizeof(uint32_t));
|
||||
outstream.write((const char *)&file.type, sizeof(AssetFile::Type));
|
||||
|
||||
uint32_t json_size = file.json.size();
|
||||
uint32_t blob_size = file.blob.size();
|
||||
outstream.write((const char *)&json_size, sizeof(uint32_t));
|
||||
outstream.write((const char *)&blob_size, sizeof(uint32_t));
|
||||
|
||||
outstream.write(file.json.c_str(), json_size);
|
||||
outstream.write((const char *)file.blob.data(), blob_size);
|
||||
|
||||
outstream.close();
|
||||
|
||||
outstream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto load_binary_file(const char *path, AssetFile &out_file) -> bool
|
||||
{
|
||||
std::ifstream instream(path, std::ios::binary);
|
||||
instream.seekg(0ull);
|
||||
|
||||
if (!instream.is_open())
|
||||
return false;
|
||||
|
||||
instream.read((char *)&out_file.version, sizeof(uint32_t));
|
||||
instream.read((char *)&out_file.type, sizeof(AssetFile::Type));
|
||||
|
||||
uint32_t json_size;
|
||||
uint32_t blob_size;
|
||||
instream.read((char *)&json_size, sizeof(uint32_t));
|
||||
instream.read((char *)&blob_size, sizeof(uint32_t));
|
||||
|
||||
out_file.json.resize(json_size);
|
||||
out_file.blob.resize(blob_size);
|
||||
instream.read((char *)out_file.json.data(), json_size);
|
||||
instream.read((char *)out_file.blob.data(), blob_size);
|
||||
|
||||
instream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
// void Asset::unpack(std::byte *destination)
|
||||
// {
|
||||
// if (!m_stream.is_open())
|
||||
// {
|
||||
// throw std::logic_error {
|
||||
// "Failed to unpack asset: "
|
||||
// "ifstream is closed",
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// switch (m_metadata.blob_compression_type)
|
||||
// {
|
||||
// case CompressionType::None:
|
||||
// if (m_metadata.packed_size != m_metadata.unpacked_size)
|
||||
// {
|
||||
// throw std::logic_error {
|
||||
// "Failed to unpack asset: "
|
||||
// "compression type set to none but packed/unpacked sizes differ",
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// m_stream.read(
|
||||
// std::bit_cast<char *>(destination),
|
||||
// static_cast<long>(m_metadata.packed_size)
|
||||
// );
|
||||
// m_stream.close();
|
||||
//
|
||||
// case CompressionType::LZ4:
|
||||
// m_stream.close();
|
||||
// throw std::logic_error {
|
||||
// "Failed to unpack asset: "
|
||||
// "LZ4 compression is not implemented yet",
|
||||
// };
|
||||
//
|
||||
//
|
||||
// case CompressionType::LZ4HC:
|
||||
// m_stream.close();
|
||||
// throw std::logic_error {
|
||||
// "Failed to unpack asset: "
|
||||
// "LZ4HC compression is not implemented yet",
|
||||
// };
|
||||
//
|
||||
// default:
|
||||
// m_stream.close();
|
||||
// throw std::logic_error {
|
||||
// std::format(
|
||||
// "Failed to unpack asset: "
|
||||
// "Compression type was not recognized: {}",
|
||||
// std::to_underlying(m_metadata.blob_compression_type)
|
||||
// ),
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
} // namespace Assets
|
||||
|
|
|
@ -104,7 +104,7 @@ target_link_libraries(
|
|||
PUBLIC opengl::opengl
|
||||
PUBLIC glfw
|
||||
PUBLIC imgui
|
||||
PUBLIC stb::stb
|
||||
PUBLIC asset_parser
|
||||
PUBLIC yaml-cpp::yaml-cpp
|
||||
PUBLIC EnTT::EnTT
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Light {
|
|||
class BasicFileHandle
|
||||
{
|
||||
public:
|
||||
virtual ~BasicFileHandle() = default;
|
||||
virtual ~BasicFileHandle() = default;
|
||||
BasicFileHandle(
|
||||
uint8_t *data = nullptr,
|
||||
uint32_t size = 0ull,
|
||||
|
@ -58,13 +58,12 @@ virtual ~BasicFileHandle() = default;
|
|||
return is_valid();
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
// made protected for custom free():
|
||||
uint8_t *m_data;
|
||||
|
||||
uint32_t m_size;
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
|
||||
const std::string m_name;
|
||||
|
@ -72,68 +71,10 @@ private:
|
|||
const std::string m_extension;
|
||||
};
|
||||
|
||||
class ImageFileHandle: public BasicFileHandle
|
||||
{
|
||||
public:
|
||||
virtual ~ImageFileHandle() = default;
|
||||
ImageFileHandle(
|
||||
uint8_t *data,
|
||||
uint32_t size,
|
||||
const std::string &path,
|
||||
const std::string &name,
|
||||
const std::string &extension,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t components,
|
||||
uint32_t desiredComponents
|
||||
)
|
||||
: BasicFileHandle(data, size, path, name, extension)
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
, m_components(components)
|
||||
, m_desired_components(desiredComponents)
|
||||
{
|
||||
}
|
||||
|
||||
void release() override;
|
||||
|
||||
[[nodiscard]] auto get_width() const -> uint32_t
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_height() const -> uint32_t
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_components() const -> uint32_t
|
||||
{
|
||||
return m_components;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_desired_components() const -> uint32_t
|
||||
{
|
||||
return m_desired_components;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_width;
|
||||
|
||||
uint32_t m_height;
|
||||
|
||||
uint32_t m_components;
|
||||
|
||||
uint32_t m_desired_components;
|
||||
};
|
||||
|
||||
class FileManager
|
||||
{
|
||||
public:
|
||||
static auto read_text_file(const std::string &path) -> BasicFileHandle;
|
||||
|
||||
static auto read_image_file(const std::string &path, int32_t desiredComponents)
|
||||
-> ImageFileHandle;
|
||||
};
|
||||
|
||||
} // namespace Light
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <engine/base/base.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
namespace Light {
|
||||
|
||||
|
@ -26,13 +27,9 @@ public:
|
|||
instance().load_shader_impl(name, vertexPath, pixelPath);
|
||||
}
|
||||
|
||||
static void load_texture(
|
||||
const std::string &name,
|
||||
const std::string &path,
|
||||
unsigned int desiredComponents = 4u
|
||||
)
|
||||
static void load_texture(const std::string &name, const std::string &path)
|
||||
{
|
||||
instance().load_texture_impl(name, path, desiredComponents);
|
||||
instance().load_texture_impl(name, path);
|
||||
}
|
||||
|
||||
static void release_texture(const std::string &name)
|
||||
|
@ -59,11 +56,7 @@ private:
|
|||
const std::string &pixelPath
|
||||
);
|
||||
|
||||
void load_texture_impl(
|
||||
const std::string &name,
|
||||
const std::string &path,
|
||||
unsigned int desiredComponents = 4u
|
||||
);
|
||||
void load_texture_impl(const std::string &name, const std::filesystem::path &path);
|
||||
|
||||
void release_texture_impl(const std::string &name);
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <engine/utils/file_manager.hpp>
|
||||
#include <utility>
|
||||
#include <stb_image.h>
|
||||
|
||||
namespace Light {
|
||||
|
||||
|
@ -27,7 +25,6 @@ void BasicFileHandle::release()
|
|||
m_size = 0ull;
|
||||
}
|
||||
|
||||
|
||||
auto FileManager::read_text_file(const std::string &path) -> BasicFileHandle
|
||||
{
|
||||
// parse path info
|
||||
|
@ -63,52 +60,52 @@ auto FileManager::read_text_file(const std::string &path) -> BasicFileHandle
|
|||
return { data, static_cast<unsigned int>(size), path, name, extension };
|
||||
}
|
||||
|
||||
auto FileManager::read_image_file(const std::string &path, int32_t desiredComponents)
|
||||
-> ImageFileHandle
|
||||
{
|
||||
// parse path info
|
||||
auto name = path.substr(0, path.find('.') + -1);
|
||||
auto extension = path.substr(path.find('.') + 1);
|
||||
// auto FileManager::read_image_file(const std::string &path, int32_t desiredComponents)
|
||||
// -> ImageFileHandle
|
||||
// {
|
||||
// // parse path info
|
||||
// auto name = path.substr(0, path.find('.') + -1);
|
||||
// auto extension = path.substr(path.find('.') + 1);
|
||||
//
|
||||
// // load image
|
||||
// auto width = 0;
|
||||
// auto height = 0;
|
||||
// auto fetchedComponents = 0;
|
||||
// auto *pixels = stbi_load(path.c_str(), &width, &height, &fetchedComponents, desiredComponents);
|
||||
//
|
||||
// // check
|
||||
// if (!pixels)
|
||||
// {
|
||||
// log_wrn("Failed to load image file: <{}>", path);
|
||||
// }
|
||||
// else if (fetchedComponents != desiredComponents)
|
||||
// {
|
||||
// log_wrn(
|
||||
// "Mismatch of fetched/desired components: <{}> ({}/{})",
|
||||
// name + '.' + extension,
|
||||
// fetchedComponents,
|
||||
// desiredComponents
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// return ImageFileHandle(
|
||||
// pixels,
|
||||
// width * height,
|
||||
// path,
|
||||
// name,
|
||||
// extension,
|
||||
// width,
|
||||
// height,
|
||||
// fetchedComponents,
|
||||
// desiredComponents
|
||||
// );
|
||||
// }
|
||||
|
||||
// load image
|
||||
auto width = 0;
|
||||
auto height = 0;
|
||||
auto fetchedComponents = 0;
|
||||
auto *pixels = stbi_load(path.c_str(), &width, &height, &fetchedComponents, desiredComponents);
|
||||
|
||||
// check
|
||||
if (!pixels)
|
||||
{
|
||||
log_wrn("Failed to load image file: <{}>", path);
|
||||
}
|
||||
else if (fetchedComponents != desiredComponents)
|
||||
{
|
||||
log_wrn(
|
||||
"Mismatch of fetched/desired components: <{}> ({}/{})",
|
||||
name + '.' + extension,
|
||||
fetchedComponents,
|
||||
desiredComponents
|
||||
);
|
||||
}
|
||||
|
||||
return ImageFileHandle(
|
||||
pixels,
|
||||
width * height,
|
||||
path,
|
||||
name,
|
||||
extension,
|
||||
width,
|
||||
height,
|
||||
fetchedComponents,
|
||||
desiredComponents
|
||||
);
|
||||
}
|
||||
|
||||
void ImageFileHandle::release()
|
||||
{
|
||||
stbi_image_free(reinterpret_cast<void *>(m_data));
|
||||
m_data = nullptr;
|
||||
m_size = 0ull;
|
||||
}
|
||||
// void ImageFileHandle::release()
|
||||
// {
|
||||
// stbi_image_free(reinterpret_cast<void *>(m_data));
|
||||
// m_data = nullptr;
|
||||
// m_size = 0ull;
|
||||
// }
|
||||
|
||||
} // namespace Light
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <asset_parser/assets/texture.hpp>
|
||||
#include <asset_parser/parser.hpp>
|
||||
#include <engine/graphics/graphics_context.hpp>
|
||||
#include <engine/graphics/shader.hpp>
|
||||
#include <engine/graphics/texture.hpp>
|
||||
|
@ -12,49 +14,44 @@ void ResourceManager::load_shader_impl(
|
|||
const std::string &pixelPath
|
||||
)
|
||||
{
|
||||
// check
|
||||
lt_assert(!vertexPath.empty(), "Empty 'vertexPath'");
|
||||
lt_assert(!pixelPath.empty(), "Empty 'pixelPath'");
|
||||
|
||||
// load files
|
||||
auto vertexFile = FileManager::read_text_file(vertexPath);
|
||||
auto pixelFile = FileManager::read_text_file(pixelPath);
|
||||
|
||||
// check
|
||||
lt_assert(vertexFile.is_valid(), "Failed to read vertex file: {}", vertexPath);
|
||||
lt_assert(pixelFile.is_valid(), "Failed to read vertex file: {}", pixelPath);
|
||||
|
||||
// create shader
|
||||
m_shaders[name] = Ref<Shader>(
|
||||
Shader::create(vertexFile, pixelFile, GraphicsContext::get_shared_context())
|
||||
);
|
||||
|
||||
// free file
|
||||
vertexFile.release();
|
||||
pixelFile.release();
|
||||
}
|
||||
|
||||
void ResourceManager::load_texture_impl(
|
||||
const std::string &name,
|
||||
const std::string &path,
|
||||
unsigned int desiredComponents /* = 4u */
|
||||
)
|
||||
void ResourceManager::load_texture_impl(const std::string &name, const std::filesystem::path &path)
|
||||
{
|
||||
// load file
|
||||
auto imgFile = FileManager::read_image_file(path, desiredComponents);
|
||||
log_trc("Loading texture:");
|
||||
log_trc("\tname: {}", name);
|
||||
log_trc("\tpath: {}", path.string());
|
||||
|
||||
auto asset = Assets::TextureAsset { path };
|
||||
const auto metadata = asset.get_metadata();
|
||||
const auto blob_metadata = asset.get_blob_metadata(Assets::BlobMetadata::Tag::color);
|
||||
|
||||
auto blob = std::vector<std::byte>(blob_metadata.uncompressed_size);
|
||||
asset.unpack_blob(blob_metadata.tag, blob.data(), blob.size());
|
||||
|
||||
// create texture
|
||||
m_textures[name] = Ref<Texture>(Texture::create(
|
||||
imgFile.get_width(),
|
||||
imgFile.get_height(),
|
||||
imgFile.get_components(),
|
||||
imgFile.get_data(),
|
||||
metadata.pixel_size[0],
|
||||
metadata.pixel_size[1],
|
||||
metadata.num_components,
|
||||
std::bit_cast<unsigned char *>(blob.data()),
|
||||
GraphicsContext::get_shared_context(),
|
||||
path
|
||||
));
|
||||
|
||||
// free file
|
||||
imgFile.release();
|
||||
}
|
||||
|
||||
void ResourceManager::release_texture_impl(const std::string &name)
|
||||
|
|
|
@ -28,7 +28,7 @@ EditorLayer::EditorLayer(const std::string &name)
|
|||
m_camera_entity = m_scene->create_entity("Camera");
|
||||
m_camera_entity.add_component<CameraComponent>(SceneCamera(), true);
|
||||
|
||||
ResourceManager::load_texture("Awesomeface", "data/assets/textures/awesomeface.png");
|
||||
ResourceManager::load_texture("Awesomeface", "data/assets/textures/awesomeface.asset");
|
||||
|
||||
auto entity = Entity { m_scene->create_entity("Awesomeface", {}) };
|
||||
entity.add_component<SpriteRendererComponent>(
|
||||
|
|
|
@ -10,10 +10,10 @@ AssetBrowserPanel::AssetBrowserPanel(Ref<Scene> active_scene)
|
|||
, m_assets_path("./data/assets")
|
||||
, m_active_scene(std::move(active_scene))
|
||||
{
|
||||
ResourceManager::load_texture("_Assets_Directory", "data/engine/icons/asset/dir.png");
|
||||
ResourceManager::load_texture("_Assets_Scene", "data/engine/icons/asset/scene.png");
|
||||
ResourceManager::load_texture("_Assets_Image", "data/engine/icons/asset/img.png");
|
||||
ResourceManager::load_texture("_Assets_Text", "data/engine/icons/asset/txt.png");
|
||||
ResourceManager::load_texture("_Assets_Directory", "data/engine/icons/asset/dir.asset");
|
||||
ResourceManager::load_texture("_Assets_Scene", "data/engine/icons/asset/scene.asset");
|
||||
ResourceManager::load_texture("_Assets_Image", "data/engine/icons/asset/img.asset");
|
||||
ResourceManager::load_texture("_Assets_Text", "data/engine/icons/asset/txt.asset");
|
||||
|
||||
m_directory_texture = ResourceManager::get_texture("_Assets_Directory");
|
||||
m_scene_texture = ResourceManager::get_texture("_Assets_Scene");
|
||||
|
|
Loading…
Add table
Reference in a new issue