First draft of vegetation layers (WIP)

This commit is contained in:
Michaël Lemaire 2015-10-18 17:26:19 +02:00
parent 52bad18d26
commit b430f6037e
31 changed files with 949 additions and 12 deletions

View file

@ -9,6 +9,7 @@
#include "CloudsDefinition.h"
#include "TerrainDefinition.h"
#include "TexturesDefinition.h"
#include "VegetationDefinition.h"
#include "WaterDefinition.h"
#include "Logs.h"
@ -24,6 +25,7 @@ Scenery::Scenery():
terrain = new TerrainDefinition(this);
textures = new TexturesDefinition(this);
water = new WaterDefinition(this);
vegetation = new VegetationDefinition(this);
}
void Scenery::validate()
@ -118,6 +120,7 @@ void Scenery::autoPreset(int seed)
atmosphere->applyPreset(AtmosphereDefinition::ATMOSPHERE_PRESET_CLEAR_DAY);
water->applyPreset(WaterDefinition::WATER_PRESET_LAKE);
clouds->applyPreset(CloudsDefinition::CLOUDS_PRESET_PARTLY_CLOUDY);
vegetation->applyPreset(VegetationDefinition::VEGETATION_PRESET_TEMPERATE);
camera->setLocation(VECTOR_ZERO);
camera->setTarget(VECTOR_NORTH);
@ -178,6 +181,16 @@ void Scenery::getTextures(TexturesDefinition* textures)
this->textures->copy(textures);
}
void Scenery::setVegetation(VegetationDefinition* vegetation)
{
vegetation->copy(this->vegetation);
}
void Scenery::getVegetation(VegetationDefinition* vegetation)
{
this->vegetation->copy(vegetation);
}
void Scenery::setWater(WaterDefinition* water)
{
water->copy(this->water);

View file

@ -57,6 +57,10 @@ public:
inline TexturesDefinition* getTextures() const {return textures;}
void getTextures(TexturesDefinition* textures);
void setVegetation(VegetationDefinition* Vegetation);
inline VegetationDefinition* getVegetation() const {return vegetation;}
void getVegetation(VegetationDefinition* Vegetation);
void setWater(WaterDefinition* water);
inline WaterDefinition* getWater() const {return water;}
void getWater(WaterDefinition* water);
@ -69,6 +73,7 @@ private:
CloudsDefinition* clouds;
TerrainDefinition* terrain;
TexturesDefinition* textures;
VegetationDefinition* vegetation;
WaterDefinition* water;
};

View file

