diff --git a/src/definition/Scenery.cpp b/src/definition/Scenery.cpp index 7637309..0ee7409 100644 --- a/src/definition/Scenery.cpp +++ b/src/definition/Scenery.cpp @@ -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); diff --git a/src/definition/Scenery.h b/src/definition/Scenery.h index acd8858..5513490 100644 --- a/src/definition/Scenery.h +++ b/src/definition/Scenery.h @@ -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; }; diff --git a/src/definition/SurfaceMaterial.cpp b/src/definition/SurfaceMaterial.cpp index f08971a..42ddebc 100644 --- a/src/definition/SurfaceMaterial.cpp +++ b/src/definition/SurfaceMaterial.cpp @@ -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; diff --git a/src/definition/SurfaceMaterial.h b/src/definition/SurfaceMaterial.h index 3a137be..2d8f988 100644 --- a/src/definition/SurfaceMaterial.h +++ b/src/definition/SurfaceMaterial.h @@ -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(); diff --git a/src/definition/VegetationDefinition.cpp b/src/definition/VegetationDefinition.cpp new file mode 100644 index 0000000..8f4a121 --- /dev/null +++ b/src/definition/VegetationDefinition.cpp @@ -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"); + } +} diff --git a/src/definition/VegetationDefinition.h b/src/definition/VegetationDefinition.h new file mode 100644 index 0000000..e8c8ece --- /dev/null +++ b/src/definition/VegetationDefinition.h @@ -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 diff --git a/src/definition/VegetationLayerDefinition.cpp b/src/definition/VegetationLayerDefinition.cpp new file mode 100644 index 0000000..0480dea --- /dev/null +++ b/src/definition/VegetationLayerDefinition.cpp @@ -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(); +} diff --git a/src/definition/VegetationLayerDefinition.h b/src/definition/VegetationLayerDefinition.h new file mode 100644 index 0000000..e3fbf08 --- /dev/null +++ b/src/definition/VegetationLayerDefinition.h @@ -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 diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp new file mode 100644 index 0000000..ddb2dd4 --- /dev/null +++ b/src/definition/VegetationModelDefinition.cpp @@ -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 &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); + } +} diff --git a/src/definition/VegetationModelDefinition.h b/src/definition/VegetationModelDefinition.h new file mode 100644 index 0000000..6ed0823 --- /dev/null +++ b/src/definition/VegetationModelDefinition.h @@ -0,0 +1,49 @@ +#ifndef VEGETATIONMODELDEFINITION_H +#define VEGETATIONMODELDEFINITION_H + +#include "definition_global.h" + +#include +#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 &getSolidVolumes() const {return solid_volumes;} + inline const std::vector &getFoliageGroups() const {return foliage_groups;} + inline const std::vector &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 solid_volumes; + std::vector foliage_groups; + std::vector foliage_items; +}; + +} +} + +#endif // VEGETATIONMODELDEFINITION_H diff --git a/src/definition/definition.pro b/src/definition/definition.pro index 52ec8e9..d328baa 100644 --- a/src/definition/definition.pro +++ b/src/definition/definition.pro @@ -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 \ diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index 9f8ac8a..fffaa08 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -35,6 +35,9 @@ namespace definition { class TextureLayerDefinition; class TerrainDefinition; class TerrainHeightMap; + class VegetationDefinition; + class VegetationLayerDefinition; + class VegetationModelDefinition; class PaintedGrid; class PaintedGridData; class PaintedGridBrush; diff --git a/src/render/software/CanvasPixel.h b/src/render/software/CanvasPixel.h index f21b6a9..2c58679 100644 --- a/src/render/software/CanvasPixel.h +++ b/src/render/software/CanvasPixel.h @@ -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 { diff --git a/src/render/software/Rasterizer.h b/src/render/software/Rasterizer.h index beb1ac3..57096d5 100644 --- a/src/render/software/Rasterizer.h +++ b/src/render/software/Rasterizer.h @@ -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; diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index d690e33..0e88a12 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -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; } diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index f34ee76..a9fff9e 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -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); diff --git a/src/render/software/SoftwareRenderer.h b/src/render/software/SoftwareRenderer.h index 244921e..0bf60e2 100644 --- a/src/render/software/SoftwareRenderer.h +++ b/src/render/software/SoftwareRenderer.h @@ -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; }; } diff --git a/src/render/software/TerrainRasterizer.cpp b/src/render/software/TerrainRasterizer.cpp index 676d9ba..30cbff7 100644 --- a/src/render/software/TerrainRasterizer.cpp +++ b/src/render/software/TerrainRasterizer.cpp @@ -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; diff --git a/src/render/software/TerrainRasterizer.h b/src/render/software/TerrainRasterizer.h index be7180b..db33da8 100644 --- a/src/render/software/TerrainRasterizer.h +++ b/src/render/software/TerrainRasterizer.h @@ -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; diff --git a/src/render/software/VegetationInstance.cpp b/src/render/software/VegetationInstance.cpp new file mode 100644 index 0000000..77edb69 --- /dev/null +++ b/src/render/software/VegetationInstance.cpp @@ -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) +{ +} diff --git a/src/render/software/VegetationInstance.h b/src/render/software/VegetationInstance.h new file mode 100644 index 0000000..2b2d1eb --- /dev/null +++ b/src/render/software/VegetationInstance.h @@ -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 diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp new file mode 100644 index 0000000..1446112 --- /dev/null +++ b/src/render/software/VegetationModelRenderer.cpp @@ -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(); +} diff --git a/src/render/software/VegetationModelRenderer.h b/src/render/software/VegetationModelRenderer.h new file mode 100644 index 0000000..1120a7a --- /dev/null +++ b/src/render/software/VegetationModelRenderer.h @@ -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 diff --git a/src/render/software/VegetationRasterizer.cpp b/src/render/software/VegetationRasterizer.cpp new file mode 100644 index 0000000..5a85765 --- /dev/null +++ b/src/render/software/VegetationRasterizer.cpp @@ -0,0 +1,46 @@ +#include "VegetationRasterizer.h" + +#include + +#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; + } +} diff --git a/src/render/software/VegetationRasterizer.h b/src/render/software/VegetationRasterizer.h new file mode 100644 index 0000000..6a03247 --- /dev/null +++ b/src/render/software/VegetationRasterizer.h @@ -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 diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp new file mode 100644 index 0000000..489f2a4 --- /dev/null +++ b/src/render/software/VegetationRenderer.cpp @@ -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; + } +} diff --git a/src/render/software/VegetationRenderer.h b/src/render/software/VegetationRenderer.h new file mode 100644 index 0000000..eace514 --- /dev/null +++ b/src/render/software/VegetationRenderer.h @@ -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 diff --git a/src/render/software/VegetationResult.cpp b/src/render/software/VegetationResult.cpp new file mode 100644 index 0000000..52b9f30 --- /dev/null +++ b/src/render/software/VegetationResult.cpp @@ -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; +} diff --git a/src/render/software/VegetationResult.h b/src/render/software/VegetationResult.h new file mode 100644 index 0000000..6da5bdf --- /dev/null +++ b/src/render/software/VegetationResult.h @@ -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 diff --git a/src/render/software/software.pro b/src/render/software/software.pro index ab19edf..583ec81 100644 --- a/src/render/software/software.pro +++ b/src/render/software/software.pro @@ -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 { diff --git a/src/render/software/software_global.h b/src/render/software/software_global.h index 25e9e68..4996a99 100644 --- a/src/render/software/software_global.h +++ b/src/render/software/software_global.h @@ -54,6 +54,10 @@ namespace software { class GodRaysSampler; class GodRaysResult; + class VegetationResult; + class VegetationInstance; + class VegetationRenderer; + class VegetationModelRenderer; class Canvas; class CanvasPortion;