From fc827ca44d235c029602a2c9f66867c52df07734 Mon Sep 17 00:00:00 2001 From: orchestrator-bot Date: Wed, 12 Aug 2026 05:18:58 +0000 Subject: [PATCH] agent: Write a CLI interface for the Ray Tracer Application Iteration 1 --- src/util/CLI.cpp | 154 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/CLI.h | 29 +++++++++ 2 files changed, 183 insertions(+) diff --git a/src/util/CLI.cpp b/src/util/CLI.cpp index 7227ed2..d26f36e 100644 --- a/src/util/CLI.cpp +++ b/src/util/CLI.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include namespace CLI { @@ -60,6 +62,30 @@ namespace CLI parseFloat(tokens[offset + 1], argName + " y") ); } + + RendererType parseRendererType(const std::string& str) + { + if (str == "cpu" || str == "CPU") + return RendererType::CPU; + if (str == "gpu" || str == "GPU" || str == "vulkan" || str == "Vulkan") + return RendererType::GPU; + if (str == "metal" || str == "Metal") + return RendererType::Metal; + + std::cerr << "Warning: Unknown renderer type '" << str << "', using CPU renderer\n"; + return RendererType::CPU; + } + } + + const char* rendererTypeToString(RendererType type) + { + switch (type) + { + case RendererType::CPU: return "CPU"; + case RendererType::GPU: return "GPU (Vulkan)"; + case RendererType::Metal: return "Metal"; + default: return "Unknown"; + } } void printHelp() @@ -95,7 +121,18 @@ Light Parameters: --point-light Add point light (position + color + attenuation) +Model Parameters: + --model Model file path (OBJ, FBX, GLB, etc.) + --model-scale Model scale (default: 1 1 1) + --model-translate + Model translation (default: 0 0 0) + --model-rotate Model rotation in degrees (default: 0 0 0) + +Renderer Selection: + --renderer Renderer type: cpu, gpu, metal (default: cpu) + Other: + --headless Run without GUI (render and save to file) --help Show this help message Examples: @@ -105,6 +142,8 @@ Examples: RayTracer --point-light 0 5 0 1 0.5 0 0.5 RayTracer --fog-emission 0.1 0.2 0.3 --specular-coeff 0.5 --ambient-coeff 0.2 RayTracer -b 8 --bg-color 0.1 0.1 0.2 -o output.pgm + RayTracer --model models/bunny.obj --renderer cpu --headless -o bunny.pgm + RayTracer --model models/scene.fbx --model-scale 0.5 0.5 0.5 --renderer gpu )"; } @@ -304,6 +343,58 @@ Examples: options.pointLights.push_back(light); i += 7; } + else if (arg == "--model") + { + if (i + 1 >= args.size()) + { + std::cerr << "Error: --model requires a file path\n"; + return options; + } + options.modelPath = args[++i]; + } + else if (arg == "--model-scale") + { + if (i + 3 >= args.size()) + { + std::cerr << "Error: --model-scale requires 3 values (x y z)\n"; + return options; + } + options.modelScale = parseVec3(args, i + 1, "model-scale"); + i += 3; + } + else if (arg == "--model-translate") + { + if (i + 3 >= args.size()) + { + std::cerr << "Error: --model-translate requires 3 values (x y z)\n"; + return options; + } + options.modelTranslation = parseVec3(args, i + 1, "model-translate"); + i += 3; + } + else if (arg == "--model-rotate") + { + if (i + 3 >= args.size()) + { + std::cerr << "Error: --model-rotate requires 3 values (x y z)\n"; + return options; + } + options.modelRotation = parseVec3(args, i + 1, "model-rotate"); + i += 3; + } + else if (arg == "--renderer") + { + if (i + 1 >= args.size()) + { + std::cerr << "Error: --renderer requires a type (cpu, gpu, metal)\n"; + return options; + } + options.rendererType = parseRendererType(args[++i]); + } + else if (arg == "--headless") + { + options.headless = true; + } else { std::cerr << "Warning: Unknown argument: " << arg << "\n"; @@ -350,4 +441,67 @@ Examples: renderer->addPointLight(pointLight); } } + + glm::mat4 getModelTransform(const CLIOptions& opts) + { + // Build transformation matrix: translate * rotate * scale + glm::mat4 scale = glm::scale(glm::mat4(1.0f), opts.modelScale); + glm::mat4 rotationX = glm::rotate(glm::mat4(1.0f), glm::radians(opts.modelRotation.x), glm::vec3(1, 0, 0)); + glm::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(opts.modelRotation.y), glm::vec3(0, 1, 0)); + glm::mat4 rotationZ = glm::rotate(glm::mat4(1.0f), glm::radians(opts.modelRotation.z), glm::vec3(0, 0, 1)); + glm::mat4 rotation = rotationX * rotationY * rotationZ; + glm::mat4 translation = glm::translate(glm::mat4(1.0f), opts.modelTranslation); + + return translation * rotation * scale; + } + + bool saveImagePGM(const std::vector& imageData, uint32_t width, uint32_t height, const std::string& filepath) + { + if (imageData.empty() || width == 0 || height == 0) + { + std::cerr << "Error: Cannot save empty image\n"; + return false; + } + + if (imageData.size() < width * height * 3) + { + std::cerr << "Error: Image data size mismatch. Expected " << (width * height * 3) + << " floats, got " << imageData.size() << "\n"; + return false; + } + + std::ofstream file(filepath, std::ios::binary); + if (!file.is_open()) + { + std::cerr << "Error: Cannot open file for writing: " << filepath << "\n"; + return false; + } + + // PGM P5 format header + file << "P5\n"; + file << "#" << filepath << "\n"; // Comment line + file << width << " " << height << "\n"; + file << "255\n"; // Max val + + // Write pixel data + for (uint32_t i = 0; i < width * height * 3; ++i) + { + // Convert float [0, 1] to uint8_t [0, 255] + uint8_t val = static_cast(std::clamp(imageData[i], 0.0f, 1.0f) * 255.0f); + file.put(val); + } + + file.close(); + + if (file.good()) + { + std::cout << "Image saved to: " << filepath << "\n"; + return true; + } + else + { + std::cerr << "Error: Failed to write image to: " << filepath << "\n"; + return false; + } + } } diff --git a/src/util/CLI.h b/src/util/CLI.h index c6efb2a..733f9a5 100644 --- a/src/util/CLI.h +++ b/src/util/CLI.h @@ -5,6 +5,13 @@ #include #include +enum class RendererType +{ + CPU, + GPU, // Vulkan + Metal +}; + struct CLIOptions { // Render parameters @@ -33,6 +40,19 @@ struct CLIOptions std::vector directionalLights; std::vector pointLights; + // Model parameters + std::string modelPath; + glm::vec3 modelScale = glm::vec3(1, 1, 1); + glm::vec3 modelTranslation = glm::vec3(0, 0, 0); + glm::vec3 modelRotation = glm::vec3(0, 0, 0); // Euler angles in degrees + + // Renderer selection + RendererType rendererType = RendererType::CPU; + + // Headless mode (no GUI, render and exit) + bool headless = false; + + // Help flag bool showHelp = false; }; @@ -52,4 +72,13 @@ namespace CLI // Apply lights from CLIOptions to renderer void applyLights(Renderer* renderer, const CLIOptions& opts); + + // Generate model transform matrix from CLI options + glm::mat4 getModelTransform(const CLIOptions& opts); + + // Save image buffer to PGM file + bool saveImagePGM(const std::vector& imageData, uint32_t width, uint32_t height, const std::string& filepath); + + // Get renderer type as string + const char* rendererTypeToString(RendererType type); } -- 2.54.0