@ -17,6 +17,12 @@ SurfaceMaterial::SurfaceMaterial(const Color &color)
receive_shadows = 1.0;
}
SurfaceMaterial::SurfaceMaterial(const SurfaceMaterial &other):
SurfaceMaterial(COLOR_BLACK)
{
other.copy(this);
}
SurfaceMaterial::~SurfaceMaterial()
{
delete base;

View file

@ -10,13 +10,14 @@ class DEFINITIONSHARED_EXPORT SurfaceMaterial
{
public:
SurfaceMaterial();
SurfaceMaterial(const Color& color);
SurfaceMaterial(const Color &color);
SurfaceMaterial(const SurfaceMaterial &other);
~SurfaceMaterial();
void setColor(double r, double g, double b, double a);
void save(PackStream* stream) const;
void load(PackStream* stream);
void save(PackStream *stream) const;
void load(PackStream *stream);
void copy(SurfaceMaterial *destination) const;
void validate();

View file

@ -0,0 +1,34 @@
#include "VegetationDefinition.h"
#include "VegetationLayerDefinition.h"
#include "VegetationModelDefinition.h"
static DefinitionNode* _layer_constructor(Layers* parent)
{
return new VegetationLayerDefinition(parent);
}
VegetationDefinition::VegetationDefinition(DefinitionNode* parent) :
Layers(parent, "vegetation", _layer_constructor)
{
debug_model = new VegetationModelDefinition(this);
}
VegetationDefinition::~VegetationDefinition()
{
delete debug_model;
}
void VegetationDefinition::applyPreset(VegetationPreset preset)
{
VegetationLayerDefinition *layer;
clear();
if (preset == VEGETATION_PRESET_TEMPERATE)
{
layer = getVegetationLayer(addLayer());
layer->applyPreset(VegetationLayerDefinition::VEGETATION_BASIC_TREES);
layer->setName("Basic tree");
}
}

View file

@ -0,0 +1,38 @@
#ifndef VEGETATIONDEFINITION_H
#define VEGETATIONDEFINITION_H
#include "definition_global.h"
#include "Layers.h"
namespace paysages {
namespace definition {
/**
* Definition of all vegetation layers in the scenery.
*/
class DEFINITIONSHARED_EXPORT VegetationDefinition : public Layers
{
public:
VegetationDefinition(DefinitionNode* parent);
virtual ~VegetationDefinition();
/**
* Get a vegetation layer by its position.
*/
inline VegetationLayerDefinition* getVegetationLayer(int position) const {return (VegetationLayerDefinition*)getLayer(position);}
typedef enum
{
VEGETATION_PRESET_TEMPERATE
} VegetationPreset;
void applyPreset(VegetationPreset preset);
// TEMP
VegetationModelDefinition *debug_model;
};
}
}
#endif // VEGETATIONDEFINITION_H

View file

@ -0,0 +1,49 @@
#include "VegetationLayerDefinition.h"
#include "TerrainHeightMap.h"
#include "VegetationModelDefinition.h"
VegetationLayerDefinition::VegetationLayerDefinition(DefinitionNode* parent) :
DefinitionNode(parent, "layer")
{
//area = new TerrainHeightMap();
model = new VegetationModelDefinition(this);
}
VegetationLayerDefinition::~VegetationLayerDefinition()
{
//delete area;
delete model;
}
void VegetationLayerDefinition::save(PackStream *stream) const
{
//area->save(stream);
model->save(stream);
}
void VegetationLayerDefinition::load(PackStream *stream)
{
//area->load(stream);
model->load(stream);
}
void VegetationLayerDefinition::copy(DefinitionNode *destination_) const
{
VegetationLayerDefinition *destination = (VegetationLayerDefinition *)destination_;
//area->copy(destination->area);
model->copy(destination->model);
}
void VegetationLayerDefinition::validate()
{
//area->validate();
model->validate();
}
void VegetationLayerDefinition::applyPreset(VegetationLayerPreset preset)
{
// TODO
model->randomize();
}

View file

@ -0,0 +1,46 @@
#ifndef VEGETATIONLAYERDEFINITION_H
#define VEGETATIONLAYERDEFINITION_H
#include "definition_global.h"
#include "DefinitionNode.h"
namespace paysages {
namespace definition {
/**
* Definition of one vegetation layer.
*/
class DEFINITIONSHARED_EXPORT VegetationLayerDefinition : public DefinitionNode
{
public:
VegetationLayerDefinition(DefinitionNode *parent);
virtual ~VegetationLayerDefinition();
virtual void save(PackStream *stream) const override;
virtual void load(PackStream *stream) override;
virtual void copy(DefinitionNode *destination) const override;
virtual void validate() override;
typedef enum
{
VEGETATION_BASIC_TREES
} VegetationLayerPreset;
void applyPreset(VegetationLayerPreset preset);
private:
/**
* Geographic area of presence of this layer.
*/
TerrainHeightMap *area;
/**
* Base vegetation model to use for this layer.
*/
VegetationModelDefinition *model;
};
}
}
#endif // VEGETATIONLAYERDEFINITION_H

View file

@ -0,0 +1,166 @@
#include "VegetationModelDefinition.h"
#include "VegetationDefinition.h"
#include "RandomGenerator.h"
#include "Matrix4.h"
#include "SurfaceMaterial.h"
#include "PackStream.h"
VegetationModelDefinition::VegetationModelDefinition(DefinitionNode *parent):
DefinitionNode(parent, "model")
{
solid_material = new SurfaceMaterial();
foliage_material = new SurfaceMaterial();
randomize();
}
VegetationModelDefinition::~VegetationModelDefinition()
{
delete solid_material;
delete foliage_material;
}
void VegetationModelDefinition::save(PackStream *stream) const
{
int n;
solid_material->save(stream);
foliage_material->save(stream);
n = solid_volumes.size();
stream->write(&n);
for (const auto &solid_volume: solid_volumes)
{
solid_volume.save(stream);
}
n = foliage_groups.size();
stream->write(&n);
for (const auto &foliage_group: foliage_groups)
{
foliage_group.save(stream);
}
n = foliage_items.size();
stream->write(&n);
for (const auto &foliage_item: foliage_items)
{
foliage_item.save(stream);
}
}
void VegetationModelDefinition::load(PackStream *stream)
{
int i, n;
solid_material->load(stream);
foliage_material->load(stream);
stream->read(&n);
solid_volumes.clear();
for (i = 0; i < n; i++)
{
CappedCylinder solid_volume;
solid_volume.load(stream);
solid_volumes.push_back(solid_volume);
}
stream->read(&n);
foliage_groups.clear();
for (i = 0; i < n; i++)
{
Sphere foliage_group;
foliage_group.load(stream);
foliage_groups.push_back(foliage_group);
}
stream->read(&n);
foliage_items.clear();
for (i = 0; i < n; i++)
{
Disk foliage_item;
foliage_item.load(stream);
foliage_items.push_back(foliage_item);
}
}
void VegetationModelDefinition::copy(DefinitionNode *destination_) const
{
VegetationModelDefinition *destination = (VegetationModelDefinition *)destination_;
solid_material->copy(destination->solid_material);
foliage_material->copy(destination->foliage_material);
destination->solid_volumes.clear();
for (const auto &solid_volume: solid_volumes)
{
destination->solid_volumes.push_back(CappedCylinder(solid_volume));
}
destination->foliage_groups.clear();
for (const auto &foliage_group: foliage_groups)
{
destination->foliage_groups.push_back(Sphere(foliage_group));
}
destination->foliage_items.clear();
for (const auto &foliage_item: foliage_items)
{
destination->foliage_items.push_back(Disk(foliage_item));
}
}
void VegetationModelDefinition::validate()
{
}
static inline double randomizeValue(double base, double min_factor, double max_factor)
{
return base * (min_factor + RandomGenerator::random() * (max_factor - min_factor));
}
static void addBranchRecurse(std::vector<CappedCylinder> &branches, const Vector3 &base, const Vector3 &direction, double radius, double length)
{
branches.push_back(CappedCylinder(base, direction, radius, length));
if (length > 0.1)
{
int split_count = 3;
Vector3 new_base = base.add(direction.scale(length));
Matrix4 pivot1 = Matrix4::newRotateAxis(randomizeValue(1.0 - 0.6 * length, 0.9, 1.1), VECTOR_EAST);
Vector3 new_direction = pivot1.multPoint(direction);
for (int i = 0; i < split_count; i++)
{
Matrix4 pivot2 = Matrix4::newRotateAxis(randomizeValue(M_PI * 2.0 / (double)split_count, 0.9, 1.1), direction);
new_direction = pivot2.multPoint(new_direction);
addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.65, 0.75), randomizeValue(length, 0.55, 0.65));
}
}
}
void VegetationModelDefinition::randomize()
{
// Clear structure
solid_volumes.clear();
foliage_groups.clear();
foliage_items.clear();
// Add trunk and branches
addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.05, 0.5);
// Add foliage groups
for (const auto &branch: solid_volumes)
{
double length = branch.getLength();
if (length < 0.2)
{
double radius = length * 0.5;
Vector3 center = branch.getAxis().getOrigin().add(branch.getAxis().getDirection().scale(radius));
foliage_groups.push_back(Sphere(center, radius * 5.0));
}
}
// Add foliage items
for (int i = 0; i < 150; i++)
{
double radius = 0.15;
Vector3 dir = Vector3::randomInSphere(1.0 - radius);
Vector3 normal = dir.add(Vector3::randomInSphere(0.4)).add(Vector3(0.0, 0.6, 0.0)).normalize();
Disk leaf(dir, normal, randomizeValue(radius, 0.8, 1.0));
foliage_items.push_back(leaf);
}
}

