<commit_before>#include "Common.hpp"
#include "Config.hpp"
#include "resources/ImageUtilities.hpp"

/**
 	\defgroup AtmosphericScatteringEstimator Atmospheric Scattering Estimator
 	\brief Real-time atmospheric scattering preprocess.
 	\see AtmosphericScattering
 	\ingroup Tools
*/

/** \brief Configuration for the atmospheric scattering precomputations.
 	\ingroup AtmosphericScatteringEstimator
 */
class AtmosphericScatteringConfig : public Config {
public:
	
	/** Initialize a new config object, parsing the input arguments and filling the attributes with their values.
	 	\param argv the raw input arguments
	 */
	AtmosphericScatteringConfig(const std::vector<std::string> & argv) : Config(argv) {
		processArguments();
	}
	
	/**
	 Read the internal (key, [values]) populated dictionary, and transfer their values to the configuration attributes.
	 */
	void processArguments(){
		
		for(const auto & arg : _rawArguments){
			const std::string key = arg.first;
			const std::vector<std::string> & values = arg.second;
			
			if(key == "output-path"){
				outputPath = values[0];
			} else if(key == "samples"){
				samples = std::stoi(values[0]);
			}  else if(key == "resolution"){
				resolution = std::stoi(values[0]);
			}
		}
		
	}
	
public:
	
	std::string outputPath = "./scattering.exr"; ///< Lookup table output path.
	
	unsigned int samples = 256; ///< Number of samples for iterative sampling.

	unsigned int resolution = 512; ///< Output image resolution.
};


/** Perform intersection test with a centered sphere.
 \param rayOrigin the ray origin position with respect to the center of the sphere.
 \param rayDir the ray normalized direction.
 \param radius the radius of the sphere.
 \param roots if the ray intersects the sphere, stores the two roots of the associated polynomial, such that roots[0]<=roots[1].
 \return a boolean denoting if the ray intersected the sphere.
 \warning The intersection can be behind the viewer (ie in the opposite orientation along the ray direction).
 \ingroup AtmosphericScatteringEstimator
 */
bool intersects(const glm::vec3 & rayOrigin, const glm::vec3 & rayDir, float radius, glm::vec2 & roots){
	float a = glm::dot(rayDir,rayDir);
	float b = glm::dot(rayOrigin, rayDir);
	float c = glm::dot(rayOrigin, rayOrigin) - radius*radius;
	float delta = b*b - a*c;
	// No intersection if the polynome has no real roots.
	if(delta < 0.0){
		return false;
	}
	// If it intersects, return the two roots.
	float dsqrt = sqrt(delta);
	roots = (-b+glm::vec2(-dsqrt,dsqrt))/a;
	return true;
}


/**
 Compute a scattering lookup table for real-time atmosphere rendering and saves it on disk.
 \param argc the number of input arguments.
 \param argv a pointer to the raw input arguments.
 \return a general error code.
 \ingroup AtmosphericScatteringEstimator
 */
