WIP on new clouds renderer

This commit is contained in:
Michaël Lemaire 2013-12-04 22:52:18 +01:00
parent ff27afe675
commit 9be090b1ff
20 changed files with 371 additions and 407 deletions

1
TODO
View file

@ -1,5 +1,6 @@
Technology Preview 2 : Technology Preview 2 :
- Add initial terrain offset so that the (0,0) coordinates are above water. - Add initial terrain offset so that the (0,0) coordinates are above water.
- Use water height as 0.0 (offset the terrain).
- Finalize lighting/clouds refactoring - Finalize lighting/clouds refactoring
=> Restore cloud lighting => Restore cloud lighting
=> Improve cloud rendering precision (and beware of precision discontinuity when rendering clouds in front of ground (shorter distance)). => Improve cloud rendering precision (and beware of precision discontinuity when rendering clouds in front of ground (shorter distance)).

View file

@ -10,7 +10,7 @@ class Color
{ {
public: public:
Color(); Color();
Color(double r, double g, double b, double a); Color(double r, double g, double b, double a=1.0);
void save(PackStream* stream) const; void save(PackStream* stream) const;
void load(PackStream* stream); void load(PackStream* stream);

View file

@ -8,20 +8,14 @@
CloudLayerDefinition::CloudLayerDefinition(BaseDefinition* parent): CloudLayerDefinition::CloudLayerDefinition(BaseDefinition* parent):
BaseDefinition(parent) BaseDefinition(parent)
{ {
_coverage_by_altitude = new Curve; type = CIRRUS;
_coverage_noise = new NoiseGenerator(); altitude = 0.5;
_shape_noise = new NoiseGenerator(); scaling = 0.5;
_edge_noise = new NoiseGenerator(); coverage = 0.5;
material = new SurfaceMaterial;
} }
CloudLayerDefinition::~CloudLayerDefinition() CloudLayerDefinition::~CloudLayerDefinition()
{ {
delete _coverage_by_altitude;
delete _coverage_noise;
delete _shape_noise;
delete _edge_noise;
delete material;
} }
CloudLayerDefinition* CloudLayerDefinition::newCopy(const CloudLayerDefinition& other, BaseDefinition* parent) CloudLayerDefinition* CloudLayerDefinition::newCopy(const CloudLayerDefinition& other, BaseDefinition* parent)
@ -45,21 +39,9 @@ void CloudLayerDefinition::save(PackStream* stream) const
int clouds_type = (int)type; int clouds_type = (int)type;
stream->write(&clouds_type); stream->write(&clouds_type);
stream->write(&lower_altitude); stream->write(&altitude);
stream->write(&thickness); stream->write(&scaling);
_coverage_by_altitude->save(stream); stream->write(&coverage);
_coverage_noise->save(stream);
_shape_noise->save(stream);
_edge_noise->save(stream);
materialSave(stream, material);
stream->write(&hardness);
stream->write(&transparencydepth);
stream->write(&lighttraversal);
stream->write(&minimumlight);
stream->write(&shape_scaling);
stream->write(&edge_scaling);
stream->write(&edge_length);
stream->write(&base_coverage);
} }
void CloudLayerDefinition::load(PackStream* stream) void CloudLayerDefinition::load(PackStream* stream)
@ -70,21 +52,9 @@ void CloudLayerDefinition::load(PackStream* stream)
stream->read(&clouds_type); stream->read(&clouds_type);
type = (CloudsType)clouds_type; type = (CloudsType)clouds_type;
stream->read(&lower_altitude); stream->read(&altitude);
stream->read(&thickness); stream->read(&scaling);
_coverage_by_altitude->load(stream); stream->read(&coverage);
_coverage_noise->load(stream);
_shape_noise->load(stream);
_edge_noise->load(stream);
materialLoad(stream, material);
stream->read(&hardness);
stream->read(&transparencydepth);
stream->read(&lighttraversal);
stream->read(&minimumlight);
stream->read(&shape_scaling);
stream->read(&edge_scaling);
stream->read(&edge_length);
stream->read(&base_coverage);
validate(); validate();
} }
@ -96,168 +66,15 @@ void CloudLayerDefinition::copy(BaseDefinition* _destination) const
CloudLayerDefinition* destination = (CloudLayerDefinition*)_destination; CloudLayerDefinition* destination = (CloudLayerDefinition*)_destination;
destination->type = type; destination->type = type;
destination->lower_altitude = lower_altitude; destination->altitude = altitude;
destination->thickness = thickness; destination->scaling = scaling;
_coverage_by_altitude->copy(destination->_coverage_by_altitude); destination->coverage = coverage;
_coverage_noise->copy(destination->_coverage_noise);
_shape_noise->copy(destination->_shape_noise);
_edge_noise->copy(destination->_edge_noise);
*destination->material = *material;
destination->hardness = hardness;
destination->transparencydepth = transparencydepth;
destination->lighttraversal = lighttraversal;
destination->minimumlight = minimumlight;
destination->shape_scaling = shape_scaling;
destination->edge_scaling = edge_scaling;
destination->edge_length = edge_length;
destination->base_coverage = base_coverage;
} }
void CloudLayerDefinition::validate() void CloudLayerDefinition::validate()
{ {
if (shape_scaling < 0.0001) if (scaling < 0.1)
{ {
shape_scaling = 0.00001; scaling = 0.1;
} }
if (edge_scaling < 0.0001)
{
edge_scaling = 0.00001;
}
_coverage_by_altitude->clear();
_shape_noise->clearLevels();
_edge_noise->clearLevels();
_coverage_noise->clearLevels();
_coverage_noise->addLevelsSimple(2, 10.0, 0.0, 1.0, 0.0);
_coverage_noise->addLevelsSimple(2, 1.0, 0.0, 1.0, 0.0);
_coverage_noise->setFunctionParams(NOISE_FUNCTION_NAIVE, 0.0, 0.0);
switch (type)
{
case CLOUDS_TYPE_CIRRUS:
_coverage_by_altitude->addPoint(0.0, 0.0);
_coverage_by_altitude->addPoint(0.5, 1.0);
_coverage_by_altitude->addPoint(1.0, 0.0);
_shape_noise->addLevelsSimple(3, 1.0, 0.0, 1.0, 0.5);
_shape_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, 0.0, 0.0);
_edge_noise->addLevelsSimple(4, 1.0, -0.5, 0.5, 0.5);
_edge_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, -0.2, 0.0);
break;
case CLOUDS_TYPE_CUMULUS:
_coverage_by_altitude->addPoint(0.0, 0.0);
_coverage_by_altitude->addPoint(0.1, 1.0);
_coverage_by_altitude->addPoint(0.4, 0.8);
_coverage_by_altitude->addPoint(0.7, 1.0);
_coverage_by_altitude->addPoint(1.0, 0.0);
_shape_noise->addLevelsSimple(7, 1.0, 0.0, 1.0, 0.5);
_shape_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, 0.4, 0.0);
_edge_noise->addLevelsSimple(4, 1.0, -0.5, 0.5, 0.5);
_edge_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, 0.8, 0.0);
break;
case CLOUDS_TYPE_STRATOCUMULUS:
_coverage_by_altitude->addPoint(0.0, 0.0);
_coverage_by_altitude->addPoint(0.2, 1.0);
_coverage_by_altitude->addPoint(0.5, 1.0);
_coverage_by_altitude->addPoint(1.0, 0.0);
_shape_noise->addLevelsSimple(4, 1.0, 0.0, 1.0, 0.5);
_shape_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, 0.3, 0.0);
_edge_noise->addLevelsSimple(6, 1.0, -0.5, 0.5, 0.5);
_edge_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, 0.5, 0.0);
break;
case CLOUDS_TYPE_STRATUS:
_coverage_by_altitude->addPoint(0.0, 0.0);
_coverage_by_altitude->addPoint(0.2, 1.0);
_coverage_by_altitude->addPoint(0.8, 1.0);
_coverage_by_altitude->addPoint(1.0, 0.0);
_shape_noise->addLevelsSimple(3, 1.0, 0.0, 1.0, 0.5);
_shape_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, -0.3, 0.0);
_edge_noise->addLevelsSimple(4, 1.0, -0.5, 0.5, 0.5);
_edge_noise->setFunctionParams(NOISE_FUNCTION_SIMPLEX, -0.5, 0.0);
break;
default:
break;
}
_coverage_noise->normalizeAmplitude(-1.0, 3.0, 0);
_shape_noise->normalizeAmplitude(-0.5, 0.5, 0);
_edge_noise->normalizeAmplitude(-0.5, 0.5, 0);
materialValidate(material);
}
void CloudLayerDefinition::applyPreset(CloudsLayerPreset preset)
{
_coverage_noise->randomizeOffsets();
_edge_noise->randomizeOffsets();
_shape_noise->randomizeOffsets();
material->base = colorToHSL(colorFromValues(0.7, 0.7, 0.7, 1.0));
switch (preset)
{
case CLOUDS_LAYER_PRESET_CIRRUS:
type = CLOUDS_TYPE_CIRRUS;
lower_altitude = 25.0;
thickness = 2.0;
material->reflection = 0.4;
material->shininess = 0.5;
hardness = 0.0;
transparencydepth = 3.0;
lighttraversal = 10.0;
minimumlight = 0.6;
shape_scaling = 8.0;
edge_scaling = 2.0;
edge_length = 0.8;
base_coverage = 0.6;
break;
case CLOUDS_LAYER_PRESET_CUMULUS:
type = CLOUDS_TYPE_CUMULUS;
lower_altitude = 15.0;
thickness = 15.0;
material->reflection = 0.5;
material->shininess = 1.2;
hardness = 0.25;
transparencydepth = 1.5;
lighttraversal = 8.0;
minimumlight = 0.4;
shape_scaling = 20.0;
edge_scaling = 2.0;
edge_length = 0.0;
base_coverage = 0.7;
break;
case CLOUDS_LAYER_PRESET_STRATOCUMULUS:
type = CLOUDS_TYPE_STRATOCUMULUS;
lower_altitude = 5.0;
thickness = 6.0;
material->reflection = 0.3;
material->shininess = 0.8;
hardness = 0.25;
transparencydepth = 1.5;
lighttraversal = 7.0;
minimumlight = 0.4;
shape_scaling = 10.0;
edge_scaling = 0.8;
edge_length = 0.3;
base_coverage = 0.4;
break;
case CLOUDS_LAYER_PRESET_STRATUS:
type = CLOUDS_TYPE_STRATUS;
lower_altitude = 3.0;
thickness = 4.0;
material->reflection = 0.1;
material->shininess = 0.8;
hardness = 0.1;
transparencydepth = 3.0;
lighttraversal = 10.0;
minimumlight = 0.6;
shape_scaling = 8.0;
edge_scaling = 2.0;
edge_length = 1.0;
base_coverage = 0.4;
break;
default:
break;
}
validate();
} }