View file

@ -0,0 +1,49 @@
#ifndef VEGETATIONMODELDEFINITION_H
#define VEGETATIONMODELDEFINITION_H
#include "definition_global.h"
#include <vector>
#include "DefinitionNode.h"
#include "Sphere.h"
#include "CappedCylinder.h"
#include "Disk.h"
namespace paysages {
namespace definition {
/**
* Model of vegetation.
*/
class DEFINITIONSHARED_EXPORT VegetationModelDefinition: public DefinitionNode
{
public:
VegetationModelDefinition(DefinitionNode *parent);
virtual ~VegetationModelDefinition();
inline const std::vector<CappedCylinder> &getSolidVolumes() const {return solid_volumes;}
inline const std::vector<Sphere> &getFoliageGroups() const {return foliage_groups;}
inline const std::vector<Disk> &getFoliageItems() const {return foliage_items;}
virtual void save(PackStream *stream) const override;
virtual void load(PackStream *stream) override;
virtual void copy(DefinitionNode *destination) const override;
virtual void validate() override;
/**
* Randomize the model geometry.
*/
void randomize();
private:
SurfaceMaterial *solid_material;
SurfaceMaterial *foliage_material;
std::vector<CappedCylinder> solid_volumes;
std::vector<Sphere> foliage_groups;
std::vector<Disk> foliage_items;
};
}
}
#endif // VEGETATIONMODELDEFINITION_H

