| 262 | } |
| 263 | |
| 264 | static void setupModelRender(CLI::App& model) |
| 265 | { |
| 266 | auto* cmd = model.add_subcommand("render", "Render P3D model wireframe to image file"); |
| 267 | static std::string inputPath; |
| 268 | static std::string outputPath; |
| 269 | static int width = 512; |
| 270 | static int height = 512; |
| 271 | static int lodIndex = 0; |
| 272 | static std::string view = "front"; |
| 273 | |
| 274 | cmd->add_option("input", inputPath, "Input P3D file path")->required()->check(CLI::ExistingFile); |
| 275 | cmd->add_option("-o,--output", outputPath, "Output image path (.png, .bmp, .tga)")->required(); |
| 276 | cmd->add_option("-W,--width", width, "Image width in pixels")->default_val(512); |
| 277 | cmd->add_option("-H,--height", height, "Image height in pixels")->default_val(512); |
| 278 | cmd->add_option("-l,--lod", lodIndex, "LOD level to render")->default_val(0); |
| 279 | cmd->add_option("--view", view, "View: front, back, top, bottom, right, left, 3d, quad")->default_val("front"); |
| 280 | |
| 281 | cmd->callback( |
| 282 | [&]() |
| 283 | { |
| 284 | Poseidon::ModelPreviewOptions opts; |
| 285 | opts.width = width; |
| 286 | opts.height = height; |
| 287 | opts.lodIndex = lodIndex; |
| 288 | opts.view = view; |
| 289 | |
| 290 | auto preview = Poseidon::PreviewModel(inputPath, opts); |
| 291 | if (!preview.valid()) |
| 292 | { |
| 293 | std::cerr << "Error: Failed to render model: " << inputPath << std::endl; |
| 294 | throw CLI::RuntimeError(1); |
| 295 | } |
| 296 | |
| 297 | if (!preview.saveToFile(outputPath)) |
| 298 | { |
| 299 | std::cerr << "Error: Failed to save: " << outputPath << std::endl; |
| 300 | throw CLI::RuntimeError(1); |
| 301 | } |
| 302 | |
| 303 | std::cout << "Rendered: " << inputPath << " (LOD " << lodIndex << ", " << view << " view, " << width << "x" |
| 304 | << height << ") -> " << outputPath << std::endl; |
| 305 | }); |
| 306 | } |
| 307 | |
| 308 | static void setupModelShow(CLI::App& model) |
| 309 | { |
no test coverage detected