View file

@ -26,39 +26,23 @@ public:
public: public:
typedef enum typedef enum
{ {
CLOUDS_TYPE_CIRRUS, STRATUS,
CLOUDS_TYPE_CUMULUS, NIMBOSTRATUS,
CLOUDS_TYPE_STRATOCUMULUS, CUMULUS,
CLOUDS_TYPE_STRATUS STRATOCUMULUS,
ALTOCUMULUS,
ALTOSTRATUS,
CUMULONIMBUS,
CIRROCUMULUS,
CIRROSTRATUS,
CIRRUS
} CloudsType; } CloudsType;
typedef enum
{
CLOUDS_LAYER_PRESET_CIRRUS,
CLOUDS_LAYER_PRESET_CUMULUS,
CLOUDS_LAYER_PRESET_STRATOCUMULUS,
CLOUDS_LAYER_PRESET_STRATUS
} CloudsLayerPreset;
void applyPreset(CloudsLayerPreset preset);
public: public:
CloudsType type; CloudsType type;
double lower_altitude; double altitude;
double thickness; double scaling;
double base_coverage; double coverage;
double shape_scaling;
double edge_scaling;
double edge_length;
SurfaceMaterial* material;
double hardness;
double transparencydepth;
double lighttraversal;
double minimumlight;
Curve* _coverage_by_altitude;
NoiseGenerator* _coverage_noise;
NoiseGenerator* _shape_noise;
NoiseGenerator* _edge_noise;
}; };
} }