View file

@ -27,6 +27,9 @@ SOURCES += \
TerrainDefinition.cpp \
TerrainHeightMap.cpp \
Scenery.cpp \
VegetationLayerDefinition.cpp \
VegetationDefinition.cpp \
VegetationModelDefinition.cpp \
PaintedGrid.cpp \
PaintedGridBrush.cpp \
PaintedGridData.cpp \
@ -56,6 +59,9 @@ HEADERS +=\
TerrainDefinition.h \
TerrainHeightMap.h \
Scenery.h \
VegetationLayerDefinition.h \
VegetationDefinition.h \
VegetationModelDefinition.h \
PaintedGrid.h \
PaintedGridBrush.h \
PaintedGridData.h \

View file

@ -35,6 +35,9 @@ namespace definition {
class TextureLayerDefinition;
class TerrainDefinition;
class TerrainHeightMap;
class VegetationDefinition;
class VegetationLayerDefinition;
class VegetationModelDefinition;
class PaintedGrid;
class PaintedGridData;
class PaintedGridBrush;

View file

@ -5,7 +5,7 @@
#include "CanvasFragment.h"
const int MAX_FRAGMENTS_PER_PIXEL = 2;
const int MAX_FRAGMENTS_PER_PIXEL = 5;
namespace paysages {
namespace software {

View file

@ -9,6 +9,7 @@ namespace software {
const int RASTERIZER_CLIENT_SKY = 0;
const int RASTERIZER_CLIENT_WATER = 1;
const int RASTERIZER_CLIENT_TERRAIN = 2;
const int RASTERIZER_CLIENT_VEGETATION = 3;
typedef struct ScanPoint ScanPoint;
typedef struct RenderScanlines RenderScanlines;

View file

@ -6,6 +6,7 @@
#include "TerrainRasterizer.h"
#include "WaterRasterizer.h"
#include "SkyRasterizer.h"
#include "VegetationRasterizer.h"
#include "CameraDefinition.h"
#include "ParallelWork.h"
#include "CanvasPortion.h"
@ -30,6 +31,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
rasterizers.push_back(new SkyRasterizer(this, progress, RASTERIZER_CLIENT_SKY));
rasterizers.push_back(new WaterRasterizer(this, progress, RASTERIZER_CLIENT_WATER));
rasterizers.push_back(new TerrainRasterizer(this, progress, RASTERIZER_CLIENT_TERRAIN));
rasterizers.push_back(new VegetationRasterizer(this, progress, RASTERIZER_CLIENT_VEGETATION));
current_work = NULL;
}

View file

@ -10,6 +10,7 @@
#include "CloudsDefinition.h"
#include "TerrainRenderer.h"
#include "TexturesRenderer.h"
#include "VegetationRenderer.h"
#include "WaterRenderer.h"
#include "SkyRasterizer.h"
#include "TerrainRasterizer.h"
@ -34,6 +35,7 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery):
clouds_renderer = new CloudsRenderer(this);
terrain_renderer = new TerrainRenderer(this);
textures_renderer = new TexturesRenderer(this);
vegetation_renderer = new VegetationRenderer(this);
water_renderer = new WaterRenderer(this);
nightsky_renderer = new NightSky(this);
@ -44,6 +46,7 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery):
lighting->registerFilter(water_renderer);
lighting->registerFilter(terrain_renderer);
lighting->registerFilter(vegetation_renderer);
lighting->registerFilter(clouds_renderer);
lighting->registerSource(atmosphere_renderer);

View file

@ -50,6 +50,7 @@ public:
inline TerrainRenderer* getTerrainRenderer() const {return terrain_renderer;}
inline TexturesRenderer* getTexturesRenderer() const {return textures_renderer;}
inline WaterRenderer* getWaterRenderer() const {return water_renderer;}
inline VegetationRenderer* getVegetationRenderer() const {return vegetation_renderer;}
inline NightSky* getNightSky() const {return nightsky_renderer;}
@ -74,6 +75,7 @@ private:
TexturesRenderer* textures_renderer;
WaterRenderer* water_renderer;
NightSky* nightsky_renderer;
VegetationRenderer *vegetation_renderer;
};
}

