agent: the commandline parsing is done using a custom parser, use a standard library in

This commit is contained in:
orchestrator-bot
2026-08-09 17:12:54 +02:00
parent 5ea6fd3fae
commit 01caad8a28
5 changed files with 133 additions and 258 deletions
+2
View File
@@ -18,6 +18,7 @@ find_package(glm CONFIG REQUIRED)
find_package(Ktx CONFIG REQUIRED) find_package(Ktx CONFIG REQUIRED)
find_package(imgui CONFIG REQUIRED) find_package(imgui CONFIG REQUIRED)
find_package(slang CONFIG REQUIRED) find_package(slang CONFIG REQUIRED)
find_package(CLI11 CONFIG REQUIRED)
add_executable(RayTracer "") add_executable(RayTracer "")
target_include_directories(RayTracer PUBLIC src/) target_include_directories(RayTracer PUBLIC src/)
@@ -28,6 +29,7 @@ target_link_libraries(RayTracer PUBLIC GLEW::GLEW)
target_link_libraries(RayTracer PUBLIC glm::glm) target_link_libraries(RayTracer PUBLIC glm::glm)
target_link_libraries(RayTracer PUBLIC KTX::ktx) target_link_libraries(RayTracer PUBLIC KTX::ktx)
target_link_libraries(RayTracer PUBLIC slang::slang) target_link_libraries(RayTracer PUBLIC slang::slang)
target_link_libraries(RayTracer PUBLIC CLI11::CLI11)
if(APPLE) if(APPLE)
target_include_directories(RayTracer PUBLIC ${VCPKG_INSTALLED_DIR}/arm64-osx/include) target_include_directories(RayTracer PUBLIC ${VCPKG_INSTALLED_DIR}/arm64-osx/include)
+4 -4
View File
@@ -8,7 +8,7 @@
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
// Parse command-line arguments // Parse command-line arguments
CLIOptions cliOpts = CLI::parseArgs(argc, argv); CLIOptions cliOpts = AppCLI::parseArgs(argc, argv);
// If help was requested, just exit after printing // If help was requested, just exit after printing
if (cliOpts.showHelp) if (cliOpts.showHelp)
@@ -30,7 +30,7 @@ int main(int argc, const char* argv[])
} }
else else
{ {
CLI::applyLights(renderer.get(), cliOpts); AppCLI::applyLights(renderer.get(), cliOpts);
} }
renderer->addModels(ModelLoader::loadModel("../res/models/stanford-bunny.obj"), renderer->addModels(ModelLoader::loadModel("../res/models/stanford-bunny.obj"),
@@ -39,8 +39,8 @@ int main(int argc, const char* argv[])
renderer->generate(); renderer->generate();
// Create camera and render parameters from CLI options // Create camera and render parameters from CLI options
Camera camera = CLI::toCamera(cliOpts); Camera camera = AppCLI::toCamera(cliOpts);
RenderParameter render = CLI::toRenderParameter(cliOpts); RenderParameter render = AppCLI::toRenderParameter(cliOpts);
renderer->startRender(camera, render); renderer->startRender(camera, render);
+123 -251
View File
@@ -1,67 +1,9 @@
#include "CLI.h" #include "CLI.h"
#include <sstream> #include <CLI/CLI.hpp>
#include <iostream> #include <iostream>
#include <algorithm>
#include <cmath>
namespace CLI namespace AppCLI
{ {
namespace
{
float parseFloat(const std::string& str, const std::string& argName)
{
try
{
return std::stof(str);
}
catch (const std::exception& e)
{
std::cerr << "Error: Invalid float value for " << argName << ": " << str << "\n";
return 0.0f;
}
}
uint32_t parseUInt(const std::string& str, const std::string& argName)
{
try
{
return static_cast<uint32_t>(std::stoul(str));
}
catch (const std::exception& e)
{
std::cerr << "Error: Invalid unsigned integer value for " << argName << ": " << str << "\n";
return 0;
}
}
glm::vec3 parseVec3(const std::vector<std::string>& tokens, size_t offset, const std::string& argName)
{
if (offset + 3 > tokens.size())
{
std::cerr << "Error: " << argName << " requires 3 float values (x y z)\n";
return glm::vec3(0, 0, 0);
}
return glm::vec3(
parseFloat(tokens[offset], argName + " x"),
parseFloat(tokens[offset + 1], argName + " y"),
parseFloat(tokens[offset + 2], argName + " z")
);
}
glm::vec2 parseVec2(const std::vector<std::string>& tokens, size_t offset, const std::string& argName)
{
if (offset + 2 > tokens.size())
{
std::cerr << "Error: " << argName << " requires 2 float values\n";
return glm::vec2(0, 0);
}
return glm::vec2(
parseFloat(tokens[offset], argName + " x"),
parseFloat(tokens[offset + 1], argName + " y")
);
}
}
void printHelp() void printHelp()
{ {
std::cout << R"( std::cout << R"(
@@ -69,7 +11,7 @@ Usage: RayTracer [options]
Render Parameters: Render Parameters:
-w, --width <int> Image width (default: 1920) -w, --width <int> Image width (default: 1920)
-h, --height <int> Image height (default: 1080) --height <int> Image height (default: 1080)
-s, --samples <int> Number of samples per pixel (default: 10000) -s, --samples <int> Number of samples per pixel (default: 10000)
-b, --bounces <int> Maximum ray bounces (default: 4) -b, --bounces <int> Maximum ray bounces (default: 4)
--bg-color <r> <g> <b> Background/sky color (default: 0.05 0.05 0.1) --bg-color <r> <g> <b> Background/sky color (default: 0.05 0.05 0.1)
@@ -96,10 +38,10 @@ Light Parameters:
Add point light (position + color + attenuation) Add point light (position + color + attenuation)
Other: Other:
--help Show this help message -h, --help Show this help message
Examples: Examples:
RayTracer -w 1280 -h 720 -s 1000 RayTracer -w 1280 --height 720 -s 1000
RayTracer --cam-pos 3 2 3 --cam-focal 0.5 RayTracer --cam-pos 3 2 3 --cam-focal 0.5
RayTracer --dir-light -0.4 -0.3 -0.2 1 1 1 RayTracer --dir-light -0.4 -0.3 -0.2 1 1 1
RayTracer --point-light 0 5 0 1 0.5 0 0.5 RayTracer --point-light 0 5 0 1 0.5 0 0.5
@@ -111,205 +53,135 @@ Examples:
CLIOptions parseArgs(int argc, const char* argv[]) CLIOptions parseArgs(int argc, const char* argv[])
{ {
CLIOptions options; CLIOptions options;
std::vector<std::string> args; ::CLI::App app{"RayTracer - A Path Tracing Renderer"};
for (int i = 1; i < argc; ++i) app.set_help_flag("-h,--help", "Show this help message");
// Render parameters
app.add_option_group("Render Parameters")->add_option(
"-w,--width", options.width, "Image width");
app.add_option("--height", options.height, "Image height");
app.add_option("-s,--samples", options.numSamples, "Number of samples per pixel");
app.add_option("-b,--bounces", options.maxBounces, "Maximum ray bounces");
app.add_option("--throughput", options.throughputThreshold, "Minimum throughput threshold");
app.add_option("-o,--output", options.outputFile, "Output image file path");
// Background color (3 floats)
std::vector<float> bgColor(3, 0.0f);
app.add_option("--bg-color", bgColor, "Background/sky color (r g b)")
->expected(3);
// Camera parameters
std::vector<float> camPos(3, 0.0f);
app.add_option("--cam-pos", camPos, "Camera position (x y z)")
->expected(3);
std::vector<float> camTarget(3, 0.0f);
app.add_option("--cam-target", camTarget, "Camera target (x y z)")
->expected(3);
app.add_option("--cam-focal", options.camFocalLength, "Focal length");
app.add_option("--cam-aperture", options.camAperture, "Aperture size");
app.add_option("--cam-distance", options.camDistance, "Subject distance S_O");
std::vector<float> camSensor(2, 0.0f);
app.add_option("--cam-sensor", camSensor, "Sensor size (w h)")
->expected(2);
// Camera shading parameters
std::vector<float> fogEmission(3, 0.0f);
app.add_option("--fog-emission", fogEmission, "Fog emission color (r g b)")
->expected(3);
app.add_option("--specular-coeff", options.specularCoeff, "Specular coefficient ks");
app.add_option("--ambient-coeff", options.ambientCoeff, "Ambient coefficient ka");
// Light parameters - using TakeAll policy to allow multiple occurrences
std::vector<std::vector<float>> dirLightArgs;
app.add_option("--dir-light", dirLightArgs, "Add directional light (dx dy dz r g b)")
->expected(6)
->multi_option_policy(::CLI::MultiOptionPolicy::TakeAll);
std::vector<std::vector<float>> pointLightArgs;
app.add_option("--point-light", pointLightArgs, "Add point light (x y z r g b attenuation)")
->expected(7)
->multi_option_policy(::CLI::MultiOptionPolicy::TakeAll);
// Parse arguments
bool helpRequested = false;
try
{ {
args.push_back(argv[i]); app.parse(argc, argv);
}
catch (const ::CLI::CallForHelp& e)
{
helpRequested = true;
}
catch (const ::CLI::CallForAllHelp& e)
{
helpRequested = true;
}
catch (const ::CLI::ParseError& e)
{
std::cerr << e.what() << std::endl;
return options;
} }
size_t i = 0; // Check if help was requested
while (i < args.size()) if (helpRequested || app.get_help_ptr()->count() > 0)
{
const std::string& arg = args[i];
if (arg == "--help")
{ {
options.showHelp = true; options.showHelp = true;
printHelp(); printHelp();
return options; return options;
} }
else if (arg == "--width" || arg == "-w")
// Process background color
if (bgColor.size() == 3)
{ {
if (i + 1 >= args.size()) options.backgroundColor = glm::vec3(bgColor[0], bgColor[1], bgColor[2]);
{
std::cerr << "Error: --width requires a value\n";
return options;
}
options.width = parseUInt(args[++i], "width");
}
else if (arg == "--height" || arg == "-h")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --height requires a value\n";
return options;
}
options.height = parseUInt(args[++i], "height");
}
else if (arg == "--samples" || arg == "-s")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --samples requires a value\n";
return options;
}
options.numSamples = parseUInt(args[++i], "samples");
}
else if (arg == "--bounces" || arg == "-b")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --bounces requires a value\n";
return options;
}
options.maxBounces = parseUInt(args[++i], "bounces");
}
else if (arg == "--bg-color")
{
if (i + 3 >= args.size())
{
std::cerr << "Error: --bg-color requires 3 values (r g b)\n";
return options;
}
options.backgroundColor = parseVec3(args, i + 1, "bg-color");
i += 3;
}
else if (arg == "--throughput")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --throughput requires a value\n";
return options;
}
options.throughputThreshold = parseFloat(args[++i], "throughput");
}
else if (arg == "--output" || arg == "-o")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --output requires a file path\n";
return options;
}
options.outputFile = args[++i];
}
else if (arg == "--cam-pos")
{
if (i + 3 >= args.size())
{
std::cerr << "Error: --cam-pos requires 3 values (x y z)\n";
return options;
}
options.camPosition = parseVec3(args, i + 1, "cam-pos");
i += 3;
}
else if (arg == "--cam-target")
{
if (i + 3 >= args.size())
{
std::cerr << "Error: --cam-target requires 3 values (x y z)\n";
return options;
}
options.camTarget = parseVec3(args, i + 1, "cam-target");
i += 3;
}
else if (arg == "--cam-focal")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --cam-focal requires a value\n";
return options;
}
options.camFocalLength = parseFloat(args[++i], "cam-focal");
}
else if (arg == "--cam-aperture")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --cam-aperture requires a value\n";
return options;
}
options.camAperture = parseFloat(args[++i], "cam-aperture");
}
else if (arg == "--cam-distance")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --cam-distance requires a value\n";
return options;
}
options.camDistance = parseFloat(args[++i], "cam-distance");
}
else if (arg == "--cam-sensor")
{
if (i + 2 >= args.size())
{
std::cerr << "Error: --cam-sensor requires 2 values (w h)\n";
return options;
}
options.camSensorSize = parseVec2(args, i + 1, "cam-sensor");
i += 2;
}
else if (arg == "--fog-emission")
{
if (i + 3 >= args.size())
{
std::cerr << "Error: --fog-emission requires 3 values (r g b)\n";
return options;
}
options.fogEmission = parseVec3(args, i + 1, "fog-emission");
i += 3;
}
else if (arg == "--specular-coeff")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --specular-coeff requires a value\n";
return options;
}
options.specularCoeff = parseFloat(args[++i], "specular-coeff");
}
else if (arg == "--ambient-coeff")
{
if (i + 1 >= args.size())
{
std::cerr << "Error: --ambient-coeff requires a value\n";
return options;
}
options.ambientCoeff = parseFloat(args[++i], "ambient-coeff");
}
else if (arg == "--dir-light")
{
if (i + 6 > args.size())
{
std::cerr << "Error: --dir-light requires 6 values (dx dy dz r g b)\n";
return options;
}
DirectionalLight light;
light.direction = parseVec3(args, i + 1, "dir-light direction");
light.color = parseVec3(args, i + 4, "dir-light color");
options.directionalLights.push_back(light);
i += 6;
}
else if (arg == "--point-light")
{
if (i + 7 > args.size())
{
std::cerr << "Error: --point-light requires 7 values (x y z r g b attenuation)\n";
return options;
}
PointLight light;
light.position = parseVec3(args, i + 1, "point-light position");
light.color = parseVec3(args, i + 4, "point-light color");
light.attenuation = parseFloat(args[i + 7], "point-light attenuation");
options.pointLights.push_back(light);
i += 7;
}
else
{
std::cerr << "Warning: Unknown argument: " << arg << "\n";
} }
++i; // Process camera position
if (camPos.size() == 3)
{
options.camPosition = glm::vec3(camPos[0], camPos[1], camPos[2]);
}
// Process camera target
if (camTarget.size() == 3)
{
options.camTarget = glm::vec3(camTarget[0], camTarget[1], camTarget[2]);
}
// Process camera sensor size
if (camSensor.size() == 2)
{
options.camSensorSize = glm::vec2(camSensor[0], camSensor[1]);
}
// Process fog emission
if (fogEmission.size() == 3)
{
options.fogEmission = glm::vec3(fogEmission[0], fogEmission[1], fogEmission[2]);
}
// Process directional lights
for (const auto& args : dirLightArgs)
{
if (args.size() == 6)
{
DirectionalLight light;
light.direction = glm::vec3(args[0], args[1], args[2]);
light.color = glm::vec3(args[3], args[4], args[5]);
options.directionalLights.push_back(light);
}
}
// Process point lights
for (const auto& args : pointLightArgs)
{
if (args.size() == 7)
{
PointLight light;
light.position = glm::vec3(args[0], args[1], args[2]);
light.color = glm::vec3(args[3], args[4], args[5]);
light.attenuation = args[6];
options.pointLights.push_back(light);
}
} }
return options; return options;
+1 -1
View File
@@ -36,7 +36,7 @@ struct CLIOptions
bool showHelp = false; bool showHelp = false;
}; };
namespace CLI namespace AppCLI
{ {
// Parse command-line arguments and return CLIOptions // Parse command-line arguments and return CLIOptions
CLIOptions parseArgs(int argc, const char* argv[]); CLIOptions parseArgs(int argc, const char* argv[]);
+1
View File
@@ -5,6 +5,7 @@
"features": [ "glfw-binding", "opengl3-binding", "metal-binding" ] "features": [ "glfw-binding", "opengl3-binding", "metal-binding" ]
}, },
"assimp", "assimp",
"cli11",
"ktx", "ktx",
"glfw3", "glfw3",
"glew", "glew",