View file

@ -19,8 +19,8 @@ void CloudsDefinition::applyPreset(CloudsPreset preset)
if (preset == CLOUDS_PRESET_PARTLY_CLOUDY) if (preset == CLOUDS_PRESET_PARTLY_CLOUDY)
{ {
CloudLayerDefinition* layer = new CloudLayerDefinition(this); CloudLayerDefinition* layer = new CloudLayerDefinition(this);
layer->applyPreset(CloudLayerDefinition::CLOUDS_LAYER_PRESET_CIRRUS); layer->type = CloudLayerDefinition::STRATOCUMULUS;
layer->setName("Cirrus"); layer->setName("Strato-cumulus");
addLayer(layer); addLayer(layer);
} }
} }

View file

@ -14,11 +14,6 @@
FormClouds::FormClouds(QWidget *parent): FormClouds::FormClouds(QWidget *parent):
BaseFormLayer(parent) BaseFormLayer(parent)
{ {
addAutoPreset(tr("Cirrus"));
addAutoPreset(tr("Cumulus"));
addAutoPreset(tr("Stratocumulus"));
addAutoPreset(tr("Stratus"));
_definition = new CloudsDefinition(NULL); _definition = new CloudsDefinition(NULL);
_layer = new CloudLayerDefinition(NULL); _layer = new CloudLayerDefinition(NULL);
@ -33,17 +28,9 @@ FormClouds::FormClouds(QWidget *parent):
_previewColor->setRenderer(_previewColorRenderer); _previewColor->setRenderer(_previewColorRenderer);
addInputEnum(tr("Clouds model"), (int*)&_layer->type, QStringList() << tr("Cirrus") << tr("Cumulus") << tr("Stratocumulus") << tr("Stratus")); addInputEnum(tr("Clouds model"), (int*)&_layer->type, QStringList() << tr("Cirrus") << tr("Cumulus") << tr("Stratocumulus") << tr("Stratus"));
addInputDouble(tr("Lower altitude"), &_layer->lower_altitude, -10.0, 50.0, 0.5, 5.0); addInputDouble(tr("Lower altitude"), &_layer->altitude, 0.0, 1.0, 0.01, 0.1);
addInputDouble(tr("Layer thickness"), &_layer->thickness, 0.0, 20.0, 0.1, 1.0); addInputDouble(tr("Scaling"), &_layer->scaling, 0.0, 1.0, 0.01, 0.1);
addInputDouble(tr("Max coverage"), &_layer->base_coverage, 0.0, 1.0, 0.01, 0.1); addInputDouble(tr("Coverage"), &_layer->coverage, 0.0, 1.0, 0.01, 0.1);
addInputDouble(tr("Shape scaling"), &_layer->shape_scaling, 3.0, 30.0, 0.3, 3.0);
addInputDouble(tr("Edge scaling"), &_layer->edge_scaling, 0.5, 5.0, 0.05, 0.5);
addInputDouble(tr("Edge length"), &_layer->edge_length, 0.0, 1.0, 0.01, 0.1);
addInputMaterial(tr("Material"), _layer->material);
addInputDouble(tr("Hardness to light"), &_layer->hardness, 0.0, 1.0, 0.01, 0.1);
addInputDouble(tr("Transparency depth"), &_layer->transparencydepth, 0.0, 10.0, 0.1, 1.0);
addInputDouble(tr("Light traversal depth"), &_layer->lighttraversal, 0.0, 10.0, 0.1, 1.0);
addInputDouble(tr("Minimum lighting"), &_layer->minimumlight, 0.0, 1.0, 0.01, 0.1);
setLayers(_definition); setLayers(_definition);
} }
@ -81,9 +68,3 @@ void FormClouds::layerWriteCurrentTo(void* layer_definition)
{ {
_layer->copy((CloudLayerDefinition*)layer_definition); _layer->copy((CloudLayerDefinition*)layer_definition);
} }
void FormClouds::autoPresetSelected(int preset)
{
_layer->applyPreset((CloudLayerDefinition::CloudsLayerPreset)preset);
BaseForm::autoPresetSelected(preset);
}

View file

@ -21,7 +21,6 @@ public slots:
protected: protected:
virtual void layerReadCurrentFrom(void* layer_definition); virtual void layerReadCurrentFrom(void* layer_definition);
virtual void layerWriteCurrentTo(void* layer_definition); virtual void layerWriteCurrentTo(void* layer_definition);
virtual void autoPresetSelected(int preset);
private: private:
CloudsDefinition* _definition; CloudsDefinition* _definition;

View file