int main(int argc, char** argv) {
	
	// First, init/parse/load configuration.
	AtmosphericScatteringConfig config(std::vector<std::string>(argv, argv+argc));
	
	if(config.outputPath.empty()){
		Log::Error() << Log::Utilities << "Need an output path." << std::endl;
		return 3;
	}
	
	Log::Info() << Log::Utilities << "Generating scattering lookup table." << std::endl;
	
	// Parameters.
	const float groundRadius = 6371e3;
	const float topRadius = 6471e3;
	const glm::vec3 kRayleigh = glm::vec3(5.5e-6, 13.0e-6, 22.4e-6);
	const float heightRayleigh = 8000.0;
	const float heightMie = 1200.0;
	const float kMie = 21e-6;
	
	Image transmittanceTable(config.resolution, config.resolution, 3);
	const unsigned int samplesCount = config.samples;
	
	for(size_t y = 0; y < config.resolution; ++y){
		for(size_t x = 0; x < config.resolution; ++x){
			// Move to 0,1.
			// No need to take care of the 0.5 shift as we are working with indices
			const float xf = float(x) / (config.resolution - 1.0);
			const float yf = float(y) / (config.resolution - 1.0);
			// Position and ray direction.
			// x becomes the height
			// y become the cosine
			const glm::vec3 currPos = glm::vec3(0.0f, (topRadius - groundRadius) * xf + groundRadius, 0.0f);
			const float cosA = 2.0f * yf - 1.0f;
			const float sinA = sqrt(1.0 - cosA*cosA);
			const glm::vec3 sunDir = -glm::normalize(glm::vec3(sinA, cosA, 0.0f));
			// Check when the ray leaves the atmosphere.
			glm::vec2 interSecondTop;
			const bool didHitSecondTop = intersects(currPos, sunDir, topRadius, interSecondTop);
			// Divide the distance traveled through the atmosphere in samplesCount parts.
			const float secondStepSize = didHitSecondTop ? interSecondTop.y/samplesCount : 0.0f;
				
			// Accumulate optical distance for both scatterings.
			float rayleighSecondDist = 0.0;
			float mieSecondDist = 0.0;
			
			// March along the secondary ray.
			for(unsigned int j = 0; j < samplesCount; ++j){
				// Compute the current position along the ray, ...
				const glm::vec3 currSecondPos = currPos + (j+0.5f) * secondStepSize * sunDir;
				// ...and its distance to the ground (as we are in planet space).
				const float currSecondHeight = glm::length(currSecondPos) - groundRadius;
				// Compute density based on the characteristic height of Rayleigh and Mie.
				const float rayleighSecondStep = exp(-currSecondHeight/heightRayleigh) * secondStepSize;
				const float mieSecondStep = exp(-currSecondHeight/heightMie) * secondStepSize;
				// Accumulate optical distances.
				rayleighSecondDist += rayleighSecondStep;
				mieSecondDist += mieSecondStep;
			}
			
			// Compute associated attenuation.
			const glm::vec3 secondaryAttenuation = exp(-(kMie * (mieSecondDist) + kRayleigh * (rayleighSecondDist)));
			const unsigned int pixelPos = config.resolution*y+x;
			transmittanceTable.pixels[3*pixelPos+0] = secondaryAttenuation[0];
			transmittanceTable.pixels[3*pixelPos+1] = secondaryAttenuation[1];
			transmittanceTable.pixels[3*pixelPos+2] = secondaryAttenuation[2];
			
		}
	}
	
	ImageUtilities::saveHDRImage(config.outputPath,transmittanceTable, true);
	
	Log::Info() << Log::Utilities << "Done." << std::endl;
	
	return 0;
}

<commit_msg>Documentation: tweak tools grouping.<commit_after>#include "Common.hpp"
#include "Config.hpp"
#include "resources/ImageUtilities.hpp"

/** \brief Configuration for the atmospheric scattering precomputations.
 	\ingroup AtmosphericScattering
 */
class AtmosphericScatteringConfig : public Config {
public:
	
	/** Initialize a new config object, parsing the input arguments and filling the attributes with their values.
	 	\param argv the raw input arguments
	 */
	AtmosphericScatteringConfig(const std::vector<std::string> & argv) : Config(argv) {
		processArguments();
	}
	
	/**
	 Read the internal (key, [values]) populated dictionary, and transfer their values to the configuration attributes.
	 */
	void processArguments(){
		
		for(const auto & arg : _rawArguments){
			const std::string key = arg.first;
			const std::vector<std::string> & values = arg.second;
			
			if(key == "output-path"){
				outputPath = values[0];
			} else if(key == "samples"){
				samples = std::stoi(values[0]);
			}  else if(key == "resolution"){
				resolution = std::stoi(values[0]);
			}
		}
		
	}
	
public:
	
	std::string outputPath = "./scattering.exr"; ///< Lookup table output path.
	
	unsigned int samples = 256; ///< Number of samples for iterative sampling.

	unsigned int resolution = 512; ///< Output image resolution.
};


/** Perform intersection test with a centered sphere.
 \param rayOrigin the ray origin position with respect to the center of the sphere.
 \param rayDir the ray normalized direction.
 \param radius the radius of the sphere.
 \param roots if the ray intersects the sphere, stores the two roots of the associated polynomial, such that roots[0]<=roots[1].
 \return a boolean denoting if the ray intersected the sphere.
 \warning The intersection can be behind the viewer (ie in the opposite orientation along the ray direction).
 \ingroup AtmosphericScattering
 */
bool intersects(const glm::vec3 & rayOrigin, const glm::vec3 & rayDir, float radius, glm::vec2 & roots){
	float a = glm::dot(rayDir,rayDir);
	float b = glm::dot(rayOrigin, rayDir);
	float c = glm::dot(rayOrigin, rayOrigin) - radius*radius;
	float delta = b*b - a*c;
	// No intersection if the polynome has no real roots.
	if(delta < 0.0){
		return false;
	}
	// If it intersects, return the two roots.
	float dsqrt = sqrt(delta);
	roots = (-b+glm::vec2(-dsqrt,dsqrt))/a;
	return true;
}


