This commit is contained in:
Jorijn van der Graaf 2026-02-19 03:00:13 +01:00
commit 057e7b94d7
10 changed files with 425 additions and 2323 deletions

View file

@ -1,163 +0,0 @@
/*
Crafter®.Asset
Copyright (C) 2025 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include <cstdint>
#include <string>
#include <vector>
#include <fstream>
#include <exception>
#include <filesystem>
#include <iostream>
#include <fstream>
module Crafter.Asset;
using namespace Crafter;
namespace fs = std::filesystem;
struct HeaderData {
std::uint32_t nameLenght;
std::uint32_t typeLenght;
std::uint32_t offset;
std::uint32_t lenght;
};
AssetLoad::AssetLoad(fs::path path) : f(path, std::ios::binary) {
if(!f) {
throw std::runtime_error(std::format("Error opening file {} not found", path.generic_string()));
}
std::uint32_t version;
f.read(reinterpret_cast<char*>(&version), sizeof(std::uint32_t));
std::uint32_t entryCount;
f.read(reinterpret_cast<char*>(&entryCount), sizeof(std::uint32_t));
entries.resize(entryCount);
std::vector<std::uint32_t> lenghts(entryCount);
for(std::uint32_t i = 0; i < entryCount; i++) {
HeaderData header;
f.read(reinterpret_cast<char*>(&header), sizeof(std::uint32_t)*4);
entries[i].name.resize(header.nameLenght);
f.read(reinterpret_cast<char*>(&entries[i].name[0]), header.nameLenght);
entries[i].type.resize(header.typeLenght);
f.read(reinterpret_cast<char*>(&entries[i].type[0]), header.typeLenght);
lenghts[i] = header.lenght;
entries[i].lenght = header.lenght;
entries[i].offset = header.offset;
headerLength+=header.nameLenght + header.typeLenght;
}
headerLength += sizeof(std::uint32_t)*2 + (sizeof(std::uint32_t)*4*entryCount);
}
std::vector<char> AssetLoad::Load(std::string_view name) {
auto it = std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.name == name;
});
return LoadOffset(it->offset, it->lenght);
}
std::vector<char> AssetLoad::Load(std::string_view name, uint32_t lenght) {
auto it = std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.name == name;
});
return LoadOffset(it->offset, lenght);
}
void AssetLoad::Load(std::string_view name, void* data) {
auto it = std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.name == name;
});
return LoadOffset(it->offset, it->lenght, data);
}
void AssetLoad::Load(std::string_view name, void* data, uint32_t lenght) {
auto it = std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.name == name;
});
return LoadOffset(it->offset, lenght, data);
}
std::vector<char> AssetLoad::Load(uint32_t index) {
return LoadOffset(entries[index].offset, entries[index].lenght);
}
std::vector<char> AssetLoad::Load(uint32_t index, uint32_t lenght) {
return LoadOffset(entries[index].offset, lenght);
}
void AssetLoad::Load(uint32_t index, void* data) {
return LoadOffset(entries[index].offset, entries[index].lenght, data);
}
void AssetLoad::Load(uint32_t index, void* data, uint32_t lenght) {
return LoadOffset(entries[index].offset, lenght, data);
}
std::vector<char> AssetLoad::Load(const AssetEntry& entry) {
return LoadOffset(entry.offset, entry.lenght);
}
std::vector<char> AssetLoad::Load(const AssetEntry& entry, uint32_t lenght) {
return LoadOffset(entry.offset, lenght);
}
void AssetLoad::Load(const AssetEntry& entry, void* data) {
return LoadOffset(entry.offset, entry.lenght, data);
}
void AssetLoad::Load(const AssetEntry& entry, void* data, uint32_t lenght) {
return LoadOffset(entry.offset, lenght, data);
}
std::vector<char> AssetLoad::LoadOffset(uint32_t offset, uint32_t lenght) {
f.seekg(offset+headerLength, std::ios_base::beg);
std::vector<char> vector(lenght);
f.read(vector.data(), lenght);
return vector;
}
void AssetLoad::LoadOffset(uint32_t offset, uint32_t lenght, void* data) {
f.seekg(offset+headerLength, std::ios_base::beg);
f.read(reinterpret_cast<char*>(data), lenght);
}
std::vector<char> AssetLoad::LoadAll() {
f.seekg(headerLength, std::ios_base::beg);
return std::vector<char>((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
}
AssetSave::AssetSave(std::span<AssetEntry const> entries, std::span<char const> buffer) : entries(entries.begin(), entries.end()), buffer(buffer.begin(), buffer.end()) {
}
void AssetSave::AddEntry(std::string_view name, std::string_view type, std::span<char const> data) {
entries.emplace_back(name, type, data.size());
buffer.insert(buffer.end(), data.begin(), data.end());
}
void AssetSave::Save(fs::path path) {
std::ofstream f(path, std::ios::binary);
std::uint32_t version = 0;
f.write(reinterpret_cast<const char*>(&version), sizeof(std::uint32_t));
std::uint32_t entriesCount = entries.size();
f.write(reinterpret_cast<const char*>(&entriesCount), sizeof(std::uint32_t));
std::uint32_t offset = 0;
for(const AssetEntry& entry : entries) {
std::uint32_t nameSize = entry.name.size();
f.write(reinterpret_cast<const char*>(&nameSize), sizeof(std::uint32_t));
std::uint32_t typeSize = entry.type.size();
f.write(reinterpret_cast<const char*>(&typeSize), sizeof(std::uint32_t));
f.write(reinterpret_cast<const char*>(&offset), sizeof(std::uint32_t));
f.write(reinterpret_cast<const char*>(&entry.lenght), sizeof(std::uint32_t));
offset+=entry.lenght;
f.write(reinterpret_cast<const char*>(&entry.name[0]), entry.name.size());
f.write(reinterpret_cast<const char*>(&entry.type[0]), entry.type.size());
}
f.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
}

View file

@ -1,244 +0,0 @@
/*
Crafter®.Asset
Copyright (C) 2025 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include <cstdint>
#include <string>
#include <vector>
#include <filesystem>
#include <unordered_map>
#include <iostream>
#include <functional>
#include <fstream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
export module Crafter.Asset:Asset;
namespace fs = std::filesystem;
struct __attribute__((packed)) Float3 {
float x;
float y;
float z;
};
struct __attribute__((packed)) Float2 {
float x;
float y;
};
struct __attribute__((packed)) VertexUV {
Float3 position;
Float2 uv;
};
void SplitString(const std::string& s, const char delimiter, std::vector<std::string>& tokens) {
std::istringstream iss(s);
std::string token;
while (std::getline(iss, token, delimiter)) {
tokens.push_back(token);
}
}
uint32_t ProcessVertex(std::vector<VertexUV>& verticies, std::vector<Float3> positions, std::vector<Float2> uvs, uint32_t posIndex, uint32_t uvIndex) {
Float3 pos = positions[posIndex - 1];
Float2 uv = uvs[uvIndex - 1];
for (uint32_t i = 0; i < verticies.size(); i++) {
VertexUV vert = verticies[i];
if (vert.position.x == pos.x && vert.position.y == pos.y && vert.position.z == pos.z && vert.uv.x == uv.x && vert.uv.y == uv.y) {
return i;
}
}
verticies.push_back({ pos ,uv });
return verticies.size() - 1;
}
export namespace Crafter {
std::unordered_map<std::string, std::function<std::vector<char>(fs::path)>> serializers = {
{
"PNG:Crafter.Graphics.TextureR8B8G8A8",
[](fs::path file) {
int x,y,n;
unsigned char *imageData = stbi_load(file.generic_string().c_str(),&x, &y, &n, 4);
std::uint32_t x2 = x;
std::uint32_t y2 = y;
std::vector<char> data(sizeof(std::uint32_t)+sizeof(std::uint32_t)+(x*y*4));
memcpy(&data[0], &x2, sizeof(std::uint32_t));
memcpy(&data[sizeof(std::uint32_t)], &y2, sizeof(std::uint32_t));
memcpy(&data[sizeof(std::uint32_t)*2], imageData, x*y*4);
return data;
}
},
{
"OBJ:Crafter.Graphics.Mesh",
[](fs::path path) {
std::ifstream file(path);
std::string line;
std::vector<Float3> positions;
std::vector<std::uint32_t> indices;
while (std::getline(file, line)) {
if (line.substr(0, 1) == "v") {
std::istringstream iss(line.substr(2));
Float3 pos{0,0,0};
iss >> pos.x >> pos.y >> pos.z;
positions.push_back(pos);
}
else if (line.substr(0, 1) == "f") {
std::vector<std::string> tokens;
SplitString(line.substr(2), ' ', tokens);
for (size_t i = 0; i < 3; ++i) {
indices.push_back(std::stoul(tokens[i])-1);
}
}
}
file.close();
std::vector<char> buffer((positions.size()*sizeof(Float3))+(indices.size()*sizeof(std::uint32_t))+sizeof(std::uint32_t)+sizeof(std::uint32_t));
std::uint32_t vertSize = positions.size();
std::uint32_t indexSize = indices.size();
memcpy(&buffer[0], &vertSize, sizeof(std::uint32_t));
memcpy(&buffer[sizeof(std::uint32_t)], &indexSize, sizeof(std::uint32_t));
memcpy(&buffer[sizeof(std::uint32_t)*2], positions.data(), positions.size()*sizeof(Float3));
memcpy(&buffer[positions.size()*sizeof(Float3)+sizeof(std::uint32_t)+sizeof(std::uint32_t)], indices.data(), indices.size()*sizeof(std::uint32_t));
return buffer;
}
},
{
"OBJ:Crafter.Graphics.MeshUV",
[](fs::path path) {
std::ifstream file(path);
std::string line;
std::vector<Float3> positions;
std::vector<Float2> uvs;
std::vector<VertexUV> verticies;
std::vector<uint32_t> indicies;
while (std::getline(file, line)) {
if (line.substr(0, 2) == "vt") {
std::istringstream iss(line.substr(3));
Float2 uv{0,0};
iss >> uv.x >> uv.y;
uvs.push_back(uv);
}
else if (line.substr(0, 1) == "v") {
std::istringstream iss(line.substr(2));
Float3 pos{0,0,0};
iss >> pos.x >> pos.y >> pos.z;
positions.push_back(pos);
}
else if (line.substr(0, 1) == "f") {
std::vector<std::string> tokens;
SplitString(line.substr(2), ' ', tokens);
for (size_t i = 0; i < 3; ++i) {
size_t slashPos = tokens[i].find('/');
indicies.push_back(ProcessVertex(verticies, positions, uvs, std::stoul(tokens[i].substr(0, slashPos)), std::stoul(tokens[i].substr(slashPos + 1))));
}
}
}
file.close();
std::vector<char> buffer((verticies.size()*sizeof(VertexUV))+(indicies.size()*sizeof(std::uint32_t))+sizeof(std::uint32_t)+sizeof(std::uint32_t));
std::uint32_t vertSize = verticies.size();
std::uint32_t indexSize = indicies.size();
memcpy(&buffer[0], &vertSize, sizeof(std::uint32_t));
memcpy(&buffer[sizeof(std::uint32_t)], &indexSize, sizeof(std::uint32_t));
memcpy(&buffer[sizeof(std::uint32_t)*2], verticies.data(), verticies.size()*sizeof(VertexUV));
memcpy(&buffer[verticies.size()*sizeof(VertexUV)+sizeof(std::uint32_t)+sizeof(std::uint32_t)], indicies.data(), indicies.size()*sizeof(std::uint32_t));
return buffer;
}
}
};
std::unordered_map<std::string, std::function<void(fs::path, const void*)>> extractors = {
{
"Crafter.Graphics.TextureR8B8G8A8:PNG",
[](fs::path dst, const void* data) {
stbi_write_png(dst.generic_string().c_str(), reinterpret_cast<const std::uint32_t*>(data)[0], reinterpret_cast<const std::uint32_t*>(data)[1], 4, reinterpret_cast<const char*>(data)+(sizeof(std::uint32_t)*2), reinterpret_cast<const std::uint32_t*>(data)[0]*4);
}
}
};
class AssetEntry {
public:
std::string name;
std::string type;
uint32_t offset;
uint32_t lenght;
AssetEntry() = default;
AssetEntry(std::string_view name, std::string_view type, uint32_t lenght) : name(name), type(type), lenght(lenght) {
}
};
class AssetLoad {
public:
std::vector<AssetEntry> entries;
AssetLoad(fs::path path);
AssetLoad() = default;
std::vector<char> Load(std::string_view name);
std::vector<char> Load(std::string_view name, uint32_t lenght);
void Load(std::string_view name, void* data);
void Load(std::string_view name, void* data, uint32_t lenght);
std::vector<char> Load(uint32_t index);
std::vector<char> Load(uint32_t index, uint32_t lenght);
void Load(uint32_t index, void* data);
void Load(uint32_t index, void* data, uint32_t lenght);
std::vector<char> Load(const AssetEntry& entry);
std::vector<char> Load(const AssetEntry& entry, uint32_t lenght);
void Load(const AssetEntry& entry, void* data);
void Load(const AssetEntry& entry, void* data, uint32_t lenght);
std::vector<char> LoadOffset(uint32_t offset, uint32_t lenght);
void LoadOffset(uint32_t offset, uint32_t lenght, void* data);
std::vector<char> LoadAll();
private:
uint32_t headerLength;
std::ifstream f;
};
class AssetSave {
public:
std::vector<AssetEntry> entries;
std::vector<char> buffer;
AssetSave() = default;
AssetSave(std::span<AssetEntry const> entries, std::span<char const> buffer);
void AddEntry(std::string_view name, std::string_view type, std::span<char const> data);
void Save(fs::path path);
};
}

63
implementations/main.cpp Normal file
View file

@ -0,0 +1,63 @@
/*
Crafter®.Asset
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import Crafter.Asset;
import Crafter.Math;
import std;
using namespace Crafter;
namespace fs = std::filesystem;
int main(int argc, char** argv) {
if (argc != 3) {
std::cout << "Usage: crafter-asset <input_file> <output_file>\n";
return 1;
}
fs::path inputPath = argv[1];
fs::path outputPath = argv[2];
if (!fs::exists(inputPath)) {
std::cerr << "Error: Input file does not exist.\n";
return 1;
}
std::string extension = inputPath.extension().string();
try {
if (extension == ".obj") {
// Load OBJ as MeshAsset with normal/tangent/uv data
auto mesh = MeshAsset<VertexNormalTangentUVPacked>::LoadOBJ(inputPath);
mesh.Save(outputPath);
}
else if (extension == ".png") {
// Load PNG as TextureAsset
auto texture = TextureAsset<Vector<std::uint8_t, 4, 0>>::LoadPNG(inputPath);
texture.Save(outputPath);
}
else {
std::cerr << "Unsupported file type: " << extension << "\n";
return 1;
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
return 0;
}

View file

@ -0,0 +1,244 @@
/*
Crafter®.Asset
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
export module Crafter.Asset:Mesh;
import Crafter.Math;
import std;
namespace fs = std::filesystem;
export namespace Crafter {
struct __attribute__((packed)) VertexNormalTangentUVPacked {
Vector<float, 3, 4> normal;
Vector<float, 3, 4> tangent;
Vector<float, 2, 4> uv;
};
struct VertexNormalTangentUV {
Vector<float, 3, 0> normal;
Vector<float, 3, 0> tangent;
Vector<float, 2, 0> uv;
};
template <typename T>
struct MeshAsset {
std::vector<Vector<float, 3, 3>> vertexes;
std::vector<std::uint32_t> indexes;
std::vector<T> datas;
void Save(fs::path path) {
std::ofstream file(path, std::ios::binary);
std::uint32_t vertexCount = vertexes.size();
std::uint32_t indexCount = indexes.size();
std::uint32_t dataCount = datas.size();
file.write(reinterpret_cast<char*>(&vertexCount), sizeof(vertexCount));
file.write(reinterpret_cast<char*>(&indexCount), sizeof(indexCount));
file.write(reinterpret_cast<char*>(&dataCount), sizeof(dataCount));
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));
file.write(reinterpret_cast<char*>(datas.data()), dataCount * sizeof(T));
}
static MeshAsset<T> Load(fs::path path) {
MeshAsset<T> mesh;
std::ifstream file(path, std::ios::binary);
std::uint32_t vertexCount = 0;
std::uint32_t indexCount = 0;
std::uint32_t dataCount = 0;
file.read(reinterpret_cast<char*>(&vertexCount), sizeof(vertexCount));
file.read(reinterpret_cast<char*>(&indexCount), sizeof(indexCount));
file.read(reinterpret_cast<char*>(&dataCount), sizeof(dataCount));
mesh.vertexes.resize(vertexCount);
mesh.indexes.resize(indexCount);
mesh.datas.resize(dataCount);
file.read(reinterpret_cast<char*>(mesh.vertexes.data()), vertexCount * sizeof(Vector<float, 3, 3>));
file.read(reinterpret_cast<char*>(mesh.indexes.data()), indexCount * sizeof(std::uint32_t));
file.read(reinterpret_cast<char*>(mesh.datas.data()), dataCount * sizeof(T));
return mesh;
}
static MeshAsset<T> LoadOBJ(fs::path path) requires (std::same_as<T, VertexNormalTangentUVPacked> || std::same_as<T, VertexNormalTangentUV>) {
std::ifstream file(path);
MeshAsset<T> mesh;
std::string line;
std::vector<Vector<float, 3, 0>> positions;
std::vector<Vector<float, 3, 0>> normals;
std::vector<Vector<float, 2, 0>> uvs;
while (std::getline(file, line)) {
if (line.substr(0, 2) == "vt") {
std::istringstream iss(line.substr(3));
Vector<float,2, 0> uv;
iss >> uv.x >> uv.y;
uvs.push_back(uv);
} else if (line.substr(0, 2) == "vn") {
std::istringstream iss(line.substr(3));
Vector<float, 3, 0> normal;
iss >> normal.x >> normal.y >> normal.z;
normals.push_back(normal);
} else if (line.substr(0, 1) == "v") {
std::istringstream iss(line.substr(2));
Vector<float,3, 3> position;
iss >> position.x >> position.y >> position.z;
positions.push_back(position);
} else if (line.substr(0, 1) == "f") {
std::istringstream iss(line.substr(2));
std::string vertexGroup;
while (iss >> vertexGroup) {
std::istringstream vss(vertexGroup);
std::string vIndex, vtIndex, vnIndex;
std::getline(vss, vIndex, '/');
std::getline(vss, vtIndex, '/');
std::getline(vss, vnIndex, '/');
Vector<float, 3, 0> v = positions[std::stoi(vIndex) - 1];
Vector<float, 2, 0> vt = uvs[std::stoi(vtIndex) - 1];
Vector<float, 3, 0> vn = normals[std::stoi(vnIndex) - 1];
for (std::uint32_t i = 0; i < mesh.datas.size(); i++) {
if (mesh.datas[i].normal == vn && mesh.datas[i].uv == vt && mesh.vertexes[i] == v) {
mesh.indexes.push_back(i);
goto skip;
}
}
mesh.indexes.push_back(mesh.datas.size());
mesh.datas.push_back({vn, {0,0,0}, vt});
mesh.vertexes.push_back(v);
skip:;
}
}
}
// Accumulate face normals and tangents
for (std::uint32_t i = 0; i < mesh.indexes.size(); i += 3)
{
std::uint32_t i0 = mesh.indexes[i];
std::uint32_t i1 = mesh.indexes[i + 1];
std::uint32_t i2 = mesh.indexes[i + 2];
// Edges of the triangle
Vector<float, 3, 0> edge1 = mesh.vertexes[i1] - mesh.vertexes[i0];
Vector<float, 3, 0> edge2 = mesh.vertexes[i2] - mesh.vertexes[i0];
// Texture coordinate deltas
Vector<float, 2, 0> deltaUV1 = mesh.datas[i1].uv - mesh.datas[i0].uv;
Vector<float, 2, 0> deltaUV2 = mesh.datas[i2].uv - mesh.datas[i0].uv;
// Tangent calculation
float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
Vector<float, 3, 0> tangent;
tangent.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
tangent.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
tangent.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);
// Normalize tangent
tangent.Normalize();
// Accumulate normals and tangents for each vertex
mesh.datas[i0].tangent += tangent;
mesh.datas[i1].tangent += tangent;
mesh.datas[i2].tangent += tangent;
}
// Normalize vertex normals and tangents
for (T& v : mesh.datas) {
v.tangent.Normalize();
}
return mesh;
}
};
template <>
struct MeshAsset<void> {
std::vector<Vector<float, 3, 3>> vertexes;
std::vector<std::uint32_t> indexes;
void Save(fs::path path) {
std::ofstream file(path, std::ios::binary);
std::uint32_t vertexCount = vertexes.size();
std::uint32_t indexCount = indexes.size();
file.write(reinterpret_cast<char*>(&vertexCount), sizeof(vertexCount));
file.write(reinterpret_cast<char*>(&indexCount), sizeof(indexCount));
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));
}
static MeshAsset<void> Load(fs::path path) {
MeshAsset<void> mesh;
std::ifstream file(path, std::ios::binary);
std::uint32_t vertexCount = 0;
std::uint32_t indexCount = 0;
file.read(reinterpret_cast<char*>(&vertexCount), sizeof(vertexCount));
file.read(reinterpret_cast<char*>(&indexCount), sizeof(indexCount));
mesh.vertexes.resize(vertexCount);
mesh.indexes.resize(indexCount);
file.read(reinterpret_cast<char*>(mesh.vertexes.data()), vertexCount * sizeof(Vector<float, 3, 3>));
file.read(reinterpret_cast<char*>(mesh.indexes.data()), indexCount * sizeof(std::uint32_t));
return mesh;
}
static MeshAsset<void> LoadOBJ(fs::path path) {
std::ifstream file(path);
MeshAsset<void> mesh;
std::string line;
while (std::getline(file, line)) {
if (line.rfind("v ", 0) == 0) {
std::istringstream iss(line.substr(2));
Vector<float,3, 3> position;
iss >> position.x >> position.y >> position.z;
mesh.vertexes.push_back(position);
} else if (line.rfind("f ", 0) == 0) {
std::istringstream iss(line.substr(2));
std::string vertexGroup;
while (iss >> vertexGroup) {
mesh.indexes.push_back(std::stoi(vertexGroup) - 1);
}
}
}
return mesh;
}
};
}

View file

@ -0,0 +1,81 @@
/*
Crafter®.Asset
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#define STB_IMAGE_IMPLEMENTATION
#include "../lib/stb_image.h"
export module Crafter.Asset:Texture;
import std;
import Crafter.Math;
namespace fs = std::filesystem;
export namespace Crafter {
template <typename T>
struct TextureAsset {
std::uint32_t sizeX;
std::uint32_t sizeY;
std::uint32_t sizeZ;
std::vector<T> pixels;
void Save(fs::path path) {
std::ofstream file(path, std::ios::binary);
file.write(reinterpret_cast<char*>(&sizeX), sizeof(sizeX));
file.write(reinterpret_cast<char*>(&sizeY), sizeof(sizeY));
file.write(reinterpret_cast<char*>(&sizeZ), sizeof(sizeZ));
file.write(reinterpret_cast<char*>(pixels.data()), pixels.size() * sizeof(T));
}
static TextureAsset<T> Load(fs::path path) {
TextureAsset<T> tex;
std::ifstream file(path, std::ios::binary);
file.read(reinterpret_cast<char*>(&tex.sizeX), sizeof(tex.sizeX));
file.read(reinterpret_cast<char*>(&tex.sizeY), sizeof(tex.sizeY));
file.read(reinterpret_cast<char*>(&tex.sizeZ), sizeof(tex.sizeZ));
tex.pixels.resize(tex.sizeX * tex.sizeY * tex.sizeZ);
file.read(reinterpret_cast<char*>(tex.pixels.data()), tex.sizeX * tex.sizeY * tex.sizeZ * sizeof(T));
}
static TextureAsset<T> LoadPNG(fs::path path) requires (std::same_as<T, Vector<std::uint8_t, 4, 0>>) {
TextureAsset<T> tex;
std::filesystem::path abs = std::filesystem::absolute(path);
int sizeX;
int sizeY;
unsigned char* data = stbi_load(abs.string().c_str(), &sizeX, &sizeY, nullptr, 4);
tex.sizeX = sizeX;
tex.sizeY = sizeY;
tex.sizeZ = 1;
tex.pixels.resize(tex.sizeX*tex.sizeY);
std::memcpy(tex.pixels.data(), data, tex.sizeX * tex.sizeY * 4);
return tex;
}
};
}

View file

@ -1,12 +1,11 @@
/* /*
Crafter®.Asset Crafter®.Asset
Copyright (C) 2025 Catcrafts® Copyright (C) 2026 Catcrafts®
Catcrafts.net catcrafts.net
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either License version 3.0 as published by the Free Software Foundation;
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
@ -18,5 +17,7 @@ License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
export module Crafter.Asset; export module Crafter.Asset;
export import :Asset; export import :Mesh;
export import :Texture;

166
main.cpp
View file

@ -1,166 +0,0 @@
/*
Crafter®.Asset
Copyright (C) 2025 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <iostream>
#include <string>
#include <filesystem>
#include <unordered_map>
#include <vector>
#include <functional>
import Crafter.Asset;
using namespace Crafter;
namespace fs = std::filesystem;
int main(int argc, char* argv[]) {
if(argc == 1) {
std::cout << "No arguments provided, use help for help" << std::endl;
return -1;
}
std::string command = std::string(argv[1]);
if(command == "help") {
std::cout << "help: Displays this help message.\nadd {file} {entry file} {entry type} {serializer} {entry name}: Adds an entry to an existing assset file or creates a new file if the file in {file} does not exist, {entry file} is the path to the file you want to add to the asset, {entry type} is the type of the resulting entry, {serializer} is the serializer to use to add the entry file to the asset, {entry name} is the name of the resulting entry\nremove {file} {entry}\nextract {file} {entry name} {extractor} {extracted path}: Extracts a entry with the name {entry name} from the asset file in {file}, using the extractor in {extractor} writes the result to the file {extracted path}\nlist serialize: Shows a list of the support serializers\nlist {file}: Lists all entries in the asset file\nlist extract: Shows a list of supported extractors" << std::endl;
} else if(command == "list") {
if(argc == 2) {
std::cout << "Unkown command, use help for help" << std::endl;
return -1;
}
std::string command2 = std::string(argv[2]);
if(command2 == "serialize") {
for (const auto& pair : serializers) {
std::cout << pair.first << std::endl;
}
} else if(command2 == "extract") {
for (const auto& pair : extractors) {
std::cout << pair.first << std::endl;
}
} else {
if(!fs::exists(argv[2])) {
std::cout << "Asset file does not exist" << std::endl;
return -1;
}
AssetLoad asset(argv[2]);
std::cout << "<name>\t<type>\t<size>" << std::endl;
for(const AssetEntry& entry : asset.entries) {
std::cout << std::format("{}\t{}\t{}", entry.name, entry.type, entry.lenght) << std::endl;
}
}
} else if(command == "remove") {
if(argc == 2) {
std::cout << "Missing argument {file}. Run help for help" << std::endl;
return -1;
} else if(argc == 3) {
std::cout << "Missing argument {enty name}. Run help for help" << std::endl;
return -1;
}
if(!fs::exists(argv[2])) {
std::cout << "Asset file does not exist" << std::endl;
return -1;
}
AssetLoad asset(argv[2]);
if(asset.entries.size() == 0) {
std::cout << "Nothing to remove, asset file empty" << std::endl;
}
for (auto it = asset.entries.begin(); it != asset.entries.end(); std::advance(it, 1)) {
if (it->name == argv[3]) {
asset.entries.erase(it);
fs::remove(argv[2]);
AssetSave ass2(asset.entries, std::move(asset.LoadAll()));
ass2.Save(argv[2]);
return 0;
}
std::cout << std::format("Entry {} not found", argv[3]) << std::endl;
return -1;
}
} else if(command == "add") {
if(argc == 2) {
std::cout << "Missing argument {file}. Run help for help" << std::endl;
return -1;
} else if(argc == 3) {
std::cout << "Missing argument {entry file}. Run help for help" << std::endl;
return -1;
} else if(argc == 4) {
std::cout << "Missing argument {entry type}. Run help for help" << std::endl;
return -1;
} else if(argc == 5) {
std::cout << "Missing argument {serializer}. Run help for help" << std::endl;
return -1;
} else if(argc == 6) {
std::cout << "Missing argument {entry name}. Run help for help" << std::endl;
return -1;
}
AssetEntry entry;
entry.name = std::string(argv[6]);
entry.type = std::string(argv[4]);
std::unordered_map<std::string, std::function<std::vector<char>(fs::path)>>::const_iterator pos = serializers.find(argv[5]);
if (pos == serializers.end()) {
std::cout << std::format("Unkown serializer {}", argv[5]) << std::endl;
return -1;
} else {
AssetSave asset;
if(fs::exists(argv[2])) {
AssetLoad assload(argv[2]);
asset = AssetSave(assload.entries, std::move(assload.LoadAll()));
fs::remove(argv[2]);
}
asset.AddEntry(entry.name, entry.type, pos->second(argv[3]));
asset.Save(argv[2]);
}
} else if(command == "extract") {
if(argc == 2) {
std::cout << "Missing argument {file}. Run help for help" << std::endl;
return -1;
} else if(argc == 3) {
std::cout << "Missing argument {entry name}. Run help for help" << std::endl;
return -1;
} else if(argc == 4) {
std::cout << "Missing argument {extractor}. Run help for help" << std::endl;
return -1;
} else if(argc == 5) {
std::cout << "Missing argument {extracted path}. Run help for help" << std::endl;
return -1;
}
if(!fs::exists(argv[2])) {
std::cout << "Asset file does not exist" << std::endl;
return -1;
}
AssetLoad asset(argv[2]);
auto pos = extractors.find(argv[4]);
if (pos == extractors.end()) {
std::cout << std::format("Unkown extractor {}", argv[4]) << std::endl;
return -1;
} else {
for(const AssetEntry& entry : asset.entries) {
if(entry.name == std::string(argv[3])) {
std::vector<char> loaded = asset.Load(entry);
pos->second(argv[5], loaded.data());
}
}
std::cout << std::format("Entry {} not found", argv[3]) << std::endl;
return -1;
}
} else {
std::cout << "Unkown command, use help for help" << std::endl;
}
}

View file

@ -3,41 +3,51 @@
"configurations": [ "configurations": [
{ {
"name": "base", "name": "base",
"standard": "c++26", "implementations": ["implementations/main"],
"source_files": ["Crafter.Asset-Asset"], "interfaces": ["interfaces/Crafter.Asset", "interfaces/Crafter.Asset-Texture", "interfaces/Crafter.Asset-Mesh"]
"module_files": ["Crafter.Asset-Asset", "Crafter.Asset"], },
"build_dir": "build", {
"output_dir": "bin" "name": "deps-debug",
"extends": ["base"],
"dependencies": [
{
"path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git",
"configuration":"lib-debug"
}
]
},
{
"name": "deps",
"extends": ["base"],
"type":"executable",
"dependencies": [
{
"path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git",
"configuration":"lib"
}
]
}, },
{ {
"name": "executable", "name": "executable",
"extends": ["base"], "extends": ["deps"],
"type":"executable", "type": "executable"
"source_files": ["main"]
}, },
{ {
"name": "executable-debug", "name": "executable-debug",
"extends": ["executable"], "extends": ["deps-debug"],
"type": "executable",
"debug": true "debug": true
}, },
{
"name": "executable-release",
"extends": ["executable"],
"optimization_level": "3"
},
{ {
"name": "lib", "name": "lib",
"extends": ["base"], "extends": ["deps"],
"type":"library" "type": "library"
}, },
{ {
"name": "lib-debug", "name": "lib-debug",
"extends": ["lib"], "extends": ["deps-debug"],
"debug": true "type": "library",
},
{
"name": "lib-release",
"extends": ["lib"],
"debug": true "debug": true
} }
] ]

File diff suppressed because it is too large Load diff