diff --git a/.gitignore b/.gitignore index 4cfd6c4..dde5668 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,8 @@ temp/ .vs/ # pixi environments .pixi/ -output/ \ No newline at end of file +output/ +*.stp +*.stl +*.step +*.glb \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 584c6b8..149b659 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/out.glb b/out.glb deleted file mode 100644 index 4cb6945..0000000 Binary files a/out.glb and /dev/null differ diff --git a/out1.glb b/out1.glb deleted file mode 100644 index 6ce065f..0000000 Binary files a/out1.glb and /dev/null differ diff --git a/src/compression_utils.cpp b/src/compression_utils.cpp new file mode 100644 index 0000000..92c2b78 --- /dev/null +++ b/src/compression_utils.cpp @@ -0,0 +1,99 @@ +#include "compression_utils.h" + +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/compression_utils.h b/src/compression_utils.h new file mode 100644 index 0000000..b057a14 --- /dev/null +++ b/src/compression_utils.h @@ -0,0 +1,8 @@ +#ifndef COMPRESSION_UTILS_H +#define COMPRESSION_UTILS_H + +#include "config_structs.h" + +void apply_gltfpack_compression(const GlobalConfig& config); + +#endif diff --git a/src/config_structs.h b/src/config_structs.h index 5eaf45a..c37b9ab 100644 --- a/src/config_structs.h +++ b/src/config_structs.h @@ -7,9 +7,10 @@ #include #include +#include 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"; }; diff --git a/src/config_utils.cpp b/src/config_utils.cpp index f01389d..d6760fe 100644 --- a/src/config_utils.cpp +++ b/src/config_utils.cpp @@ -76,6 +76,11 @@ GlobalConfig process_parameters(CLI::App& app) const auto filter_names_exclude_input = app.get_option("--filter-names-exclude")->as(); const auto filter_names_file_exclude = app.get_option("--filter-names-file-exclude")->as(); + const bool compress_glb = app.get_option("--compress-glb")->as(); + const auto compressed_glb_value = app.get_option("--compressed-glb")->as(); + const auto gltfpack_path_value = app.get_option("--gltfpack-path")->as(); + const auto gltfpack_args_value = app.get_option("--gltfpack-args")->as(); + // 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(), .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(), .port = app.get_option("--port")->as(), @@ -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 }; } diff --git a/src/http_server.cpp b/src/http_server.cpp index 9fe203f..dbbe349 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -3,6 +3,7 @@ #include "cadit/occt/convert.h" #include "cadit/occt/debug.h" #include "http_downloader.h" +#include "compression_utils.h" #include #include #include @@ -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(stop - start); const double seconds = static_cast(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()) + "\"}"; diff --git a/src/main.cpp b/src/main.cpp index 24e94b7..8eed163 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(); + config.gltfpack_path = app.get_option("--gltfpack-path")->as(); + config.gltfpack_args = app.get_option("--gltfpack-args")->as(); + + const auto compressed_glb_cli = app.get_option("--compressed-glb")->as(); + 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; diff --git a/test.glb b/test.glb deleted file mode 100644 index 56cd6f2..0000000 Binary files a/test.glb and /dev/null differ