@ -30,7 +30,7 @@ static void _getLightingStatus(Renderer*, LightStatus* status, Vector3, int)
static double _getDensity(Renderer*, CloudLayerDefinition* layer, Vector3 location) static double _getDensity(Renderer*, CloudLayerDefinition* layer, Vector3 location)
{ {
double distance = 2.0 * v3Norm(location) / layer->thickness; double distance = 2.0 * v3Norm(location) / layer->scaling;
if (distance > 1.0) if (distance > 1.0)
{ {
return 0.0; return 0.0;
@ -75,8 +75,7 @@ void CloudsAspectPreviewRenderer::updateEvent()
CloudLayerDefinition* preview_layer = getScenery()->getClouds()->getCloudLayer(0); CloudLayerDefinition* preview_layer = getScenery()->getClouds()->getCloudLayer(0);
layer->copy(preview_layer); layer->copy(preview_layer);
preview_layer->thickness = preview_layer->shape_scaling; preview_layer->altitude = -preview_layer->scaling / 2.0;
preview_layer->lower_altitude = -preview_layer->thickness / 2.0;
preview_layer->validate(); preview_layer->validate();
prepare(); prepare();
@ -89,7 +88,7 @@ void CloudsAspectPreviewRenderer::updateEvent()
Color CloudsAspectPreviewRenderer::getColor2D(double x, double y, double) Color CloudsAspectPreviewRenderer::getColor2D(double x, double y, double)
{ {
Vector3 start, end; Vector3 start, end;
double thickness = layer->thickness; double thickness = layer->scaling;
start.x = x * thickness * 0.5; start.x = x * thickness * 0.5;
start.z = y * thickness * 0.5; start.z = y * thickness * 0.5;

View file

@ -1,6 +1,6 @@
#include "BaseCloudLayerRenderer.h" #include "BaseCloudLayerRenderer.h"
#include "CloudLayerDefinition.h" #include "clouds/BaseCloudsModel.h"
BaseCloudLayerRenderer::BaseCloudLayerRenderer(SoftwareRenderer* parent): BaseCloudLayerRenderer::BaseCloudLayerRenderer(SoftwareRenderer* parent):
parent(parent) parent(parent)
@ -12,67 +12,65 @@ BaseCloudLayerRenderer::~BaseCloudLayerRenderer()
} }
double BaseCloudLayerRenderer::getDensity(CloudLayerDefinition *, const Vector3 &) Color BaseCloudLayerRenderer::getColor(BaseCloudsModel *, const Vector3 &, const Vector3 &)
{
return 0.0;
}
Color BaseCloudLayerRenderer::getColor(CloudLayerDefinition *, const Vector3 &, const Vector3 &)
{ {
return COLOR_TRANSPARENT; return COLOR_TRANSPARENT;
} }
bool BaseCloudLayerRenderer::alterLight(CloudLayerDefinition *, LightDefinition *, const Vector3 &, const Vector3 &) bool BaseCloudLayerRenderer::alterLight(BaseCloudsModel *, LightDefinition *, const Vector3 &, const Vector3 &)
{ {
return false; return false;
} }
bool BaseCloudLayerRenderer::optimizeSearchLimits(CloudLayerDefinition *layer, Vector3 *start, Vector3 *end) bool BaseCloudLayerRenderer::optimizeSearchLimits(BaseCloudsModel *model, Vector3 *start, Vector3 *end)
{ {
Vector3 diff; Vector3 diff;
double min_altitude, max_altitude;
if (start->y > layer->lower_altitude + layer->thickness) model->getAltitudeRange(&min_altitude, &max_altitude);
if (start->y > max_altitude)
{ {
if (end->y >= layer->lower_altitude + layer->thickness) if (end->y >= max_altitude)
{ {
return false; return false;
} }
else else
{ {
diff = v3Sub(*end, *start); diff = v3Sub(*end, *start);
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y)); *start = v3Add(*start, v3Scale(diff, (max_altitude - start->y) / diff.y));
if (end->y < layer->lower_altitude) if (end->y < min_altitude)
{ {
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude - end->y) / diff.y)); *end = v3Add(*end, v3Scale(diff, (min_altitude - end->y) / diff.y));
} }
} }
} }
else if (start->y < layer->lower_altitude) else if (start->y < min_altitude)
{ {
if (end->y <= layer->lower_altitude) if (end->y <= min_altitude)
{ {
return false; return false;
} }
else else
{ {
diff = v3Sub(*end, *start); diff = v3Sub(*end, *start);
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y)); *start = v3Add(*start, v3Scale(diff, (min_altitude - start->y) / diff.y));
if (end->y >= layer->lower_altitude + layer->thickness) if (end->y >= max_altitude)
{ {
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude + layer->thickness - end->y) / diff.y)); *end = v3Add(*end, v3Scale(diff, (max_altitude - end->y) / diff.y));
} }
} }
} }
else /* start is inside layer */ else /* start is inside layer */
{ {
diff = v3Sub(*end, *start); diff = v3Sub(*end, *start);
if (end->y > layer->lower_altitude + layer->thickness) if (end->y > max_altitude)
{ {
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y)); *end = v3Add(*start, v3Scale(diff, (max_altitude - start->y) / diff.y));
} }
else if (end->y < layer->lower_altitude) else if (end->y < min_altitude)
{ {
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y)); *end = v3Add(*start, v3Scale(diff, (min_altitude - start->y) / diff.y));
} }
} }

View file

@ -14,11 +14,10 @@ public:
BaseCloudLayerRenderer(SoftwareRenderer* parent); BaseCloudLayerRenderer(SoftwareRenderer* parent);
virtual ~BaseCloudLayerRenderer(); virtual ~BaseCloudLayerRenderer();
virtual bool optimizeSearchLimits(CloudLayerDefinition *layer, Vector3 *start, Vector3 *end); virtual bool optimizeSearchLimits(BaseCloudsModel *model, Vector3 *start, Vector3 *end);
virtual double getDensity(CloudLayerDefinition* layer, const Vector3 &location); virtual Color getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location);
virtual Color getColor(CloudLayerDefinition* layer, const Vector3 &eye, const Vector3 &location); virtual bool alterLight(BaseCloudsModel *model, LightDefinition* light, const Vector3 &eye, const Vector3 &location);
virtual bool alterLight(CloudLayerDefinition* layer, LightDefinition* light, const Vector3 &eye, const Vector3 &location);
protected: protected:
SoftwareRenderer* parent; SoftwareRenderer* parent;

View file

