2013-12-01 18:24:53 +00:00
|
|
|
#include "CloudBasicLayerRenderer.h"
|
|
|
|
|
2015-12-08 23:32:29 +00:00
|
|
|
#include <cassert>
|
|
|
|
#include <cmath>
|
2016-01-10 16:14:54 +00:00
|
|
|
#include <algorithm>
|
2013-12-01 18:24:53 +00:00
|
|
|
#include "CloudLayerDefinition.h"
|
|
|
|
#include "SoftwareRenderer.h"
|
|
|
|
#include "NoiseGenerator.h"
|
|
|
|
#include "Curve.h"
|
|
|
|
#include "AtmosphereRenderer.h"
|
2013-12-08 19:54:34 +00:00
|
|
|
#include "AtmosphereResult.h"
|
|
|
|
#include "LightComponent.h"
|
2013-12-04 21:52:18 +00:00
|
|
|
#include "clouds/BaseCloudsModel.h"
|
|
|
|
#include "SurfaceMaterial.h"
|
2015-09-13 16:58:11 +00:00
|
|
|
#include "Logs.h"
|
2015-10-15 15:52:03 +00:00
|
|
|
#include "FloatNode.h"
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
struct CloudSegment {
|
2013-12-01 18:24:53 +00:00
|
|
|
Vector3 start;
|
|
|
|
Vector3 end;
|
|
|
|
double length;
|
2015-09-13 16:58:11 +00:00
|
|
|
};
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
CloudBasicLayerRenderer::CloudBasicLayerRenderer(SoftwareRenderer *parent) : BaseCloudLayerRenderer(parent) {
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
static inline double _getDistanceToBorder(BaseCloudsModel *model, const Vector3 &position) {
|
2013-12-04 21:52:18 +00:00
|
|
|
return model->getDensity(position);
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Go through the cloud layer to find segments (parts of the lookup that are inside the cloud).
|
|
|
|
*
|
2016-01-03 18:21:23 +00:00
|
|
|
* definition - The cloud layer
|
|
|
|
* renderer - The renderer environment
|
|
|
|
* start - Start position of the lookup (already optimized)
|
|
|
|
* direction - Normalized direction of the lookup
|
|
|
|
* detail - Level of noise detail required
|
|
|
|
* max_segments - Maximum number of segments to collect
|
|
|
|
* max_inside_length - Maximum length to spend inside the cloud
|
|
|
|
* max_total_length - Maximum lookup length
|
|
|
|
* inside_length - Resulting length inside cloud (sum of all segments length)
|
|
|
|
* total_length - Resulting lookup length
|
|
|
|
* out_segments - Allocated space to fill found segments
|
|
|
|
*
|
|
|
|
* Returns the number of segments found.
|
2013-12-01 18:24:53 +00:00
|
|
|
*/
|
2015-11-09 21:30:46 +00:00
|
|
|
int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3 &start, const Vector3 &direction,
|
|
|
|
int max_segments, double max_inside_length, double max_total_length,
|
|
|
|
double *inside_length, double *total_length, CloudSegment *out_segments) {
|
2013-12-17 22:01:36 +00:00
|
|
|
double ymin, ymax;
|
2013-12-01 18:24:53 +00:00
|
|
|
int inside, segment_count;
|
|
|
|
double current_total_length, current_inside_length;
|
2013-12-18 21:09:35 +00:00
|
|
|
double step_length, segment_length;
|
2013-12-20 16:30:27 +00:00
|
|
|
double min_step, max_step;
|
|
|
|
double noise_distance;
|
2015-10-15 15:52:03 +00:00
|
|
|
Vector3 walker, step, segment_start, offset;
|
2013-12-01 18:24:53 +00:00
|
|
|
double render_precision;
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (max_segments <= 0) {
|
2013-12-01 18:24:53 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-17 22:01:36 +00:00
|
|
|
model->getAltitudeRange(&ymin, &ymax);
|
|
|
|
|
2013-12-20 16:30:27 +00:00
|
|
|
model->getDetailRange(&min_step, &max_step);
|
2016-01-10 16:14:54 +00:00
|
|
|
|
|
|
|
double distance = parent->getCameraLocation(start).sub(start).getNorm();
|
|
|
|
render_precision = min_step + (max_step - min_step) * min(distance / (quality + 0.1), 100.0) * 0.01;
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
segment_count = 0;
|
|
|
|
current_total_length = 0.0;
|
|
|
|
current_inside_length = 0.0;
|
|
|
|
segment_length = 0.0;
|
|
|
|
walker = start;
|
2015-10-15 15:52:03 +00:00
|
|
|
offset = Vector3(model->getLayer()->propXOffset()->getValue(), 0.0, model->getLayer()->propZOffset()->getValue());
|
|
|
|
noise_distance = _getDistanceToBorder(model, start.add(offset)) * render_precision;
|
2013-12-17 22:01:36 +00:00
|
|
|
inside = 0;
|
2013-12-11 10:32:10 +00:00
|
|
|
step = direction.scale(render_precision);
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
do {
|
2013-12-11 10:32:10 +00:00
|
|
|
walker = walker.add(step);
|
|
|
|
step_length = step.getNorm();
|
2015-10-15 15:52:03 +00:00
|
|
|
noise_distance = _getDistanceToBorder(model, walker.add(offset)) * render_precision;
|
2013-12-01 18:24:53 +00:00
|
|
|
current_total_length += step_length;
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (noise_distance > 0.0) {
|
|
|
|
if (inside) {
|
2013-12-01 18:24:53 +00:00
|
|
|
// inside the cloud
|
|
|
|
segment_length += step_length;
|
|
|
|
current_inside_length += step_length;
|
2013-12-11 10:32:10 +00:00
|
|
|
step = direction.scale((noise_distance < render_precision) ? render_precision : noise_distance);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2013-12-01 18:24:53 +00:00
|
|
|
// entering the cloud
|
|
|
|
inside = 1;
|
2013-12-18 21:09:35 +00:00
|
|
|
segment_length = 0.0;
|
|
|
|
segment_start = walker;
|
2013-12-01 18:24:53 +00:00
|
|
|
current_inside_length += segment_length;
|
2013-12-11 10:32:10 +00:00
|
|
|
step = direction.scale(render_precision);
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
|
|
|
if (inside) {
|
2013-12-01 18:24:53 +00:00
|
|
|
// exiting the cloud
|
2013-12-18 21:09:35 +00:00
|
|
|
segment_length += step_length;
|
|
|
|
current_inside_length += step_length;
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
out_segments->start = segment_start;
|
2013-12-18 21:09:35 +00:00
|
|
|
out_segments->end = walker;
|
2013-12-01 18:24:53 +00:00
|
|
|
out_segments->length = segment_length;
|
|
|
|
out_segments++;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (++segment_count >= max_segments) {
|
2013-12-01 18:24:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
inside = 0;
|
2013-12-11 10:32:10 +00:00
|
|
|
step = direction.scale(render_precision);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2013-12-01 18:24:53 +00:00
|
|
|
// searching for a cloud
|
2013-12-11 10:32:10 +00:00
|
|
|
step = direction.scale((noise_distance > -render_precision) ? render_precision : -noise_distance);
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-10 16:14:54 +00:00
|
|
|
|
|
|
|
render_precision *= 1.0 + 0.001 / (quality + 0.1);
|
2015-11-09 21:30:46 +00:00
|
|
|
} while (inside || (walker.y >= ymin - 0.001 && walker.y <= ymax + 0.001 &&
|
|
|
|
current_total_length < max_total_length && current_inside_length < max_inside_length));
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
*total_length = current_total_length;
|
|
|
|
*inside_length = current_inside_length;
|
|
|
|
return segment_count;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location) {
|
2013-12-01 18:24:53 +00:00
|
|
|
int i, segment_count;
|
2013-12-20 16:30:27 +00:00
|
|
|
double max_length, total_length, inside_length;
|
2013-12-01 18:24:53 +00:00
|
|
|
Vector3 start, end, direction;
|
|
|
|
Color result, col;
|
2016-01-10 16:14:54 +00:00
|
|
|
CloudSegment segments[30];
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
start = eye;
|
|
|
|
end = location;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (!optimizeSearchLimits(model, &start, &end)) {
|
2013-12-01 18:24:53 +00:00
|
|
|
return COLOR_TRANSPARENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
direction = end.sub(start);
|
|
|
|
max_length = direction.getNorm();
|
|
|
|
direction = direction.normalize();
|
|
|
|
result = COLOR_TRANSPARENT;
|
|
|
|
|
2013-12-20 16:30:27 +00:00
|
|
|
double ymin, ymax;
|
|
|
|
model->getAltitudeRange(&ymin, &ymax);
|
2016-01-10 16:14:54 +00:00
|
|
|
double transparency_depth = (ymax - ymin);
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2016-01-10 16:14:54 +00:00
|
|
|
SurfaceMaterial material(COLOR_WHITE.scaled(5.0));
|
|
|
|
material.hardness = 1.0;
|
|
|
|
material.reflection = 0.0;
|
|
|
|
material.shininess = 0.0;
|
|
|
|
material.validate();
|
|
|
|
|
|
|
|
segment_count = findSegments(model, start, direction, 30, transparency_depth, max_length, &inside_length,
|
2015-11-09 21:30:46 +00:00
|
|
|
&total_length, segments);
|
|
|
|
for (i = segment_count - 1; i >= 0; i--) {
|
2016-01-10 16:14:54 +00:00
|
|
|
col = parent->applyLightingToSurface(segments[i].start, VECTOR_UP, material);
|
2013-12-30 16:22:01 +00:00
|
|
|
|
2013-12-04 21:52:18 +00:00
|
|
|
col.a = (segments[i].length >= transparency_depth) ? 1.0 : (segments[i].length / transparency_depth);
|
2013-12-11 09:24:35 +00:00
|
|
|
result.mask(col);
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
2015-09-22 16:17:00 +00:00
|
|
|
|
|
|
|
// Opacify when hitting inside_length limit
|
2015-11-09 21:30:46 +00:00
|
|
|
if (inside_length >= transparency_depth) {
|
2013-12-01 18:24:53 +00:00
|
|
|
result.a = 1.0;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else if (inside_length >= transparency_depth * 0.8) {
|
2015-09-22 16:17:00 +00:00
|
|
|
result.a += (1.0 - result.a) * ((inside_length - transparency_depth * 0.8) / (transparency_depth * 0.2));
|
|
|
|
}
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2015-10-08 22:43:32 +00:00
|
|
|
// Apply aerial perspective
|
2015-11-09 21:30:46 +00:00
|
|
|
if (result.a > 0.00001) {
|
2015-10-08 22:43:32 +00:00
|
|
|
assert(segment_count > 0);
|
|
|
|
|
|
|
|
double a = result.a;
|
|
|
|
// TODO Don't apply it only at first segment
|
|
|
|
result = parent->getAtmosphereRenderer()->applyAerialPerspective(segments[0].start, result).final;
|
|
|
|
result.a = a;
|
|
|
|
}
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
bool CloudBasicLayerRenderer::alterLight(BaseCloudsModel *model, LightComponent *light, const Vector3 &,
|
|
|
|
const Vector3 &location) {
|
2013-12-18 21:09:35 +00:00
|
|
|
Vector3 start, end, direction;
|
2013-12-01 18:24:53 +00:00
|
|
|
double inside_depth, total_depth, factor;
|
2016-01-10 16:14:54 +00:00
|
|
|
CloudSegment segments[30];
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
start = location;
|
2013-12-18 21:09:35 +00:00
|
|
|
direction = light->direction.scale(-1.0);
|
|
|
|
end = location.add(direction.scale(10000.0));
|
2015-11-09 21:30:46 +00:00
|
|
|
if (not optimizeSearchLimits(model, &start, &end)) {
|
2013-12-01 18:24:53 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-20 16:30:27 +00:00
|
|
|
double ymin, ymax;
|
|
|
|
model->getAltitudeRange(&ymin, &ymax);
|
2016-01-10 16:14:54 +00:00
|
|
|
double light_traversal = (ymax - ymin) * 0.8 * light->color.getPower();
|
|
|
|
findSegments(model, start, direction, 30, light_traversal, end.sub(start).getNorm(), &inside_depth, &total_depth,
|
2015-11-09 21:30:46 +00:00
|
|
|
segments);
|
2013-12-01 18:24:53 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (light_traversal < 0.0001) {
|
2013-12-01 18:24:53 +00:00
|
|
|
factor = 0.0;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2013-12-04 21:52:18 +00:00
|
|
|
factor = inside_depth / light_traversal;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (factor > 1.0) {
|
2013-12-01 18:24:53 +00:00
|
|
|
factor = 1.0;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else if (factor > 0.00001) {
|
2015-09-23 22:13:52 +00:00
|
|
|
factor = sqrt(factor);
|
|
|
|
}
|
2013-12-01 18:24:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 16:14:54 +00:00
|
|
|
double miminum_light = 0.01 * light->color.getPower();
|
2013-12-04 21:52:18 +00:00
|
|
|
factor = 1.0 - (1.0 - miminum_light) * factor;
|
2013-12-01 18:24:53 +00:00
|
|
|
|
|
|
|
light->color.r *= factor;
|
|
|
|
light->color.g *= factor;
|
|
|
|
light->color.b *= factor;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|