Implicit surface rendering via ray tracing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

230 lines
7.1 KiB

/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2022 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <iostream>
#include <string>
#include <variant>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include "nvprint.hpp"
static constexpr int MAX_LINE_WIDTH = 60;
namespace nvh {
//--------------------------------------------------------------------------------------------------
// Command line parser.
// std::string inFilename = "";
// bool printHelp = false;
// CommandLineParser args("Test Parser");
// args.addArgument({"-f", "--filename"}, &inFilename, "Input filename");
// args.addArgument({"-h", "--help"}, &printHelp, "Print Help");
// bool result = args.parse(argc, argv);
//
class CommandLineParser
{
public:
// These are the possible variables the options may point to. Bool and
// std::string are handled in a special way, all other values are parsed
// with a std::stringstream. This std::variant can be easily extended if
// the stream operator>> is overloaded. If not, you have to add a special
// case to the parse() method.
using Value = std::variant<int32_t*, uint32_t*, double*, float*, bool*, std::string*>;
// The description is printed as part of the help message.
CommandLineParser(const std::string& description)
: m_description(description)
{
}
void addArgument(std::vector<std::string> const& flags, Value const& value, std::string const& help)
{
m_arguments.emplace_back(Argument{flags, value, help});
}
// Prints the description given to the constructor and the help for each option.
void printHelp(std::ostream& os = std::cout) const
{
// Print the general description.
os << m_description << std::endl;
// Find the argument with the longest combined flag length (in order to align the help messages).
uint32_t maxFlagLength = 0;
for(auto const& argument : m_arguments)
{
uint32_t flagLength = 0;
for(auto const& flag : argument.m_flags)
{
// Plus comma and space.
flagLength += static_cast<uint32_t>(flag.size()) + 2;
}
maxFlagLength = std::max(maxFlagLength, flagLength);
}
// Now print each argument.
for(auto const& argument : m_arguments)
{
std::string flags;
for(auto const& flag : argument.m_flags)
{
flags += flag + ", ";
}
// Remove last comma and space and add padding according to the longest flags in order to align the help messages.
std::stringstream sstr;
sstr << std::left << std::setw(maxFlagLength) << flags.substr(0, flags.size() - 2);
// Print the help for each argument. This is a bit more involved since we do line wrapping for long descriptions.
size_t spacePos = 0;
size_t lineWidth = 0;
while(spacePos != std::string::npos)
{
size_t nextspacePos = argument.m_help.find_first_of(' ', spacePos + 1);
sstr << argument.m_help.substr(spacePos, nextspacePos - spacePos);
lineWidth += nextspacePos - spacePos;
spacePos = nextspacePos;
if(lineWidth > MAX_LINE_WIDTH)
{
os << sstr.str() << std::endl;
sstr = std::stringstream();
sstr << std::left << std::setw(maxFlagLength - 1) << " ";
lineWidth = 0;
}
}
}
}
// The command line arguments are traversed from start to end. That means,
// if an option is set multiple times, the last will be the one which is
// finally used. This call will throw a std::runtime_error if a value is
// missing for a given option. Unknown flags will cause a warning on
// std::cerr.
bool parse(int argc, char* argv[])
{
bool result = true;
// Skip the first argument (name of the program).
int i = 1;
while(i < argc)
{
// First we have to identify whether the value is separated by a space or a '='.
std::string flag(argv[i]);
std::string value;
bool valueIsSeparate = false;
// If there is an '=' in the flag, the part after the '=' is actually
// the value.
size_t equalPos = flag.find('=');
if(equalPos != std::string::npos)
{
value = flag.substr(equalPos + 1);
flag = flag.substr(0, equalPos);
}
// Else the following argument is the value.
else if(i + 1 < argc)
{
value = argv[i + 1];
valueIsSeparate = true;
}
// Search for an argument with the provided flag.
bool foundArgument = false;
for(auto const& argument : m_arguments)
{
if(std::find(argument.m_flags.begin(), argument.m_flags.end(), flag) != std::end(argument.m_flags))
{
foundArgument = true;
// In the case of booleans, the value is not needed.
if(std::holds_alternative<bool*>(argument.m_value))
{
if(!value.empty() && value != "true" && value != "false")
{
valueIsSeparate = false; // No value
}
*std::get<bool*>(argument.m_value) = (value != "false");
}
// In all other cases there must be a value.
else if(value.empty())
{
LOGE("Failed to parse command line arguments. Missing value for argument %s\n", flag.c_str());
return false;
}
// For a std::string, we take the entire value.
else if(std::holds_alternative<std::string*>(argument.m_value))
{
*std::get<std::string*>(argument.m_value) = value;
}
// In all other cases we use a std::stringstream to convert the value.
else
{
std::visit(
[&value](auto&& arg) {
std::stringstream sstr(value);
sstr >> *arg;
},
argument.m_value);
}
break;
}
}
// Print a warning if there was an unknown argument.
if(!foundArgument)
{
std::cerr << "Ignoring unknown command line argument \"" << flag << "\"." << std::endl;
result = false;
}
// Advance to the next flag.
++i;
// If the value was separated, we have to advance our index once more.
if(foundArgument && valueIsSeparate)
{
++i;
}
}
return result;
}
private:
struct Argument
{
std::vector<std::string> m_flags;
Value m_value;
std::string m_help;
};
std::string m_description;
std::vector<Argument> m_arguments;
};
} // namespace nvh