@ -5,6 +5,8 @@
#include "NoiseGenerator.h" #include "NoiseGenerator.h"
#include "Curve.h" #include "Curve.h"
#include "AtmosphereRenderer.h" #include "AtmosphereRenderer.h"
#include "clouds/BaseCloudsModel.h"
#include "SurfaceMaterial.h"
typedef struct typedef struct
{ {
@ -18,67 +20,9 @@ CloudBasicLayerRenderer::CloudBasicLayerRenderer(SoftwareRenderer* parent):
{ {
} }
static inline double _standardCoverageFunc(CloudLayerDefinition* layer, Vector3 position) static inline double _getDistanceToBorder(BaseCloudsModel* model, Vector3 position)
{ {
if (position.y < layer->lower_altitude || position.y > (layer->lower_altitude + layer->thickness)) return model->getDensity(position);
{
return 0.0;
}
else
{
return layer->base_coverage * layer->_coverage_by_altitude->getValue((position.y - layer->lower_altitude) / layer->thickness);
}
}
static inline double _getDistanceToBorder(CloudLayerDefinition* layer, Vector3 position)
{
double val;
double minval, maxval;
layer->_shape_noise->getRange(&minval, &maxval);
val = 0.5 * layer->_shape_noise->get3DTotal(position.x / layer->shape_scaling, position.y / layer->shape_scaling, position.z / layer->shape_scaling) / maxval;
return (val - 0.5 + _standardCoverageFunc(layer, position)) * layer->shape_scaling;
}
static inline Vector3 _getNormal(CloudLayerDefinition* layer, Vector3 position, double detail)
{
Vector3 result = {0.0, 0.0, 0.0};
Vector3 dposition;
double val, dval;
val = _getDistanceToBorder(layer, position);
dposition.x = position.x + detail;
dposition.y = position.y;
dposition.z = position.z;
dval = val - _getDistanceToBorder(layer, dposition);
result.x += dval;
dposition.x = position.x - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.x -= dval;
dposition.x = position.x;
dposition.y = position.y + detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.y += dval;
dposition.y = position.y - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.y -= dval;
dposition.y = position.y;
dposition.z = position.z + detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.z += dval;
dposition.z = position.z - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.z -= dval;
return v3Normalize(result);
} }
/** /**
@ -97,8 +41,9 @@ static inline Vector3 _getNormal(CloudLayerDefinition* layer, Vector3 position,
* @param out_segments Allocated space to fill found segments * @param out_segments Allocated space to fill found segments
* @return Number of segments found * @return Number of segments found
*/ */
static int _findSegments(CloudLayerDefinition* definition, SoftwareRenderer* renderer, Vector3 start, Vector3 direction, double, int max_segments, double max_inside_length, double max_total_length, double* inside_length, double* total_length, CloudSegment* out_segments) static int _findSegments(BaseCloudsModel* model, SoftwareRenderer* renderer, Vector3 start, Vector3 direction, double, int max_segments, double max_inside_length, double max_total_length, double* inside_length, double* total_length, CloudSegment* out_segments)
{ {
CloudLayerDefinition* layer = model->getLayer();
int inside, segment_count; int inside, segment_count;
double current_total_length, current_inside_length; double current_total_length, current_inside_length;
double step_length, segment_length, remaining_length; double step_length, segment_length, remaining_length;
@ -112,7 +57,7 @@ static int _findSegments(CloudLayerDefinition* definition, SoftwareRenderer* ren
} }
render_precision = 15.2 - 1.5 * (double)renderer->render_quality; render_precision = 15.2 - 1.5 * (double)renderer->render_quality;
render_precision = render_precision * definition->shape_scaling / 50.0; render_precision = render_precision * layer->scaling / 50.0;
if (render_precision > max_total_length / 10.0) if (render_precision > max_total_length / 10.0)
{ {
render_precision = max_total_length / 10.0; render_precision = max_total_length / 10.0;
@ -127,7 +72,7 @@ static int _findSegments(CloudLayerDefinition* definition, SoftwareRenderer* ren
current_inside_length = 0.0; current_inside_length = 0.0;
segment_length = 0.0; segment_length = 0.0;
walker = start; walker = start;
noise_distance = _getDistanceToBorder(definition, start) * render_precision; noise_distance = _getDistanceToBorder(model, start) * render_precision;
inside = (noise_distance > 0.0) ? 1 : 0; inside = (noise_distance > 0.0) ? 1 : 0;
step = v3Scale(direction, render_precision); step = v3Scale(direction, render_precision);
@ -136,7 +81,7 @@ static int _findSegments(CloudLayerDefinition* definition, SoftwareRenderer* ren
walker = v3Add(walker, step); walker = v3Add(walker, step);
step_length = v3Norm(step); step_length = v3Norm(step);
last_noise_distance = noise_distance; last_noise_distance = noise_distance;
noise_distance = _getDistanceToBorder(definition, walker) * render_precision; noise_distance = _getDistanceToBorder(model, walker) * render_precision;
current_total_length += step_length; current_total_length += step_length;
if (noise_distance > 0.0) if (noise_distance > 0.0)
@ -185,49 +130,16 @@ static int _findSegments(CloudLayerDefinition* definition, SoftwareRenderer* ren
step = v3Scale(direction, (noise_distance > -render_precision) ? render_precision : -noise_distance); step = v3Scale(direction, (noise_distance > -render_precision) ? render_precision : -noise_distance);
} }
} }
} while (inside || (walker.y >= definition->lower_altitude - 0.001 && walker.y <= (definition->lower_altitude + definition->thickness) + 0.001 && current_total_length < max_total_length && current_inside_length < max_inside_length)); } while (inside || (walker.y >= layer->altitude - 0.001 && walker.y <= (layer->altitude + layer->scaling) + 0.001 && current_total_length < max_total_length && current_inside_length < max_inside_length));
*total_length = current_total_length; *total_length = current_total_length;
*inside_length = current_inside_length; *inside_length = current_inside_length;
return segment_count; return segment_count;
} }
static Color _applyLayerLighting(CloudLayerDefinition* definition, SoftwareRenderer* renderer, Vector3 position, double) Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location)
{
Vector3 normal;
Color col1, col2;
normal = _getNormal(definition, position, 3.0);
if (renderer->render_quality > 5)
{
normal = v3Add(normal, _getNormal(definition, position, 2.0));
normal = v3Add(normal, _getNormal(definition, position, 1.0));
}
if (renderer->render_quality > 5)
{
normal = v3Add(normal, _getNormal(definition, position, 0.5));
}
normal = v3Scale(v3Normalize(normal), definition->hardness);
// TODO Compute light filter only once
col1 = renderer->applyLightingToSurface(renderer, position, normal, definition->material);
col2 = renderer->applyLightingToSurface(renderer, position, v3Scale(normal, -1.0), definition->material);
col1.r = (col1.r + col2.r) / 2.0;
col1.g = (col1.g + col2.g) / 2.0;
col1.b = (col1.b + col2.b) / 2.0;
col1.a = (col1.a + col2.a) / 2.0;
return col1;
}
double CloudBasicLayerRenderer::getDensity(CloudLayerDefinition* layer, const Vector3 &location)
{
return 0.0;
}
Color CloudBasicLayerRenderer::getColor(CloudLayerDefinition* layer, const Vector3 &eye, const Vector3 &location)
{ {
CloudLayerDefinition* layer = model->getLayer();
int i, segment_count; int i, segment_count;
double max_length, detail, total_length, inside_length; double max_length, detail, total_length, inside_length;
Vector3 start, end, direction; Vector3 start, end, direction;
@ -236,7 +148,7 @@ Color CloudBasicLayerRenderer::getColor(CloudLayerDefinition* layer, const Vecto
start = eye; start = eye;
end = location; end = location;
if (!optimizeSearchLimits(layer, &start, &end)) if (!optimizeSearchLimits(model, &start, &end))
{ {
return COLOR_TRANSPARENT; return COLOR_TRANSPARENT;
} }
@ -246,16 +158,25 @@ Color CloudBasicLayerRenderer::getColor(CloudLayerDefinition* layer, const Vecto
direction = direction.normalize(); direction = direction.normalize();
result = COLOR_TRANSPARENT; result = COLOR_TRANSPARENT;
detail = parent->getPrecision(parent, start) / layer->shape_scaling; detail = parent->getPrecision(parent, start) / layer->scaling;
double transparency_depth = layer->scaling * 0.5;
segment_count = _findSegments(layer, parent, start, direction, detail, 20, layer->transparencydepth, max_length, &inside_length, &total_length, segments); segment_count = _findSegments(model, parent, start, direction, detail, 20, transparency_depth, max_length, &inside_length, &total_length, segments);
for (i = segment_count - 1; i >= 0; i--) for (i = segment_count - 1; i >= 0; i--)
{ {
col = _applyLayerLighting(layer, parent, segments[i].start, detail); SurfaceMaterial material;
col.a = (segments[i].length >= layer->transparencydepth) ? 1.0 : (segments[i].length / layer->transparencydepth); material.base = colorToHSL(Color(0.7, 0.7, 0.7));
material.hardness = 0.25;
material.reflection = 0.3;
material.shininess = 0.8;
materialValidate(&material);
col = parent->applyLightingToSurface(parent, segments[i].start, parent->getAtmosphereRenderer()->getSunDirection(), &material);
col.a = (segments[i].length >= transparency_depth) ? 1.0 : (segments[i].length / transparency_depth);
colorMask(&result, &col); colorMask(&result, &col);
} }
if (inside_length >= layer->transparencydepth) if (inside_length >= transparency_depth)
{ {
result.a = 1.0; result.a = 1.0;
} }
@ -267,7 +188,7 @@ Color CloudBasicLayerRenderer::getColor(CloudLayerDefinition* layer, const Vecto
return result; return result;
} }
bool CloudBasicLayerRenderer::alterLight(CloudLayerDefinition* layer, LightDefinition* light, const Vector3 &, const Vector3 &location) bool CloudBasicLayerRenderer::alterLight(BaseCloudsModel *model, LightDefinition* light, const Vector3 &, const Vector3 &location)
{ {
Vector3 start, end; Vector3 start, end;
double inside_depth, total_depth, factor; double inside_depth, total_depth, factor;
@ -275,27 +196,29 @@ bool CloudBasicLayerRenderer::alterLight(CloudLayerDefinition* layer, LightDefin
start = location; start = location;
end = location.add(light->direction.scale(10000.0)); end = location.add(light->direction.scale(10000.0));
if (not optimizeSearchLimits(layer, &start, &end)) if (not optimizeSearchLimits(model, &start, &end))
{ {
return false; return false;
} }
_findSegments(layer, parent, start, light->direction, 0.1, 20, layer->lighttraversal, end.sub(start).getNorm(), &inside_depth, &total_depth, segments); double light_traversal = model->getLayer()->scaling * 8.0;
_findSegments(model, parent, start, light->direction, 0.1, 20, light_traversal, end.sub(start).getNorm(), &inside_depth, &total_depth, segments);
if (layer->lighttraversal < 0.0001) if (light_traversal < 0.0001)
{ {
factor = 0.0; factor = 0.0;
} }
else else
{ {
factor = inside_depth / layer->lighttraversal; factor = inside_depth / light_traversal;
if (factor > 1.0) if (factor > 1.0)
{ {
factor = 1.0; factor = 1.0;
} }
} }
factor = 1.0 - (1.0 - layer->minimumlight) * factor; double miminum_light = 0.4;
factor = 1.0 - (1.0 - miminum_light) * factor;
light->color.r *= factor; light->color.r *= factor;
light->color.g *= factor; light->color.g *= factor;

View file

@ -10,14 +10,19 @@
namespace paysages { namespace paysages {
namespace software { namespace software {
/*!
* \brief Basic cloud layer renderer.
*
* This renderer simply iters through the cloud layer, collecting cloud segments.
* It does not account for local density variations.
*/
class SOFTWARESHARED_EXPORT CloudBasicLayerRenderer: public BaseCloudLayerRenderer class SOFTWARESHARED_EXPORT CloudBasicLayerRenderer: public BaseCloudLayerRenderer
{ {
public: public:
CloudBasicLayerRenderer(SoftwareRenderer* parent); CloudBasicLayerRenderer(SoftwareRenderer* parent);
virtual double getDensity(CloudLayerDefinition* layer, const Vector3 &location) override; virtual Color getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location) override;
virtual Color getColor(CloudLayerDefinition* layer, const Vector3 &eye, const Vector3 &location) override; virtual bool alterLight(BaseCloudsModel *model, LightDefinition* light, const Vector3 &eye, const Vector3 &location) override;
virtual bool alterLight(CloudLayerDefinition* layer, LightDefinition* light, const Vector3 &eye, const Vector3 &location) override;
}; };
} }

View file

@ -3,13 +3,20 @@
#include "SoftwareRenderer.h" #include "SoftwareRenderer.h"
#include "Scenery.h" #include "Scenery.h"
#include "CloudsDefinition.h" #include "CloudsDefinition.h"
#include "CloudLayerDefinition.h"
#include "BaseCloudLayerRenderer.h" #include "BaseCloudLayerRenderer.h"
#include "CloudBasicLayerRenderer.h" #include "CloudBasicLayerRenderer.h"
#include "clouds/BaseCloudsModel.h"
#include "clouds/CloudModelStratoCumulus.h"
CloudsRenderer::CloudsRenderer(SoftwareRenderer* parent): CloudsRenderer::CloudsRenderer(SoftwareRenderer* parent):
parent(parent) parent(parent)
{ {
fake_renderer = new BaseCloudLayerRenderer(parent); fake_renderer = new BaseCloudLayerRenderer(parent);
CloudLayerDefinition* fake_layer = new CloudLayerDefinition(NULL);
fake_model = new BaseCloudsModel(fake_layer);
} }
CloudsRenderer::~CloudsRenderer() CloudsRenderer::~CloudsRenderer()
@ -19,6 +26,13 @@ CloudsRenderer::~CloudsRenderer()
delete renderer; delete renderer;
} }
delete fake_renderer; delete fake_renderer;
for (auto model : layer_models)
{
delete model;
}
delete fake_model->getLayer();
delete fake_model;
} }
void CloudsRenderer::update() void CloudsRenderer::update()
@ -29,11 +43,42 @@ void CloudsRenderer::update()
} }
layer_renderers.clear(); layer_renderers.clear();
for (auto model : layer_models)
{
delete model;
}
layer_models.clear();
CloudsDefinition* clouds = parent->getScenery()->getClouds(); CloudsDefinition* clouds = parent->getScenery()->getClouds();
int n = clouds->count(); int n = clouds->count();
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
layer_renderers.push_back(new CloudBasicLayerRenderer(parent)); layer_renderers.push_back(new CloudBasicLayerRenderer(parent));
CloudLayerDefinition* layer = clouds->getCloudLayer(i);
BaseCloudsModel* model;
switch (layer->type)
{
case CloudLayerDefinition::STRATUS:
case CloudLayerDefinition::NIMBOSTRATUS:
case CloudLayerDefinition::CUMULUS:
model = new BaseCloudsModel(layer);
break;
case CloudLayerDefinition::STRATOCUMULUS:
model = new CloudModelStratoCumulus(layer);
break;
case CloudLayerDefinition::ALTOCUMULUS:
case CloudLayerDefinition::ALTOSTRATUS:
case CloudLayerDefinition::CUMULONIMBUS:
case CloudLayerDefinition::CIRROCUMULUS:
case CloudLayerDefinition::CIRROSTRATUS:
case CloudLayerDefinition::CIRRUS:
model = new BaseCloudsModel(layer);
break;
}
layer_models.push_back(model);
model->update();
} }
} }
@ -49,6 +94,18 @@ BaseCloudLayerRenderer* CloudsRenderer::getLayerRenderer(unsigned int layer)
} }
} }
BaseCloudsModel* CloudsRenderer::getLayerModel(unsigned int layer)
{
if (layer < layer_models.size())
{
return layer_models[layer];
}
else
{
return fake_model;
}
}
Color CloudsRenderer::getColor(const Vector3 &eye, const Vector3 &location, const Color &base) Color CloudsRenderer::getColor(const Vector3 &eye, const Vector3 &location, const Color &base)
{ {
CloudsDefinition* definition = parent->getScenery()->getClouds(); CloudsDefinition* definition = parent->getScenery()->getClouds();
@ -64,10 +121,10 @@ Color CloudsRenderer::getColor(const Vector3 &eye, const Vector3 &location, cons
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
CloudLayerDefinition* layer = definition->getCloudLayer(i);
BaseCloudLayerRenderer* layer_renderer = getLayerRenderer(i); BaseCloudLayerRenderer* layer_renderer = getLayerRenderer(i);
BaseCloudsModel* layer_model = getLayerModel(i);
Color layer_color = layer_renderer->getColor(layer, eye, location); Color layer_color = layer_renderer->getColor(layer_model, eye, location);
colorMask(&cumul, &layer_color); colorMask(&cumul, &layer_color);
} }

View file

@ -18,32 +18,45 @@ public:
virtual ~CloudsRenderer(); virtual ~CloudsRenderer();
/*! /*!
* Update the renderer with the bound scenery. * \brief Update the renderer with the bound scenery.
*
* Don't call this if another thread is currently using this renderer.
*/ */
void update(); void update();
/*! /*!
* Get the layer renderer for a given layer. * \brief Get the layer renderer for a given layer.
* *
* The returned renderer is managed by this object and should not be deleted. * The returned renderer is managed by this object and should not be deleted.
*/ */
virtual BaseCloudLayerRenderer* getLayerRenderer(unsigned int layer); virtual BaseCloudLayerRenderer* getLayerRenderer(unsigned int layer);
/*! /*!
* Get the composited color, as applied on a base color and location. * \brief Get the cloud model for a given layer.
*
* The returned model is managed by this object and should not be deleted.
*/
virtual BaseCloudsModel* getLayerModel(unsigned int layer);
/*!
* \brief Get the composited color, as applied on a base color and location.
*/ */
virtual Color getColor(const Vector3 &eye, const Vector3 &location, const Color &base); virtual Color getColor(const Vector3 &eye, const Vector3 &location, const Color &base);
/*! /*!
* Alter a light, as if passed through all layers. * \brief Alter a light, as if passed through all layers.
* *
* Return true if the light was altered. * Return true if the light was altered.
*/ */
virtual bool alterLight(LightDefinition* light, const Vector3 &eye, const Vector3 &location); virtual bool alterLight(LightDefinition* light, const Vector3 &eye, const Vector3 &location);
private: private:
SoftwareRenderer* parent; SoftwareRenderer* parent;
std::vector<BaseCloudLayerRenderer*> layer_renderers; std::vector<BaseCloudLayerRenderer*> layer_renderers;
BaseCloudLayerRenderer* fake_renderer; BaseCloudLayerRenderer* fake_renderer;
std::vector<BaseCloudsModel*> layer_models;
BaseCloudsModel* fake_model;
}; };
} }