/**
 Compute a scattering lookup table for real-time atmosphere rendering and saves it on disk.
 \param argc the number of input arguments.
 \param argv a pointer to the raw input arguments.
 \return a general error code.
 \ingroup AtmosphericScattering
 */
int preprocess(int argc, char** argv) {
	
	// First, init/parse/load configuration.
	AtmosphericScatteringConfig config(std::vector<std::string>(argv, argv+argc));
	
	if(config.outputPath.empty()){
		Log::Error() << Log::Utilities << "Need an output path." << std::endl;
		return 3;
	}
	
	Log::Info() << Log::Utilities << "Generating scattering lookup table." << std::endl;
	
	// Parameters.
	const float groundRadius = 6371e3;
	const float topRadius = 6471e3;
	const glm::vec3 kRayleigh = glm::vec3(5.5e-6, 13.0e-6, 22.4e-6);
	const float heightRayleigh = 8000.0;
	const float heightMie = 1200.0;
	const float kMie = 21e-6;
	
	Image transmittanceTable(config.resolution, config.resolution, 3);
	const unsigned int samplesCount = config.samples;
	
	for(size_t y = 0; y < config.resolution; ++y){
		for(size_t x = 0; x < config.resolution; ++x){
			// Move to 0,1.
			// No need to take care of the 0.5 shift as we are working with indices
			const float xf = float(x) / (config.resolution - 1.0);
			const float yf = float(y) / (config.resolution - 1.0);
			// Position and ray direction.
			// x becomes the height
			// y become the cosine
			const glm::vec3 currPos = glm::vec3(0.0f, (topRadius - groundRadius) * xf + groundRadius, 0.0f);
			const float cosA = 2.0f * yf - 1.0f;
			const float sinA = sqrt(1.0 - cosA*cosA);
			const glm::vec3 sunDir = -glm::normalize(glm::vec3(sinA, cosA, 0.0f));
			// Check when the ray leaves the atmosphere.
			glm::vec2 interSecondTop;
			const bool didHitSecondTop = intersects(currPos, sunDir, topRadius, interSecondTop);
			// Divide the distance traveled through the atmosphere in samplesCount parts.
			const float secondStepSize = didHitSecondTop ? interSecondTop.y/samplesCount : 0.0f;
				
			// Accumulate optical distance for both scatterings.
			float rayleighSecondDist = 0.0;
			float mieSecondDist = 0.0;
			
			// March along the secondary ray.
			for(unsigned int j = 0; j < samplesCount; ++j){
				// Compute the current position along the ray, ...
				const glm::vec3 currSecondPos = currPos + (j+0.5f) * secondStepSize * sunDir;
				// ...and its distance to the ground (as we are in planet space).
				const float currSecondHeight = glm::length(currSecondPos) - groundRadius;
				// Compute density based on the characteristic height of Rayleigh and Mie.
				const float rayleighSecondStep = exp(-currSecondHeight/heightRayleigh) * secondStepSize;
				const float mieSecondStep = exp(-currSecondHeight/heightMie) * secondStepSize;
				// Accumulate optical distances.
				rayleighSecondDist += rayleighSecondStep;
				mieSecondDist += mieSecondStep;
			}
			
			// Compute associated attenuation.
			const glm::vec3 secondaryAttenuation = exp(-(kMie * (mieSecondDist) + kRayleigh * (rayleighSecondDist)));
			const unsigned int pixelPos = config.resolution*y+x;
			transmittanceTable.pixels[3*pixelPos+0] = secondaryAttenuation[0];
			transmittanceTable.pixels[3*pixelPos+1] = secondaryAttenuation[1];
			transmittanceTable.pixels[3*pixelPos+2] = secondaryAttenuation[2];
			
		}
	}
	
	ImageUtilities::saveHDRImage(config.outputPath,transmittanceTable, true);
	
	Log::Info() << Log::Utilities << "Done." << std::endl;
	
	return 0;
}

/// \cond SKIP_DOXYGEN
/** The main function of the preprocessing utility.
 \param argc the number of input arguments.
 \param argv a pointer to the raw input arguments.
 \return a general error code.
 \ingroup AtmosphericScattering
 */
int main(int argc, char* argv[]) {
	return preprocess(argc, argv);
}
/// \endcond
{'fraction_non_alphanumeric': 0.1157201646090535, 'fraction_numerical': 0.016954732510288065, 'mean_word_length': 3.8604, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1877800', 'n_tokens_mistral': 4229, 'n_tokens_neox': 3695, 'n_words': 1210}