This commit is contained in:
parent
03717b5f33
commit
dea67ae5aa
2 changed files with 94 additions and 29 deletions
|
|
@ -292,36 +292,82 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asset compilation: each .png/.obj listed in cfg.assets is run through
|
// Asset compilation: each cfg.assets entry is either a single .png/.obj
|
||||||
// Crafter.Asset's library (linked into crafter-build directly — see
|
// file (flat output: outputDir/<filename>.ctex/cmesh — preserves the
|
||||||
// :Asset_impl) to produce the SaveCompressed .ctex/.cmesh in outputDir.
|
// original behavior) or a directory (recursed, with the relative tree
|
||||||
// Skipped if the output is newer than the source. Parallel with shaders
|
// mirrored under outputDir/<dirname>/; .png/.obj are compressed, every
|
||||||
// and module compiles; cancellation participates in the same flag.
|
// other file is copied through unchanged). Directory mode lets mod/map
|
||||||
auto assetOutput = [](const fs::path& asset) -> std::optional<fs::path> {
|
// trees keep their nested layout so mod.json paths like
|
||||||
std::string ext = asset.extension().string();
|
// "cannon/base.cmesh" resolve correctly at runtime.
|
||||||
if (ext == ".png") return fs::path(asset.filename()).replace_extension(".ctex");
|
// Skipped per-file if the output is newer than the source. Each
|
||||||
if (ext == ".obj") return fs::path(asset.filename()).replace_extension(".cmesh");
|
// compress runs in its own thread; passthrough copies are bundled into
|
||||||
|
// a single thread to match the cfg.files pattern.
|
||||||
|
auto compressedName = [](const fs::path& src) -> std::optional<fs::path> {
|
||||||
|
std::string ext = src.extension().string();
|
||||||
|
if (ext == ".png") return fs::path(src.filename()).replace_extension(".ctex");
|
||||||
|
if (ext == ".obj") return fs::path(src.filename()).replace_extension(".cmesh");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
};
|
};
|
||||||
for (const fs::path& asset : config.assets) {
|
auto submitCompress = [&](fs::path sourcePath, fs::path outRelative) {
|
||||||
std::optional<fs::path> outName = assetOutput(asset);
|
fs::path out = outputDir / outRelative;
|
||||||
if (!outName) {
|
if (fs::exists(out) && fs::exists(sourcePath) && fs::last_write_time(sourcePath) <= fs::last_write_time(out)) return;
|
||||||
buildCancelled.store(true);
|
threads.emplace_back([sourcePath = std::move(sourcePath), out = std::move(out), &buildError, &buildCancelled]() {
|
||||||
buildError = std::format("{}: unsupported asset extension (expected .png or .obj)", asset.string());
|
Progress::Task task(std::format("Compressing asset {}", sourcePath.filename().string()));
|
||||||
break;
|
|
||||||
}
|
|
||||||
fs::path out = outputDir / *outName;
|
|
||||||
if (fs::exists(out) && fs::exists(asset) && fs::last_write_time(asset) <= fs::last_write_time(out)) continue;
|
|
||||||
threads.emplace_back([&asset, out = std::move(out), &buildError, &buildCancelled]() {
|
|
||||||
Progress::Task task(std::format("Compressing asset {}", asset.filename().string()));
|
|
||||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||||
std::string result = CompressAsset(asset, out);
|
std::error_code ec;
|
||||||
|
fs::create_directories(out.parent_path(), ec);
|
||||||
|
std::string result = CompressAsset(sourcePath, out);
|
||||||
if (result.empty()) return;
|
if (result.empty()) return;
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||||
buildError = std::move(result);
|
buildError = std::move(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
std::vector<std::pair<fs::path, fs::path>> assetPassthroughs;
|
||||||
|
for (const fs::path& asset : config.assets) {
|
||||||
|
if (fs::is_directory(asset)) {
|
||||||
|
fs::path topName = asset.filename();
|
||||||
|
for (const auto& entry : fs::recursive_directory_iterator(asset)) {
|
||||||
|
if (!entry.is_regular_file()) continue;
|
||||||
|
fs::path rel = fs::relative(entry.path(), asset);
|
||||||
|
if (std::optional<fs::path> compName = compressedName(entry.path())) {
|
||||||
|
submitCompress(entry.path(), topName / rel.parent_path() / *compName);
|
||||||
|
} else {
|
||||||
|
assetPassthroughs.emplace_back(entry.path(), topName / rel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::optional<fs::path> outName = compressedName(asset);
|
||||||
|
if (!outName) {
|
||||||
|
buildCancelled.store(true);
|
||||||
|
buildError = std::format("{}: unsupported asset extension (expected .png or .obj, or a directory)", asset.string());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
submitCompress(asset, *outName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!assetPassthroughs.empty()) {
|
||||||
|
threads.emplace_back([passthroughs = std::move(assetPassthroughs), &outputDir, &buildCancelled, &buildError]() {
|
||||||
|
Progress::Task task("Copying asset passthrough files");
|
||||||
|
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||||
|
try {
|
||||||
|
for (const auto& [src, rel] : passthroughs) {
|
||||||
|
fs::path dst = outputDir / rel;
|
||||||
|
fs::create_directories(dst.parent_path());
|
||||||
|
if (!fs::exists(dst)) {
|
||||||
|
fs::copy_file(src, dst);
|
||||||
|
} else if (fs::last_write_time(src) > fs::last_write_time(dst)) {
|
||||||
|
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
bool expected = false;
|
||||||
|
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||||
|
buildError = e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() {
|
threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() {
|
||||||
|
|
@ -679,6 +725,16 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
||||||
copyTree(src, outputDir / additionalFile.filename());
|
copyTree(src, outputDir / additionalFile.filename());
|
||||||
}
|
}
|
||||||
for (const fs::path& asset : dep->assets) {
|
for (const fs::path& asset : dep->assets) {
|
||||||
|
// Directory entry: the dep already mirrored the
|
||||||
|
// (compressed + passthrough) tree under
|
||||||
|
// depBinDir/<asset.filename()>/. Forward it wholesale
|
||||||
|
// so our bin dir gets the same layout.
|
||||||
|
if (fs::is_directory(asset)) {
|
||||||
|
fs::path src = depBinDir / asset.filename();
|
||||||
|
if (!fs::exists(src)) continue;
|
||||||
|
copyTree(src, outputDir / asset.filename());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
std::string ext = asset.extension().string();
|
std::string ext = asset.extension().string();
|
||||||
fs::path srcName = asset.filename();
|
fs::path srcName = asset.filename();
|
||||||
if (ext == ".png") srcName.replace_extension(".ctex");
|
if (ext == ".png") srcName.replace_extension(".ctex");
|
||||||
|
|
|
||||||
|
|
@ -125,14 +125,23 @@ export namespace Crafter {
|
||||||
std::vector<fs::path> buildFiles;
|
std::vector<fs::path> buildFiles;
|
||||||
std::vector<Define> defines;
|
std::vector<Define> defines;
|
||||||
std::vector<Shader> shaders;
|
std::vector<Shader> shaders;
|
||||||
// Source assets (.png, .obj) compressed via Crafter.Asset's
|
// Source assets compressed via Crafter.Asset's SaveCompressed →
|
||||||
// SaveCompressed → .ctex/.cmesh in this configuration's bin dir.
|
// .ctex/.cmesh in this configuration's bin dir.
|
||||||
// Requires Crafter.Asset in cfg.dependencies (transitively): the
|
//
|
||||||
// build engine locates it by name and uses its library API to do
|
// Each entry is either:
|
||||||
// the conversion (an auxiliary host-target executable, generated
|
// - A single .png/.obj file. Output lands flat in the bin dir:
|
||||||
// once per build host, links Crafter.Asset and is invoked per
|
// bin/<filename>.ctex or bin/<filename>.cmesh.
|
||||||
// asset). Forwarded to a consuming executable's bin dir alongside
|
// - A directory. The build recurses; .png/.obj are compressed
|
||||||
// .spv shaders and cfg.files entries.
|
// with the relative tree mirrored under bin/<dirname>/, and
|
||||||
|
// every other file in the tree is copied through unchanged.
|
||||||
|
// Lets mod/map trees (mod.json + cannon/base.obj +
|
||||||
|
// cannon/color.png) keep their nested layout so JSON paths
|
||||||
|
// like "cannon/base.cmesh" resolve at runtime.
|
||||||
|
//
|
||||||
|
// Crafter.Asset must be reachable through cfg.dependencies (the
|
||||||
|
// build engine links its library API into crafter-build via a
|
||||||
|
// self-host pass). Forwarded to a consuming executable's bin dir
|
||||||
|
// alongside .spv shaders and cfg.files entries.
|
||||||
std::vector<fs::path> assets;
|
std::vector<fs::path> assets;
|
||||||
std::vector<ExternalDependency> externalDependencies;
|
std::vector<ExternalDependency> externalDependencies;
|
||||||
std::vector<std::string> compileFlags;
|
std::vector<std::string> compileFlags;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue