asset compression

This commit is contained in:
Jorijn van der Graaf 2026-05-11 18:37:30 +02:00
commit 30a283c1b3
57 changed files with 13237 additions and 8 deletions

View file

@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
export module Crafter.Asset:Mesh;
import :Compression;
import Crafter.Math;
import std;
namespace fs = std::filesystem;
@ -35,6 +36,44 @@ export namespace Crafter {
Vector<float, 2, 0> uv;
};
// GDeflate-compressed counterpart of MeshAsset<T>::Save output. Three
// regions: [vertex, index, data]. dataCount==0 leaves the data region
// empty (zero compressedSize/decompressedSize). dataStride records sizeof(T)
// at compress time so consumers can validate.
struct CompressedMeshAsset {
std::uint32_t vertexCount = 0;
std::uint32_t indexCount = 0;
std::uint32_t dataCount = 0;
std::uint32_t dataStride = 0;
Compression::CompressedBlob blob;
};
namespace MeshAssetFormat {
inline constexpr char magic[4] = {'C', 'G', 'D', 'M'};
inline constexpr std::uint32_t version = 1;
}
inline CompressedMeshAsset LoadCompressedMesh(fs::path path) {
std::ifstream file(path, std::ios::binary);
char magic[4];
file.read(magic, 4);
if (std::memcmp(magic, MeshAssetFormat::magic, 4) != 0) {
throw std::runtime_error("LoadCompressedMesh: bad magic on " + path.string());
}
std::uint32_t version = 0;
file.read(reinterpret_cast<char*>(&version), sizeof(version));
if (version != MeshAssetFormat::version) {
throw std::runtime_error("LoadCompressedMesh: unsupported version on " + path.string());
}
CompressedMeshAsset out;
file.read(reinterpret_cast<char*>(&out.vertexCount), sizeof(out.vertexCount));
file.read(reinterpret_cast<char*>(&out.indexCount), sizeof(out.indexCount));
file.read(reinterpret_cast<char*>(&out.dataCount), sizeof(out.dataCount));
file.read(reinterpret_cast<char*>(&out.dataStride), sizeof(out.dataStride));
out.blob = Compression::ReadBlob(file);
return out;
}
template <typename T>
struct MeshAsset {
std::vector<Vector<float, 3, 3>> vertexes;
@ -57,6 +96,29 @@ export namespace Crafter {
file.write(reinterpret_cast<char*>(datas.data()), dataCount * sizeof(T));
}
void SaveCompressed(fs::path path) const {
std::array<std::span<const std::byte>, 3> streams = {
std::as_bytes(std::span(vertexes)),
std::as_bytes(std::span(indexes)),
std::as_bytes(std::span(datas)),
};
Compression::CompressedBlob blob = Compression::CompressStreams(streams);
std::ofstream file(path, std::ios::binary);
file.write(MeshAssetFormat::magic, 4);
std::uint32_t version = MeshAssetFormat::version;
std::uint32_t vc = static_cast<std::uint32_t>(vertexes.size());
std::uint32_t ic = static_cast<std::uint32_t>(indexes.size());
std::uint32_t dc = static_cast<std::uint32_t>(datas.size());
std::uint32_t stride = static_cast<std::uint32_t>(sizeof(T));
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
file.write(reinterpret_cast<const char*>(&vc), sizeof(vc));
file.write(reinterpret_cast<const char*>(&ic), sizeof(ic));
file.write(reinterpret_cast<const char*>(&dc), sizeof(dc));
file.write(reinterpret_cast<const char*>(&stride), sizeof(stride));
Compression::WriteBlob(file, blob);
}
static MeshAsset<T> Load(fs::path path) {
MeshAsset<T> mesh;
@ -196,6 +258,32 @@ export namespace Crafter {
file.write(reinterpret_cast<char*>(vertexes.data()), vertexCount * sizeof(Vector<float, 3, 3>));
file.write(reinterpret_cast<char*>(indexes.data()), indexCount * sizeof(std::uint32_t));
}
void SaveCompressed(fs::path path) const {
// Three regions to keep file format identical to the templated
// variant; the data region is empty (skipped on the GPU path).
std::array<std::span<const std::byte>, 3> streams = {
std::as_bytes(std::span(vertexes)),
std::as_bytes(std::span(indexes)),
std::span<const std::byte>{},
};
Compression::CompressedBlob blob = Compression::CompressStreams(streams);
std::ofstream file(path, std::ios::binary);
file.write(MeshAssetFormat::magic, 4);
std::uint32_t version = MeshAssetFormat::version;
std::uint32_t vc = static_cast<std::uint32_t>(vertexes.size());
std::uint32_t ic = static_cast<std::uint32_t>(indexes.size());
std::uint32_t dc = 0;
std::uint32_t stride = 0;
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
file.write(reinterpret_cast<const char*>(&vc), sizeof(vc));
file.write(reinterpret_cast<const char*>(&ic), sizeof(ic));
file.write(reinterpret_cast<const char*>(&dc), sizeof(dc));
file.write(reinterpret_cast<const char*>(&stride), sizeof(stride));
Compression::WriteBlob(file, blob);
}
static MeshAsset<void> Load(fs::path path) {
MeshAsset<void> mesh;