220 lines
6 KiB
C++
220 lines
6 KiB
C++
#include "GodRaysSampler.h"
|
|
|
|
#include "GodRaysDefinition.h"
|
|
#include "AtmosphereDefinition.h"
|
|
#include "SoftwareRenderer.h"
|
|
#include "Vector3.h"
|
|
#include "SpaceSegment.h"
|
|
#include "GodRaysResult.h"
|
|
#include "LightingManager.h"
|
|
#include "FloatNode.h"
|
|
#include "LightStatus.h"
|
|
#include "Scenery.h"
|
|
#include "TerrainDefinition.h"
|
|
#include "CloudsRenderer.h"
|
|
#include "Interpolation.h"
|
|
#include <cmath>
|
|
|
|
GodRaysSampler::GodRaysSampler()
|
|
{
|
|
enabled = true;
|
|
bounds = new SpaceSegment();
|
|
definition = new GodRaysDefinition(NULL);
|
|
camera_location = new Vector3(0, 0, 0);
|
|
lighting = NULL;
|
|
low_altitude = -1.0;
|
|
high_altitude = 1.0;
|
|
sampling_step = 1.0;
|
|
max_length = 1.0;
|
|
data = new double[1];
|
|
setQuality(0.5);
|
|
}
|
|
|
|
GodRaysSampler::~GodRaysSampler()
|
|
{
|
|
delete definition;
|
|
delete bounds;
|
|
delete camera_location;
|
|
delete[] data;
|
|
}
|
|
|
|
void GodRaysSampler::prepare(SoftwareRenderer *renderer)
|
|
{
|
|
setCameraLocation(renderer->getCameraLocation(VECTOR_ZERO));
|
|
setLighting(renderer->getLightingManager());
|
|
setAltitudes(renderer->getScenery()->getTerrain()->getHeightInfo().min_height, renderer->getCloudsRenderer()->getHighestAltitude());
|
|
renderer->getScenery()->getAtmosphere()->childGodRays()->copy(definition);
|
|
reset();
|
|
}
|
|
|
|
void GodRaysSampler::reset()
|
|
{
|
|
*bounds = SpaceSegment(Vector3(camera_location->x - max_length, low_altitude, camera_location->z - max_length),
|
|
Vector3(camera_location->x + max_length, high_altitude, camera_location->z + max_length));
|
|
samples_x = round(bounds->getXDiff() / sampling_step) + 1;
|
|
samples_y = round(bounds->getYDiff() / sampling_step) + 1;
|
|
samples_z = round(bounds->getZDiff() / sampling_step) + 1;
|
|
|
|
long n = samples_x * samples_y * samples_z;
|
|
delete[] data;
|
|
data = new double[n];
|
|
for (long i = 0; i < n; i++)
|
|
{
|
|
data[i] = -1.0;
|
|
}
|
|
}
|
|
|
|
void GodRaysSampler::setEnabled(bool enabled)
|
|
{
|
|
this->enabled = enabled;
|
|
}
|
|
|
|
void GodRaysSampler::setLighting(LightingManager *manager)
|
|
{
|
|
this->lighting = manager;
|
|
}
|
|
|
|
void GodRaysSampler::setQuality(double factor)
|
|
{
|
|
setQuality(5.0 / (factor * 8.0 + 1.0), 100.0, 5.0 / (factor * 80.0 + 1.0));
|
|
reset();
|
|
}
|
|
|
|
void GodRaysSampler::setQuality(double sampling_step, double max_length, double walk_step)
|
|
{
|
|
this->sampling_step = sampling_step;
|
|
this->max_length = max_length;
|
|
this->walk_step = walk_step;
|
|
}
|
|
|
|
void GodRaysSampler::setCameraLocation(const Vector3 &location)
|
|
{
|
|
*camera_location = location;
|
|
}
|
|
|
|
void GodRaysSampler::setAltitudes(double low, double high)
|
|
{
|
|
low_altitude = low;
|
|
high_altitude = high;
|
|
}
|
|
|
|
void GodRaysSampler::getSamples(int *x, int *y, int *z) const
|
|
{
|
|
*x = samples_x;
|
|
*y = samples_y;
|
|
*z = samples_z;
|
|
}
|
|
|
|
Color GodRaysSampler::getRawLight(const Vector3 &location, bool filtered) const
|
|
{
|
|
if (lighting)
|
|
{
|
|
LightStatus status(lighting, location, *camera_location, filtered);
|
|
lighting->fillStatus(status, location);
|
|
return status.getSum();
|
|
}
|
|
else
|
|
{
|
|
return COLOR_TRANSPARENT;
|
|
}
|
|
}
|
|
|
|
double GodRaysSampler::getCachedLight(const Vector3 &location)
|
|
{
|
|
double x = location.x - bounds->getStart().x;
|
|
double y = location.y - bounds->getStart().y;
|
|
double z = location.z - bounds->getStart().z;
|
|
|
|
int ix = (int)floor(x / sampling_step);
|
|
int iy = (int)floor(y / sampling_step);
|
|
int iz = (int)floor(z / sampling_step);
|
|
|
|
// Check cache limits
|
|
if (ix < 0 || ix >= samples_x - 1 || iy < 0 || iy >= samples_y - 1 || iz < 0 || iz >= samples_z - 1)
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
// Hit cache
|
|
double p[8] = {
|
|
getCache(ix, iy, iz),
|
|
getCache(ix + 1, iy, iz),
|
|
getCache(ix + 1, iy + 1, iz),
|
|
getCache(ix, iy + 1, iz),
|
|
getCache(ix, iy, iz + 1),
|
|
getCache(ix + 1, iy, iz + 1),
|
|
getCache(ix + 1, iy + 1, iz + 1),
|
|
getCache(ix, iy + 1, iz + 1)
|
|
};
|
|
return Interpolation::trilinear(p,
|
|
(x - sampling_step * double(ix)) / sampling_step,
|
|
(y - sampling_step * double(iy)) / sampling_step,
|
|
(z - sampling_step * double(iz)) / sampling_step);
|
|
}
|
|
|
|
GodRaysResult GodRaysSampler::getResult(const SpaceSegment &segment)
|
|
{
|
|
Vector3 step = segment.getEnd().sub(segment.getStart());
|
|
double max_length = step.getNorm();
|
|
step = step.normalize().scale(walk_step);
|
|
Vector3 walker = segment.getStart();
|
|
double travelled = 0.0;
|
|
double inside = 0.0;
|
|
|
|
if (max_length > this->max_length)
|
|
{
|
|
max_length = this->max_length;
|
|
}
|
|
|
|
while (travelled < max_length)
|
|
{
|
|
double light = getCachedLight(walker);
|
|
|
|
inside += light * walk_step;
|
|
|
|
walker = walker.add(step);
|
|
travelled += walk_step;
|
|
}
|
|
|
|
return GodRaysResult(inside, travelled);
|
|
}
|
|
|
|
Color GodRaysSampler::apply(const Color &raw, const Color &atmosphered, const Vector3 &location)
|
|
{
|
|
if (enabled)
|
|
{
|
|
GodRaysResult result = getResult(SpaceSegment(*camera_location, location));
|
|
|
|
GodRaysResult::GodRaysParams params;
|
|
params.penetration = definition->propPenetration()->getValue();
|
|
params.resistance = definition->propResistance()->getValue();
|
|
params.boost = definition->propBoost()->getValue();
|
|
|
|
return result.apply(raw, atmosphered, params);
|
|
}
|
|
else
|
|
{
|
|
return atmosphered;
|
|
}
|
|
}
|
|
|
|
inline double GodRaysSampler::getCache(int x, int y, int z)
|
|
{
|
|
double *cache = data + z * samples_x * samples_y + y * samples_x + x;
|
|
if (*cache < 0.0)
|
|
{
|
|
Vector3 location = Vector3(bounds->getStart().x + sampling_step * (double)x,
|
|
bounds->getStart().y + sampling_step * (double)y,
|
|
bounds->getStart().z + sampling_step * (double)z);
|
|
double unfiltered_power = getRawLight(location, false).getPower();
|
|
if (unfiltered_power == 0.0)
|
|
{
|
|
*cache = 1.0;
|
|
}
|
|
else
|
|
{
|
|
*cache = getRawLight(location, true).getPower() / unfiltered_power;
|
|
}
|
|
}
|
|
return *cache;
|
|
}
|