支持批处理目录

This commit is contained in:
sladro 2026-03-12 08:51:46 +08:00
parent 20ce7caaa8
commit 939a56ae03
16 changed files with 672 additions and 292 deletions

View File

@ -10,9 +10,10 @@
"mcp__serena__write_memory",
"mcp__serena__search_for_pattern",
"mcp__context7__resolve-library-id",
"mcp__context7__get-library-docs"
"mcp__context7__get-library-docs",
"Bash(wc -l:*)"
],
"deny": [],
"ask": []
}
}
}

View File

@ -1,3 +1,90 @@
Win11 打包流程(生成适用于 Win7 的静态版)
1. 进入 VS 2022 开发者命令行
打开“x64 Native Tools Command Prompt for VS 2022”或在 PowerShell 中执行 cmd.exe /k
"<VS路径>\vcvars64.bat" 并在新开的 CMD 中操作)。
2. 安装/更新 Pixi 静态环境依赖
```bat
C:\Users\sladr\.pixi\bin\pixi.exe install -e static
```
3. 清理旧的构建目录(可选但推荐)
```bat
rmdir /s /q build\win-static
```
4. 配置静态工程(目标 Win7
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake ^
-G "Visual Studio 17 2022" -A x64 ^
-S . -B build\win-static ^
-DWIN32_WINNT=0x0601 ^
-DBUILD_STATIC=ON -DBUILD_SHARED_LIBS=OFF ^
-DCMAKE_BUILD_TYPE=Release
```
5. 编译 Release 可执行文件
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake --build build\win-static --config Release
```
6. (可选)安装到 Pixi 环境 bin
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake --install build\win-static --config Release
```
7. 产物位置
• 未安装build\win-static\Release\STP2GLB.exe
• 安装后:.pixi\envs\static\Library\bin\STP2GLB.exe
──────────────────────────────────────────
Win7 打包流程(直接在 Win7 上构建)
前提Win7 SP1 + VS 2019/2022带 MSVC v142/v143安装 Pixi并确保 SDK/工具链支持 VS 构建。
1. 使用已配置好的 VS 开发者命令行
启动 Win7 上的 “x64 Native Tools Command Prompt”保证 cl.exe 和 cmake 可用。
2. 安装 Pixi 静态环境依赖
```bat
C:\Users\sladr\.pixi\bin\pixi.exe install -e static
```
3. 清理旧目录
```bat
rmdir /s /q build\win-static
```
4. 配置工程(同样指定 Win7
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake ^
-G "Visual Studio 17 2022" -A x64 ^
-S . -B build\win-static ^
-DWIN32_WINNT=0x0601 ^
-DBUILD_STATIC=ON -DBUILD_SHARED_LIBS=OFF ^
-DCMAKE_BUILD_TYPE=Release
```
5. 编译 Release
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake --build build\win-static --config Release
```
6. (可选)安装
```bat
C:\Users\sladr\.pixi\bin\pixi.exe run -e static cmake --install build\win-static --config Release
```
7. 产物位置
同 Win11 流程,产物可执行文件位于构建目录或安装后的 Pixi 环境 bin 目录。
──────────────────────────────────────────
注意事项
• 每次变更代码后建议在 Win11 构建并将 STP2GLB.exe 拷贝到 Win7 实机进行实际验证。
• 若在 Win7 上本地构建,需确保 VS 工具链与 Windows SDK 在该机器上完整可用;否则可在 Win11
构建后直接拷贝二进制。
# Stp2Glb
## Usage

Binary file not shown.

Binary file not shown.

BIN
aa.glb

Binary file not shown.

View File

@ -1,4 +1,7 @@
# Find CGAL
if(NOT CGAL_DIR)
#set(CGAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.pixi/envs/static/Library/cmake/CGAL" CACHE PATH "Path to CGAL installation")
endif()
find_package(CGAL REQUIRED)
if (CGAL_FOUND)

Binary file not shown.

BIN
gltfpack.exe Normal file

Binary file not shown.

View File

@ -264,4 +264,4 @@ def main():
if __name__ == '__main__':
sys.exit(main())
sys.exit(main())

2
output5-log.json Normal file
View File

@ -0,0 +1,2 @@
[
]

2
output6-log.json Normal file
View File

@ -0,0 +1,2 @@
[
]

611
pixi.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,7 @@ cmake = "==3.30.5"
cli11 = "*"
ninja = "*"
nlohmann_json = "*"
cgal-cpp = "==5.6.1"
[feature.static.target.linux-64.dependencies]
c-compiler = "*"
@ -109,10 +110,13 @@ install = { cmd = ["ninja", "-C", "build/linux-static", "install"], description
build = { cmd=["cmake","-G","Ninja", "--preset", "win-static-mingw", "--fresh"] }
install = { cmd = ["ninja", "-C", "build/win-static-mingw", "install"], description = "Install the project" }
[dependencies]
cgal-cpp = "==5.6.1"
[feature.conda.target.win-64.tasks]
build-occt = { cmd=["rattler-build", "build", "-r", "recipes/occt-static/recipe.yaml", "-m","recipes/occt-static/conda_build_config-win.yaml"] }
build-stepcode = { cmd=["rattler-build", "build", "-r", "recipes/stepcode-static/recipe.yaml", "-m","recipes/stepcode-static/conda_build_config-win.yaml"] }
[feature.conda.target.linux-64.tasks]
build-occt = { cmd=["rattler-build", "build", "-r", "recipes/occt-static/recipe.yaml", "-m","recipes/occt-static/conda_build_config-linux.yaml"] }
build-stepcode = { cmd=["rattler-build", "build", "-r", "recipes/stepcode-static/recipe.yaml", "-m","recipes/stepcode-static/conda_build_config-linux.yaml"] }
build-stepcode = { cmd=["rattler-build", "build", "-r", "recipes/stepcode-static/recipe.yaml", "-m","recipes/stepcode-static/conda_build_config-linux.yaml"] }

View File

@ -67,8 +67,12 @@ bool endsWithCaseInsensitive(const std::string& str, const std::string& suffix)
return true;
}
// Main processing function
GlobalConfig process_parameters(CLI::App& app)
namespace {
GlobalConfig process_parameters_internal(CLI::App& app,
const std::filesystem::path* stp_override,
const std::filesystem::path* glb_override,
const std::filesystem::path* compressed_glb_override)
{
const auto filter_names_include_input = app.get_option("--filter-names-include")->as<std::string>();
const auto filter_names_file_include = app.get_option("--filter-names-file-include")->as<std::string>();
@ -85,8 +89,8 @@ GlobalConfig process_parameters(CLI::App& app)
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);
std::string stpFilename = app.get_option("--stp")->results()[0];
std::string glbFilename = app.get_option("--glb")->results()[0];
std::string stpFilename = stp_override ? stp_override->string() : app.get_option("--stp")->results()[0];
std::string glbFilename = glb_override ? glb_override->string() : app.get_option("--glb")->results()[0];
bool isDownloadedFromUrl = false;
std::string originalUrl;
@ -155,8 +159,30 @@ GlobalConfig process_parameters(CLI::App& app)
.is_downloaded_from_url = isDownloadedFromUrl,
.original_url = originalUrl,
.compress_glb = compress_glb,
.compressed_glb_path = compressed_glb_value.empty() ? std::filesystem::path{} : std::filesystem::path(compressed_glb_value),
.compressed_glb_path = compressed_glb_override
? *compressed_glb_override
: (compressed_glb_value.empty() ? std::filesystem::path{}
: std::filesystem::path(compressed_glb_value)),
.gltfpack_path = gltfpack_path_value,
.gltfpack_args = gltfpack_args_value
};
}
}
// Main processing function
GlobalConfig process_parameters(CLI::App& app)
{
return process_parameters_internal(app, nullptr, nullptr, nullptr);
}
GlobalConfig process_parameters(CLI::App& app,
const std::filesystem::path& stp_path_override,
const std::filesystem::path& glb_path_override,
const std::filesystem::path& compressed_glb_path_override)
{
const std::filesystem::path* compressed_override_ptr = compressed_glb_path_override.empty()
? nullptr
: &compressed_glb_path_override;
return process_parameters_internal(app, &stp_path_override, &glb_path_override, compressed_override_ptr);
}

View File

@ -4,12 +4,17 @@
#ifndef CONFIG_UTILS_H
#define CONFIG_UTILS_H
#include <filesystem>
#include <CLI/App.hpp>
#include "config_structs.h"
GlobalConfig process_parameters(CLI::App& app);
GlobalConfig process_parameters(CLI::App& app,
const std::filesystem::path& stp_path_override,
const std::filesystem::path& glb_path_override,
const std::filesystem::path& compressed_glb_path_override = {});
#endif //CONFIG_UTILS_H

View File

@ -5,7 +5,9 @@
#endif // Unix platform check
#include <algorithm>
#include <filesystem>
#include <vector>
#include "CLI/CLI.hpp"
#include "config_structs.h"
#include <chrono>
@ -17,6 +19,163 @@
#include "http_server.h"
#include "compression_utils.h"
void print_status(const GlobalConfig& config);
namespace {
bool is_step_file(const std::filesystem::path& path) {
std::string extension = path.extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char c) {
return static_cast<char>(std::tolower(c));
});
return extension == ".stp" || extension == ".step";
}
std::vector<std::filesystem::path> collect_step_files(const std::filesystem::path& input_dir) {
std::vector<std::filesystem::path> files;
for (const auto& entry : std::filesystem::directory_iterator(input_dir)) {
if (entry.is_regular_file() && is_step_file(entry.path())) {
files.push_back(entry.path());
}
}
std::sort(files.begin(), files.end(), [](const auto& lhs, const auto& rhs) {
return lhs.filename().string() < rhs.filename().string();
});
return files;
}
int run_conversion(const GlobalConfig& config) {
print_status(config);
std::cout << "\n";
std::cout << "Starting conversion..." << "\n";
const auto start = std::chrono::high_resolution_clock::now();
try {
if (config.buildConfig.build_bspline_surf)
make_a_bspline_surf(config);
if (config.debug_mode == 1)
debug_stp_to_glb(config);
else
convert_stp_to_glb(config);
apply_gltfpack_compression(config);
} catch (std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
return 1;
}
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 << "STP converted in: " << std::fixed << std::setprecision(2) << seconds << " seconds" << "\n";
if (config.is_downloaded_from_url) {
try {
if (std::filesystem::exists(config.stpFile)) {
std::filesystem::remove(config.stpFile);
std::cout << "Cleaned up downloaded file: " << config.stpFile << "\n";
}
} catch (const std::exception& ex) {
std::cerr << "Warning: Failed to clean up downloaded file: " << ex.what() << "\n";
}
}
return 0;
}
int run_batch_conversion(CLI::App& app) {
const std::filesystem::path input_dir = app.get_option("--stp")->results()[0];
const std::filesystem::path output_dir = app.get_option("--glb")->results()[0];
const auto step_files = collect_step_files(input_dir);
if (step_files.empty()) {
std::cerr << "Error: No .stp or .step files found in input directory: " << input_dir << "\n";
return 1;
}
if (std::filesystem::exists(output_dir) && !std::filesystem::is_directory(output_dir)) {
std::cerr << "Error: When --stp is a directory, --glb must be a directory path.\n";
return 1;
}
std::filesystem::create_directories(output_dir);
const auto compressed_glb_cli = app.get_option("--compressed-glb")->as<std::string>();
const std::filesystem::path compressed_glb_base = compressed_glb_cli.empty()
? std::filesystem::path{}
: std::filesystem::path(compressed_glb_cli);
if (!compressed_glb_base.empty() &&
std::filesystem::exists(compressed_glb_base) &&
!std::filesystem::is_directory(compressed_glb_base)) {
std::cerr << "Error: In batch mode, --compressed-glb must be a directory path when it already exists.\n";
return 1;
}
if (!compressed_glb_base.empty()) {
std::filesystem::create_directories(compressed_glb_base);
}
std::cout << "Batch mode enabled" << "\n";
std::cout << "Input directory: " << input_dir << "\n";
std::cout << "Output directory: " << output_dir << "\n";
std::cout << "Files to convert: " << step_files.size() << "\n";
int success_count = 0;
std::vector<std::string> failed_files;
for (size_t index = 0; index < step_files.size(); ++index) {
const auto& stp_file = step_files[index];
auto glb_file_name = stp_file.stem();
glb_file_name += ".glb";
const auto glb_file = output_dir / glb_file_name;
std::filesystem::path compressed_glb_file;
if (!compressed_glb_base.empty()) {
auto compressed_file_name = stp_file.stem();
compressed_file_name += ".glb";
compressed_glb_file = compressed_glb_base / compressed_file_name;
}
std::cout << "============================================================" << "\n";
std::cout << "[" << (index + 1) << "/" << step_files.size() << "] "
<< stp_file.filename() << " -> " << glb_file.filename() << "\n";
GlobalConfig config;
try {
config = process_parameters(app, stp_file, glb_file, compressed_glb_file);
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
failed_files.push_back(stp_file.filename().string());
continue;
}
if (run_conversion(config) == 0) {
++success_count;
} else {
failed_files.push_back(stp_file.filename().string());
}
}
std::cout << "============================================================" << "\n";
std::cout << "Batch completed: success " << success_count
<< ", failed " << failed_files.size() << "\n";
if (!failed_files.empty()) {
std::cout << "Failed files:" << "\n";
for (const auto& failed_file : failed_files) {
std::cout << " - " << failed_file << "\n";
}
return 1;
}
return 0;
}
}
void print_status(const GlobalConfig& config) {
std::cout << "STP2GLB Converter" << "\n";
std::cout << "STP File: " << config.stpFile << "\n";
@ -129,6 +288,11 @@ int main(int argc, char* argv[])
app.get_option("--stp")->required();
app.get_option("--glb")->required();
const std::filesystem::path stp_input = app.get_option("--stp")->results()[0];
if (std::filesystem::exists(stp_input) && std::filesystem::is_directory(stp_input)) {
return run_batch_conversion(app);
}
GlobalConfig config;
try {
config = process_parameters(app);
@ -137,44 +301,5 @@ int main(int argc, char* argv[])
return 1;
}
print_status(config);
std::cout << "\n";
std::cout << "Starting conversion..." << "\n";
const auto start = std::chrono::high_resolution_clock::now();
try {
if (config.buildConfig.build_bspline_surf)
make_a_bspline_surf(config);
if (config.debug_mode == 1)
debug_stp_to_glb(config);
else
{
convert_stp_to_glb(config);
}
apply_gltfpack_compression(config);
} catch (std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
return 1;
}
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 << "STP converted in: " << std::fixed << std::setprecision(2) << seconds << " seconds" << "\n";
// Clean up downloaded temporary file
if (config.is_downloaded_from_url) {
try {
if (std::filesystem::exists(config.stpFile)) {
std::filesystem::remove(config.stpFile);
std::cout << "Cleaned up downloaded file: " << config.stpFile << "\n";
}
} catch (const std::exception& ex) {
std::cerr << "Warning: Failed to clean up downloaded file: " << ex.what() << "\n";
}
}
return 0;
return run_conversion(config);
}