View file

@ -14,6 +14,8 @@
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, progress, client_id, Color(1.0, 0.9, 0.9))
{
setBackFaceCulling(true);
yoffset = 0.0;
}
void TerrainRasterizer::setQuality(double base_chunk_size, double detail_factor, int max_chunk_detail)
@ -77,19 +79,40 @@ void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, do
ov4.z = z;
dv4 = renderer->getTerrainRenderer()->getResult(x + size, z, true, true).location;
if (yoffset != 0.0)
{
dv1.y += yoffset;
dv2.y += yoffset;
dv3.y += yoffset;
dv4.y += yoffset;
}
if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height)
{
pushDisplacedQuad(canvas, dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4);
}
}
void TerrainRasterizer::getChunk(TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced)
void TerrainRasterizer::setYOffset(double offset)
{
this->yoffset = offset;
}
void TerrainRasterizer::getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, int displaced)
{
chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true, displaced).location;
chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true, displaced).location;
chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, true, displaced).location;
chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, true, displaced).location;
if (yoffset != 0.0)
{
chunk->point_nw.y += yoffset;
chunk->point_sw.y += yoffset;
chunk->point_se.y += yoffset;
chunk->point_ne.y += yoffset;
}
double displacement_power;
if (displaced)
{
@ -158,7 +181,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced
{
for (i = 0; i < chunk_count - 1; i++)
{
getChunk(&chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
@ -172,7 +195,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced
return result;
}
getChunk(&chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
@ -186,7 +209,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced
return result;
}
getChunk(&chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
@ -200,7 +223,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced
return result;
}
getChunk(&chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;

View file

@ -38,6 +38,14 @@ public:
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override;
protected:
/**
* Add a vertical offset to rasterized polygons.
*
* This may be used to rasterize a covering layer on top of ground.
*/
void setYOffset(double offset);
private:
/**
* Method called for each chunk tessellated by performTessellation.
@ -62,9 +70,11 @@ private:
void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height);
void getChunk(TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced);
void getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, int displaced);
private:
double yoffset;
// Quality control
double base_chunk_size;
double detail_factor;

View file

@ -0,0 +1,6 @@
#include "VegetationInstance.h"
VegetationInstance::VegetationInstance(const VegetationModelDefinition &model, const Vector3 &base, double size, double angle):
model(model), base(base), size(size), angle(angle)
{
}

View file

@ -0,0 +1,35 @@
#ifndef VEGETATIONINSTANCE_H
#define VEGETATIONINSTANCE_H
#include "software_global.h"
#include "Vector3.h"
namespace paysages {
namespace software {
/**
* Single instance of a vegetation layer (e.g. a single tree).
*
* This is used as potential hit on vegetation lookup.
*/
class SOFTWARESHARED_EXPORT VegetationInstance
{
public:
VegetationInstance(const VegetationModelDefinition &model, const Vector3 &base, double size=1.0, double angle=0.0);
inline const VegetationModelDefinition &getModel() const {return model;}
inline const Vector3 &getBase() const {return base;}
inline double getSize() const {return size;}
private:
const VegetationModelDefinition &model;
Vector3 base;
double size;
double angle;
};
}
}
#endif // VEGETATIONINSTANCE_H

View file

@ -0,0 +1,128 @@
#include "VegetationModelRenderer.h"
#include "Color.h"
#include "SurfaceMaterial.h"
#include "SpaceSegment.h"
#include "InfiniteRay.h"
#include "Disk.h"
#include "SoftwareRenderer.h"
#include "LightComponent.h"
#include "VegetationModelDefinition.h"
#include "VegetationResult.h"
VegetationModelRenderer::VegetationModelRenderer(SoftwareRenderer* parent, const VegetationModelDefinition* model):
parent(parent), model(model)
{
}
VegetationModelRenderer::~VegetationModelRenderer()
{
}
VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, bool only_hit) const
{
InfiniteRay ray(segment.getStart(), segment.getDirection());
int intersections;
Color result = COLOR_TRANSPARENT;
bool hit = false;
Vector3 location, normal;
double distance, nearest, maximal;
maximal = segment.getLength();
nearest = maximal;
for (const auto &branch: model->getSolidVolumes())
{
Vector3 near, far;
if (branch.checkRayIntersection(ray, &near, &far))
{
distance = ray.getCursor(near);
if (distance >= 0.0 and distance <= maximal)
{
// Got a branch hit
if (only_hit)
{
return VegetationResult(true);
}
if (distance < nearest)
{
result = Color(0.2, 0.15, 0.15);
nearest = distance;
hit = true;
location = near;
normal = near.sub(branch.getAxis().getOrigin()).crossProduct(branch.getAxis().getDirection()).normalize();
normal = branch.getAxis().getDirection().crossProduct(normal).normalize();
}
}
}
}
for (const auto &foliage: model->getFoliageGroups())
{
Vector3 near, far;
intersections = foliage.checkRayIntersection(ray, &near, &far);
if (intersections == 2)
{
InfiniteRay subray(ray.getOrigin().sub(foliage.getCenter()).scale(1.0 / foliage.getRadius()), ray.getDirection());
for (const auto &leaf: model->getFoliageItems())
{
Disk sized_leaf(leaf.getPoint(), leaf.getNormal(), leaf.getRadius() * leaf.getRadius() / foliage.getRadius());
if (sized_leaf.checkRayIntersection(subray, &near) == 1)
{
near = near.scale(foliage.getRadius()).add(foliage.getCenter());
distance = ray.getCursor(near);
if (distance >= 0.0 and distance <= maximal)
{
// Got a foliage hit
if (only_hit)
{
return VegetationResult(true);
}
if (distance < nearest)
{
result = Color(0.3, 0.5, 0.3);
nearest = distance;
hit = true;
location = near;
normal = sized_leaf.getNormal();
if (normal.dotProduct(location.sub(segment.getStart())) > 0.0)
{
// We look at backside
result = Color(0.3, 0.4, 0.3);
normal = normal.scale(-1.0);
}
}
}
}
}
}
}
if (hit)
{
SurfaceMaterial material(result);
material.reflection = 0.003;
material.shininess = 3.0;
material.hardness = 0.3;
material.validate();
// FIXME Can't use reference to temporary material
return VegetationResult(location, normal, material);
}
else
{
return VegetationResult();
}
}
bool VegetationModelRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
{
return getResult(SpaceSegment(at, light.direction.scale(-2.0)), true).isHit();
}

View file

@ -0,0 +1,44 @@
#ifndef VEGETATIONMODELRENDERER_H
#define VEGETATIONMODELRENDERER_H
#include "software_global.h"
#include "LightFilter.h"
namespace paysages {
namespace software {
/**
* Renderer of a single instance of vegetation model (e.g a single tree).
*/
class SOFTWARESHARED_EXPORT VegetationModelRenderer: public LightFilter
{
public:
VegetationModelRenderer(SoftwareRenderer* parent, const VegetationModelDefinition *model);
virtual ~VegetationModelRenderer();
/**
* Get the final color of this model on a segment.
*
* Coordinates should be expressed as relative to the model origin point.
*
* If only_hit is True, we only look whether there is a hit or not.
*/
VegetationResult getResult(const SpaceSegment &segment, bool only_hit=false) const;
/**
* Internal (relative) light filter.
*
* The 'at' parameter should be expressed as relative to the model origin point.
*/
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at);
private:
SoftwareRenderer* parent;
const VegetationModelDefinition* model;
};
}
}
#endif // VEGETATIONMODELRENDERER_H

