feat: add optional gltfpack compression

This commit is contained in:
sladro 2025-10-22 16:38:52 +08:00
parent b454f18a02
commit 003b9306e7
11 changed files with 192 additions and 8 deletions

6
.gitignore vendored
View File

@ -7,4 +7,8 @@ temp/
.vs/
# pixi environments
.pixi/
output/
output/
*.stp
*.stl
*.step
*.glb

View File

@ -73,6 +73,7 @@ set(SOURCES
src/config_utils.cpp
src/http_server.cpp
src/http_downloader.cpp
src/compression_utils.cpp
src/geom/Color.cpp
src/cadit/occt/step_tree.cpp
src/cadit/occt/debug.cpp
@ -89,6 +90,7 @@ set(HEADERS
src/config_structs.h
src/http_server.h
src/http_downloader.h
src/compression_utils.h
src/geom/Color.h
src/cadit/occt/step_tree.h
src/cadit/occt/convert.h

BIN
out.glb

Binary file not shown.

BIN
out1.glb

Binary file not shown.

99
src/compression_utils.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "compression_utils.h"
#include <cstdlib>
#include <filesystem>
#include <sstream>
#include <stdexcept>
#include <string>
#include <iostream>
namespace {
std::string quote_arg(const std::string& value) {
std::string escaped;
escaped.reserve(value.size());
for (char c : value) {
if (c == '"') {
escaped += "\\\"";
} else {
escaped.push_back(c);
}
}
return '"' + escaped + '"';
}
std::string quote_path(const std::filesystem::path& path) {
return quote_arg(path.string());
}
void ensure_parent_directory(const std::filesystem::path& path) {
const auto parent = path.parent_path();
if (!parent.empty() && !std::filesystem::exists(parent)) {
std::filesystem::create_directories(parent);
}
}
}
void apply_gltfpack_compression(const GlobalConfig& config) {
if (!config.compress_glb) {
return;
}
const std::filesystem::path input_glb = config.glbFile;
if (!std::filesystem::exists(input_glb)) {
throw std::runtime_error("GLB file does not exist, cannot run gltfpack: " + input_glb.string());
}
std::filesystem::path target_output = config.compressed_glb_path.empty() ? input_glb : config.compressed_glb_path;
ensure_parent_directory(target_output);
bool overwrite_original = target_output == input_glb;
std::filesystem::path gltfpack_output;
if (overwrite_original) {
gltfpack_output = target_output;
gltfpack_output += ".tmp";
gltfpack_output.replace_extension(".glb");
} else {
gltfpack_output = target_output;
}
if (std::filesystem::exists(gltfpack_output)) {
std::filesystem::remove(gltfpack_output);
}
std::ostringstream command;
const bool path_needs_quotes = config.gltfpack_path.find_first_of(" \t\"") != std::string::npos;
if (path_needs_quotes) {
command << quote_arg(config.gltfpack_path);
} else {
command << config.gltfpack_path;
}
if (!config.gltfpack_args.empty()) {
command << ' ' << config.gltfpack_args;
}
command << " -i " << quote_path(input_glb);
command << " -o " << quote_path(gltfpack_output);
std::cout << "Running gltfpack compression..." << std::endl;
const int exit_code = std::system(command.str().c_str());
if (exit_code != 0) {
if (std::filesystem::exists(gltfpack_output)) {
std::filesystem::remove(gltfpack_output);
}
std::ostringstream err;
err << "gltfpack failed with exit code: " << exit_code;
throw std::runtime_error(err.str());
}
if (!std::filesystem::exists(gltfpack_output)) {
throw std::runtime_error("gltfpack did not produce the expected output: " + gltfpack_output.string());
}
if (overwrite_original) {
std::filesystem::remove(input_glb);
std::filesystem::rename(gltfpack_output, input_glb);
}
std::cout << "gltfpack compression finished." << std::endl;
}

8
src/compression_utils.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef COMPRESSION_UTILS_H
#define COMPRESSION_UTILS_H
#include "config_structs.h"
void apply_gltfpack_compression(const GlobalConfig& config);
#endif

View File

@ -7,9 +7,10 @@
#include <vector>
#include <filesystem>
#include <string>
struct BuildConfig {
bool build_bspline_surf;
bool build_bspline_surf = false;
};
struct ServerConfig {
@ -46,6 +47,12 @@ struct GlobalConfig {
// HTTP download tracking
bool is_downloaded_from_url = false;
std::string original_url;
// GLB compression settings
bool compress_glb = false;
std::filesystem::path compressed_glb_path{};
std::string gltfpack_path = "gltfpack";
std::string gltfpack_args = "-cc -tc -kn -km -vpf";
};

View File

@ -76,6 +76,11 @@ GlobalConfig process_parameters(CLI::App& app)
const auto filter_names_exclude_input = app.get_option("--filter-names-exclude")->as<std::string>();
const auto filter_names_file_exclude = app.get_option("--filter-names-file-exclude")->as<std::string>();
const bool compress_glb = app.get_option("--compress-glb")->as<bool>();
const auto compressed_glb_value = app.get_option("--compressed-glb")->as<std::string>();
const auto gltfpack_path_value = app.get_option("--gltfpack-path")->as<std::string>();
const auto gltfpack_args_value = app.get_option("--gltfpack-args")->as<std::string>();
// Process include and exclude filter names
const auto filter_names_include = process_filter_names(filter_names_include_input, filter_names_file_include);
const auto filter_names_exclude = process_filter_names(filter_names_exclude_input, filter_names_file_exclude);
@ -139,9 +144,7 @@ GlobalConfig process_parameters(CLI::App& app)
.tessellation_timout = app.get_option("--tessellation-timeout")->as<int>(),
.filter_names_include = filter_names_include,
.filter_names_exclude = filter_names_exclude,
.buildConfig = {
.build_bspline_surf = false
},
.buildConfig = {},
.serverConfig = {
.enable_server = app.get_option("--server")->as<bool>(),
.port = app.get_option("--port")->as<int>(),
@ -150,6 +153,10 @@ GlobalConfig process_parameters(CLI::App& app)
.temp_dir = "./temp"
},
.is_downloaded_from_url = isDownloadedFromUrl,
.original_url = originalUrl
.original_url = originalUrl,
.compress_glb = compress_glb,
.compressed_glb_path = compressed_glb_value.empty() ? std::filesystem::path{} : std::filesystem::path(compressed_glb_value),
.gltfpack_path = gltfpack_path_value,
.gltfpack_args = gltfpack_args_value
};
}

View File

@ -3,6 +3,7 @@
#include "cadit/occt/convert.h"
#include "cadit/occt/debug.h"
#include "http_downloader.h"
#include "compression_utils.h"
#include <iostream>
#include <filesystem>
#include <fstream>
@ -95,6 +96,26 @@ void start_http_server(const GlobalConfig& base_config) {
config.max_geometry_num = std::stoi(req.form.get_field("maxGeometryNum"));
}
if (req.form.has_field("compressGlb")) {
config.compress_glb = req.form.get_field("compressGlb") == "true";
}
if (req.form.has_field("gltfpackPath")) {
config.gltfpack_path = req.form.get_field("gltfpackPath");
}
if (req.form.has_field("gltfpackArgs")) {
config.gltfpack_args = req.form.get_field("gltfpackArgs");
}
if (req.form.has_field("compressedGlb")) {
std::string compressed_value = req.form.get_field("compressedGlb");
if (!compressed_value.empty()) {
fs::path compressed_path = compressed_value;
if (!compressed_path.is_absolute()) {
compressed_path = fs::path(config.serverConfig.output_dir) / compressed_path;
}
config.compressed_glb_path = compressed_path;
}
}
std::string base_filename = generate_unique_filename("");
std::string stp_filename = base_filename + ".stp";
std::string glb_filename = base_filename + ".glb";
@ -132,16 +153,25 @@ void start_http_server(const GlobalConfig& base_config) {
convert_stp_to_glb(config);
}
apply_gltfpack_compression(config);
if (config.compress_glb && !config.compressed_glb_path.empty() && config.compressed_glb_path != glb_path && fs::exists(glb_path)) {
fs::remove(glb_path);
}
const auto stop = std::chrono::high_resolution_clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
const double seconds = static_cast<double>(duration.count()) / 1e6;
std::cout << "Conversion completed in " << std::fixed << std::setprecision(2) << seconds << " seconds" << "\n";
std::cout << "Output saved: " << glb_path << "\n";
fs::path final_glb_path = config.compress_glb && !config.compressed_glb_path.empty() ? config.compressed_glb_path : glb_path;
std::cout << "Output saved: " << final_glb_path << "\n";
fs::remove(stp_path);
std::string response = "{\"success\":true,\"output_file\":\"" + glb_filename + "\",\"output_path\":\"" + glb_path.string() + "\",\"conversion_time\":" + std::to_string(seconds) + "}";
std::string final_filename = final_glb_path.filename().string();
std::string response = "{\"success\":true,\"output_file\":\"" + final_filename + "\",\"output_path\":\"" + final_glb_path.string() + "\",\"conversion_time\":" + std::to_string(seconds) + "}";
res.set_content(response, "application/json");
} catch (const std::exception& ex) {
@ -149,6 +179,9 @@ void start_http_server(const GlobalConfig& base_config) {
if (fs::exists(stp_path)) fs::remove(stp_path);
if (fs::exists(glb_path)) fs::remove(glb_path);
if (config.compress_glb && !config.compressed_glb_path.empty() && fs::exists(config.compressed_glb_path)) {
fs::remove(config.compressed_glb_path);
}
res.status = 500;
std::string error_msg = "{\"success\":false,\"error\":\"" + std::string(ex.what()) + "\"}";

View File

@ -15,6 +15,7 @@
#include "cadit/occt/helpers.h"
#include "config_utils.h"
#include "http_server.h"
#include "compression_utils.h"
void print_status(const GlobalConfig& config) {
std::cout << "STP2GLB Converter" << "\n";
@ -30,6 +31,14 @@ void print_status(const GlobalConfig& config) {
std::cout << "Max Geometry Num: " << config.max_geometry_num << "\n";
std::cout << "Tessellation Timeout: " << config.tessellation_timout << "\n\n";
std::cout << "Compression: " << (config.compress_glb ? "enabled" : "disabled") << "\n";
if (config.compress_glb) {
std::cout << "gltfpack Path: " << config.gltfpack_path << "\n";
std::cout << "gltfpack Args: " << config.gltfpack_args << "\n\n";
} else {
std::cout << "\n";
}
// Debug output
if (!config.filter_names_include.empty())
{
@ -73,6 +82,10 @@ int main(int argc, char* argv[])
app.add_option("--filter-names-exclude", "Exclude Filter name. Command separated list")->default_val("");
app.add_option("--filter-names-file-exclude", "Exclude Filter name file")->default_val("");
app.add_option("--tessellation-timeout", "Tessellation timeout")->default_val(30);
app.add_flag("--compress-glb", "Enable glTF compression using gltfpack");
app.add_option("--compressed-glb", "Optional output path for compressed GLB (defaults to original)")->default_val("");
app.add_option("--gltfpack-path", "Path to gltfpack executable")->default_val("gltfpack");
app.add_option("--gltfpack-args", "Arguments passed to gltfpack")->default_val("-cc -tc -kn -km -vpf");
CLI11_PARSE(app, argc, argv);
@ -95,6 +108,15 @@ int main(int argc, char* argv[])
config.serverConfig.temp_dir = "./temp";
config.serverConfig.output_dir = "./output";
config.compress_glb = app.get_option("--compress-glb")->as<bool>();
config.gltfpack_path = app.get_option("--gltfpack-path")->as<std::string>();
config.gltfpack_args = app.get_option("--gltfpack-args")->as<std::string>();
const auto compressed_glb_cli = app.get_option("--compressed-glb")->as<std::string>();
if (!compressed_glb_cli.empty()) {
std::cout << "Warning: --compressed-glb is ignored in server mode." << "\n";
}
try {
start_http_server(config);
} catch (const std::exception& ex) {
@ -130,6 +152,8 @@ int main(int argc, char* argv[])
{
convert_stp_to_glb(config);
}
apply_gltfpack_compression(config);
} catch (std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
return 1;

BIN
test.glb

Binary file not shown.