Some checks failed
CI / build-test-release (push) Failing after 15m11s
120 lines
5 KiB
C++
120 lines
5 KiB
C++
/*
|
|
Crafter® Build
|
|
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 Crafter.Build:Asset_impl;
|
|
import :Asset;
|
|
import std;
|
|
#ifdef CRAFTER_BUILD_HAS_ASSET
|
|
import Crafter.Asset;
|
|
import Crafter.Math;
|
|
#endif
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace Crafter {
|
|
std::string CompressAsset(const fs::path& input, const fs::path& output) {
|
|
std::string ext = input.extension().string();
|
|
// Case-insensitive extension match — Sponza's textures dir mixes
|
|
// case (.tga and .TGA both appear in the wild).
|
|
for (char& c : ext) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
|
#ifdef CRAFTER_BUILD_HAS_ASSET
|
|
try {
|
|
// stb_image (the loader behind LoadPNG) handles all of these,
|
|
// so a single branch covers every raster format we ship. The
|
|
// function is misnamed for historical reasons.
|
|
if (ext == ".png" || ext == ".tga" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp") {
|
|
auto tex = TextureAsset<Vector<std::uint8_t, 4, 4>>::LoadPNG<std::uint8_t>(input);
|
|
tex.SaveCompressed(output);
|
|
} else if (ext == ".obj") {
|
|
auto mesh = MeshAsset<VertexNormalTangentUVPacked>::LoadOBJ(input);
|
|
mesh.SaveCompressed(output);
|
|
} else {
|
|
return std::format("{}: unsupported asset extension '{}'", input.string(), ext);
|
|
}
|
|
} catch (const std::exception& e) {
|
|
return std::format("{}: {}", input.string(), e.what());
|
|
}
|
|
return {};
|
|
#else
|
|
return std::format(
|
|
"{}: crafter-build was bootstrapped without Crafter.Asset linkage; "
|
|
"rebuild via `./bin/crafter-build` so the self-host pass picks up the "
|
|
"Crafter.Asset dep declared in project.cpp",
|
|
input.string());
|
|
#endif
|
|
}
|
|
|
|
std::string BuildOBJBundle(
|
|
const fs::path& objPath,
|
|
const fs::path& mtlPath,
|
|
const fs::path& outDir,
|
|
std::uint16_t albedoSize)
|
|
{
|
|
#ifdef CRAFTER_BUILD_HAS_ASSET
|
|
const fs::path manifest = outDir / "scene.txt";
|
|
if (fs::exists(manifest)) return {};
|
|
try {
|
|
fs::create_directories(outDir);
|
|
auto materials = LoadMTL(mtlPath);
|
|
auto meshes = MeshAsset<VertexNormalTangentUVPacked>::LoadOBJSplit(objPath);
|
|
|
|
std::vector<std::string> uniqueAlbedos;
|
|
std::unordered_map<std::string, std::uint32_t> albedoIndex;
|
|
for (auto& [matName, _] : meshes) {
|
|
auto matIt = materials.find(matName);
|
|
if (matIt == materials.end() || matIt->second.mapKd.empty()) continue;
|
|
const std::string& kd = matIt->second.mapKd;
|
|
if (!albedoIndex.contains(kd)) {
|
|
albedoIndex.emplace(kd, static_cast<std::uint32_t>(uniqueAlbedos.size()));
|
|
uniqueAlbedos.push_back(kd);
|
|
}
|
|
}
|
|
|
|
std::vector<std::int32_t> meshAlbedoIdx;
|
|
std::uint32_t emitted = 0;
|
|
for (auto& [matName, mesh] : meshes) {
|
|
if (mesh.vertexes.empty() || mesh.indexes.empty()) continue;
|
|
mesh.SaveCompressed(outDir / std::format("mesh_{}.cmesh", emitted));
|
|
std::int32_t a = -1;
|
|
if (auto it = materials.find(matName);
|
|
it != materials.end() && !it->second.mapKd.empty())
|
|
a = static_cast<std::int32_t>(albedoIndex.at(it->second.mapKd));
|
|
meshAlbedoIdx.push_back(a);
|
|
++emitted;
|
|
}
|
|
|
|
for (std::uint32_t i = 0; i < uniqueAlbedos.size(); ++i) {
|
|
const fs::path texPath = mtlPath.parent_path() / uniqueAlbedos[i];
|
|
auto tex = TextureAsset<Vector<std::uint8_t, 4, 4>>::LoadPNG<std::uint8_t>(texPath);
|
|
tex.Resize(albedoSize, albedoSize);
|
|
tex.SaveCompressed(outDir / std::format("tex_{}.ctex", i));
|
|
}
|
|
|
|
std::ofstream m(manifest);
|
|
m << uniqueAlbedos.size() << "\n" << emitted << "\n";
|
|
for (std::int32_t a : meshAlbedoIdx) m << a << "\n";
|
|
} catch (const std::exception& e) {
|
|
return std::format("BuildOBJBundle: {}", e.what());
|
|
}
|
|
return {};
|
|
#else
|
|
return "BuildOBJBundle: crafter-build was bootstrapped without Crafter.Asset linkage; "
|
|
"rebuild via crafter-build itself to pick up the Crafter.Asset dep";
|
|
#endif
|
|
}
|
|
}
|