View file

@ -0,0 +1,46 @@
#include "VegetationRasterizer.h"
#include <cassert>
#include "CanvasFragment.h"
#include "Color.h"
#include "SpaceSegment.h"
#include "SoftwareRenderer.h"
#include "VegetationRenderer.h"
#include "RayCastingResult.h"
VegetationRasterizer::VegetationRasterizer(SoftwareRenderer *renderer, RenderProgress *progress, int client_id):
TerrainRasterizer(renderer, progress, client_id)
{
setYOffset(0.5);
setColor(Color(0.8, 1.0, 0.8, 0.5));
}
Color VegetationRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const
{
assert(previous != NULL);
if (not fragment.isFrontFacing() or previous->getClient() == RASTERIZER_CLIENT_SKY)
{
// This is an exit fragment, or the last before sky
return COLOR_TRANSPARENT;
}
// Even if we assert, this may happen in rare circumstances (no opaque background fragment), so don't crash
if (previous == NULL)
{
return COLOR_TRANSPARENT;
}
SoftwareRenderer *renderer = getRenderer();
SpaceSegment segment(renderer->unprojectPoint(fragment.getPixel()), renderer->unprojectPoint(previous->getPixel()));
RayCastingResult result = renderer->getVegetationRenderer()->getResult(segment);
if (result.hit)
{
return result.hit_color;
}
else
{
return COLOR_TRANSPARENT;
}
}