View file

@ -0,0 +1,49 @@
#include "BaseCloudsModel.h"
#include "CloudLayerDefinition.h"
BaseCloudsModel::BaseCloudsModel(CloudLayerDefinition *layer):
layer(layer)
{
}
BaseCloudsModel::~BaseCloudsModel()
{
}
void BaseCloudsModel::update()
{
}
void BaseCloudsModel::getAltitudeRange(double *min_altitude, double *max_altitude) const
{
*min_altitude = layer->altitude;
*max_altitude = layer->altitude * layer->scaling;
}
void BaseCloudsModel::getDetailRange(double *min_step, double *max_step) const
{
*min_step = 0.1;
*max_step = 1.0;
}
double BaseCloudsModel::getProbability(const Vector3 &, double) const
{
return 1.0;
}
double BaseCloudsModel::getDensity(const Vector3 &) const
{
return 0.0;
}
Color BaseCloudsModel::filterLight(const Color &light, double, double) const
{
return light;
}
Color BaseCloudsModel::applyLightExit(const Color &light, const Vector3 &, const Vector3 &) const
{
return light;
}

View file

@ -0,0 +1,38 @@
#ifndef BASECLOUDSMODEL_H
#define BASECLOUDSMODEL_H
#include "../software_global.h"
#include "Color.h"
namespace paysages {
namespace software {
/*!
* \brief Abstract class for all cloud models (cirrus, cumulus...).
*/
class BaseCloudsModel
{
public:
BaseCloudsModel(CloudLayerDefinition *layer);
virtual ~BaseCloudsModel();
virtual void update();
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const;
virtual void getDetailRange(double *min_step, double *max_step) const;
virtual double getProbability(const Vector3 &location, double radius) const;
virtual double getDensity(const Vector3 &location) const;
virtual Color filterLight(const Color &light, double length, double density) const;
virtual Color applyLightExit(const Color &light, const Vector3 &light_direction, const Vector3 &direction_to_eye) const;
inline CloudLayerDefinition* getLayer() const {return layer;}
protected:
CloudLayerDefinition *layer;
};
}
}
#endif // BASECLOUDSMODEL_H