View file

@ -0,0 +1,22 @@
#ifndef VEGETATIONRASTERIZER_H
#define VEGETATIONRASTERIZER_H
#include "software_global.h"
#include "TerrainRasterizer.h"
namespace paysages {
namespace software {
class SOFTWARESHARED_EXPORT VegetationRasterizer: public TerrainRasterizer
{
public:
VegetationRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id);
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override;
};
}
}
#endif // VEGETATIONRASTERIZER_H

View file

@ -0,0 +1,100 @@
#include "VegetationRenderer.h"
#include "VegetationModelRenderer.h"
#include "RayCastingResult.h"
#include "SpaceGridIterator.h"
#include "SpaceSegment.h"
#include "VegetationInstance.h"
#include "SoftwareRenderer.h"
#include "Scenery.h"
#include "TerrainRenderer.h"
#include "VegetationDefinition.h"
#include "VegetationResult.h"
#include "LightComponent.h"
const double DEBUG_DENSITY_FACTOR = 0.5;
// TEMP
class VegetationGridIterator: public SpaceGridIterator
{
public:
VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, VegetationModelDefinition *model, bool only_hit):
segment(segment), renderer(renderer), model(model), only_hit(only_hit)
{
}
inline const RayCastingResult &getResult() const {return result;}
virtual bool onCell(int x, int, int z) override
{
double dx = ((double)x + 0.5) * DEBUG_DENSITY_FACTOR;
double dz = ((double)z + 0.5) * DEBUG_DENSITY_FACTOR;
Vector3 base = renderer->getParent()->getTerrainRenderer()->getResult(dx, dz, 1, 1).location;
VegetationInstance instance(*model, base, 0.2);
result = renderer->renderInstance(segment, instance, only_hit);
return not result.hit;
}
private:
const SpaceSegment &segment;
VegetationRenderer *renderer;
VegetationModelDefinition *model;
RayCastingResult result;
bool only_hit;
};
VegetationRenderer::VegetationRenderer(SoftwareRenderer *parent):
parent(parent)
{
}
RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit)
{
RayCastingResult final;
VegetationModelRenderer model_renderer(parent, &instance.getModel());
SpaceSegment scaled_segment(segment.getStart().sub(instance.getBase()).scale(1.0 / instance.getSize()),
segment.getEnd().sub(instance.getBase()).scale(1.0 / instance.getSize()));
VegetationResult result = model_renderer.getResult(scaled_segment, only_hit);
final.hit = result.isHit();
if (final.hit and not only_hit)
{
Vector3 location = result.getLocation().scale(instance.getSize()).add(instance.getBase());
final.hit_color = parent->applyLightingToSurface(location, result.getNormal(), result.getMaterial());
final.hit_color = parent->applyMediumTraversal(location, final.hit_color);
final.hit_location = result.getLocation();
}
return final;
}
RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool only_hit)
{
// Find instances potentially crossing the segment
// TODO Collect the nearest hit, don't stop at the first one
VegetationGridIterator it(segment, this, parent->getScenery()->getVegetation()->debug_model, only_hit);
if (not segment.projectedOnYPlane().scaled(1.0 / DEBUG_DENSITY_FACTOR).iterateOnGrid(it))
{
return it.getResult();
}
else
{
return RayCastingResult();
}
}
bool VegetationRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
{
// Get segment to iterate
SpaceSegment segment(at, at.add(light.direction.scale(-1.0 * parent->render_quality)));
if (getResult(segment, true).hit)
{
light.color = COLOR_BLACK;
return false;
}
else
{
return true;
}
}