View file

@ -0,0 +1,67 @@
#include "CloudModelStratoCumulus.h"
#include "NoiseGenerator.h"
#include "Vector3.h"
#include "CloudLayerDefinition.h"
CloudModelStratoCumulus::CloudModelStratoCumulus(CloudLayerDefinition* layer):
BaseCloudsModel(layer)
{
noise = new NoiseGenerator();
}
CloudModelStratoCumulus::~CloudModelStratoCumulus()
{
delete noise;
}
void CloudModelStratoCumulus::update()
{
noise->clearLevels();
noise->addLevelSimple(1.0, -1.0, 1.0);
noise->addLevelSimple(1.0 / 2.0, -0.6, 0.6);
noise->addLevelSimple(1.0 / 4.0, -0.3, 0.3);
noise->addLevelSimple(1.0 / 10.0, -0.15, 0.15);
noise->addLevelSimple(1.0 / 20.0, -0.09, 0.09);
noise->addLevelSimple(1.0 / 40.0, -0.06, 0.06);
noise->addLevelSimple(1.0 / 60.0, -0.03, 0.03);
noise->addLevelSimple(1.0 / 80.0, -0.015, 0.015);
noise->addLevelSimple(1.0 / 100.0, -0.06, 0.06);
noise->addLevelSimple(1.0 / 150.0, -0.015, 0.015);
noise->addLevelSimple(1.0 / 200.0, -0.009, 0.009);
noise->addLevelSimple(1.0 / 400.0, -0.024, 0.024);
noise->addLevelSimple(1.0 / 800.0, -0.003, 0.003);
noise->addLevelSimple(1.0 / 1000.0, -0.0015, 0.0015);
noise->normalizeAmplitude(-3.0, 3.0, 0);
}
void CloudModelStratoCumulus::getAltitudeRange(double *min_altitude, double *max_altitude) const
{
*min_altitude = 2.0 + 7.0 * layer->altitude;
*max_altitude = *min_altitude + 5.0 * layer->scaling;
}
double CloudModelStratoCumulus::getDensity(const Vector3 &location) const
{
double val;
double min_altitude, max_altitude;
getAltitudeRange(&min_altitude, &max_altitude);
if (location.y < min_altitude || location.y > max_altitude)
{
return 0.0;
}
else
{
double x = location.x / (5.0 * layer->scaling);
double y = (location.y - min_altitude) / (5.0 * layer->scaling);
double z = location.z / (5.0 * layer->scaling);
//double coverage = layer->coverage * layer->_coverage_by_altitude->getValue((position.y - layer->altitude) / layer->scaling);
double coverage = layer->coverage;
val = 0.5 * noise->get3DTotal(x, y, z);
return val - 0.5 + coverage;
}
}

View file

@ -0,0 +1,29 @@
#ifndef CLOUDMODELSTRATOCUMULUS_H
#define CLOUDMODELSTRATOCUMULUS_H
#include "../software_global.h"
#include "BaseCloudsModel.h"
namespace paysages {
namespace software {
class CloudModelStratoCumulus : public BaseCloudsModel
{
public:
CloudModelStratoCumulus(CloudLayerDefinition* layer);
virtual ~CloudModelStratoCumulus();
virtual void update() override;
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
virtual double getDensity(const Vector3 &location) const override;
private:
NoiseGenerator* noise;
};
}
}
#endif // CLOUDMODELSTRATOCUMULUS_H

View file

@ -20,7 +20,9 @@ SOURCES += SoftwareRenderer.cpp \
CloudsRenderer.cpp \ CloudsRenderer.cpp \
BaseCloudLayerRenderer.cpp \ BaseCloudLayerRenderer.cpp \
SkyRasterizer.cpp \ SkyRasterizer.cpp \
CloudBasicLayerRenderer.cpp CloudBasicLayerRenderer.cpp \
clouds/BaseCloudsModel.cpp \
clouds/CloudModelStratoCumulus.cpp
HEADERS += SoftwareRenderer.h\ HEADERS += SoftwareRenderer.h\
software_global.h \ software_global.h \
@ -30,7 +32,9 @@ HEADERS += SoftwareRenderer.h\
CloudsRenderer.h \ CloudsRenderer.h \
BaseCloudLayerRenderer.h \ BaseCloudLayerRenderer.h \
SkyRasterizer.h \ SkyRasterizer.h \
CloudBasicLayerRenderer.h CloudBasicLayerRenderer.h \
clouds/BaseCloudsModel.h \
clouds/CloudModelStratoCumulus.h
unix:!symbian { unix:!symbian {
maemo5 { maemo5 {

View file

@ -24,6 +24,7 @@ namespace software {
class CloudsRenderer; class CloudsRenderer;
class BaseCloudLayerRenderer; class BaseCloudLayerRenderer;
class BaseCloudsModel;
} }
} }