View file

@ -0,0 +1,37 @@
#ifndef VEGETATIONRENDERER_H
#define VEGETATIONRENDERER_H
#include "software_global.h"
#include "LightFilter.h"
namespace paysages {
namespace software {
class SOFTWARESHARED_EXPORT VegetationRenderer: public LightFilter
{
public:
VegetationRenderer(SoftwareRenderer *parent);
inline SoftwareRenderer *getParent() const {return parent;}
/**
* Perform ray casting on a single instance.
*/
RayCastingResult renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit=false);
/**
* Perform ray casting on a given segment.
*/
RayCastingResult getResult(const SpaceSegment &segment, bool only_hit=false);
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
private:
SoftwareRenderer *parent;
};
}
}
#endif // VEGETATIONRENDERER_H

View file

@ -0,0 +1,12 @@
#include "VegetationResult.h"
VegetationResult::VegetationResult(bool hit):
hit(hit)
{
}
VegetationResult::VegetationResult(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material):
location(location), normal(normal), material(material)
{
hit = true;
}

View file

@ -0,0 +1,36 @@
#ifndef VEGETATIONRESULT_H
#define VEGETATIONRESULT_H
#include "software_global.h"
#include "Vector3.h"
#include "SurfaceMaterial.h"
namespace paysages {
namespace software {
/**
* Result of a vegetation lookup.
*/
class SOFTWARESHARED_EXPORT VegetationResult
{
public:
VegetationResult(bool hit=false);
VegetationResult(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material);
inline bool isHit() const {return hit;}
inline const Vector3 &getLocation() const {return location;}
inline const Vector3 &getNormal() const {return normal;}
inline const SurfaceMaterial &getMaterial() const {return material;}
private:
bool hit;
Vector3 location;
Vector3 normal;
SurfaceMaterial material;
};
}
}
#endif // VEGETATIONRESULT_H

View file

@ -37,6 +37,7 @@ SOURCES += SoftwareRenderer.cpp \
RayCastingManager.cpp \
NightSky.cpp \
TerrainRayWalker.cpp \
VegetationModelRenderer.cpp \
Canvas.cpp \
CanvasPortion.cpp \
CanvasPixel.cpp \
@ -55,7 +56,11 @@ SOURCES += SoftwareRenderer.cpp \
LightSource.cpp \
RayCastingResult.cpp \
GodRaysSampler.cpp \
GodRaysResult.cpp
GodRaysResult.cpp \
VegetationRasterizer.cpp \
VegetationRenderer.cpp \
VegetationInstance.cpp \
VegetationResult.cpp
HEADERS += SoftwareRenderer.h\
software_global.h \
@ -82,6 +87,7 @@ HEADERS += SoftwareRenderer.h\
RayCastingManager.h \
NightSky.h \
TerrainRayWalker.h \
VegetationModelRenderer.h \
Canvas.h \
CanvasPortion.h \
CanvasPixel.h \
@ -100,7 +106,11 @@ HEADERS += SoftwareRenderer.h\
LightSource.h \
RayCastingResult.h \
GodRaysSampler.h \
GodRaysResult.h
GodRaysResult.h \
VegetationRasterizer.h \
VegetationRenderer.h \
VegetationInstance.h \
VegetationResult.h
unix:!symbian {
maemo5 {

View file

@ -54,6 +54,10 @@ namespace software {
class GodRaysSampler;
class GodRaysResult;
class VegetationResult;
class VegetationInstance;
class VegetationRenderer;
class VegetationModelRenderer;
class Canvas;
class CanvasPortion;