From b430f6037e2373bee54708c79dcd2d38a09e8366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 18 Oct 2015 17:26:19 +0200 Subject: [PATCH 01/17] First draft of vegetation layers (WIP) --- src/definition/Scenery.cpp | 13 ++ src/definition/Scenery.h | 5 + src/definition/SurfaceMaterial.cpp | 6 + src/definition/SurfaceMaterial.h | 7 +- src/definition/VegetationDefinition.cpp | 34 ++++ src/definition/VegetationDefinition.h | 38 ++++ src/definition/VegetationLayerDefinition.cpp | 49 ++++++ src/definition/VegetationLayerDefinition.h | 46 +++++ src/definition/VegetationModelDefinition.cpp | 166 ++++++++++++++++++ src/definition/VegetationModelDefinition.h | 49 ++++++ src/definition/definition.pro | 6 + src/definition/definition_global.h | 3 + src/render/software/CanvasPixel.h | 2 +- src/render/software/Rasterizer.h | 1 + .../software/SoftwareCanvasRenderer.cpp | 2 + src/render/software/SoftwareRenderer.cpp | 3 + src/render/software/SoftwareRenderer.h | 2 + src/render/software/TerrainRasterizer.cpp | 33 +++- src/render/software/TerrainRasterizer.h | 12 +- src/render/software/VegetationInstance.cpp | 6 + src/render/software/VegetationInstance.h | 35 ++++ .../software/VegetationModelRenderer.cpp | 128 ++++++++++++++ src/render/software/VegetationModelRenderer.h | 44 +++++ src/render/software/VegetationRasterizer.cpp | 46 +++++ src/render/software/VegetationRasterizer.h | 22 +++ src/render/software/VegetationRenderer.cpp | 100 +++++++++++ src/render/software/VegetationRenderer.h | 37 ++++ src/render/software/VegetationResult.cpp | 12 ++ src/render/software/VegetationResult.h | 36 ++++ src/render/software/software.pro | 14 +- src/render/software/software_global.h | 4 + 31 files changed, 949 insertions(+), 12 deletions(-) create mode 100644 src/definition/VegetationDefinition.cpp create mode 100644 src/definition/VegetationDefinition.h create mode 100644 src/definition/VegetationLayerDefinition.cpp create mode 100644 src/definition/VegetationLayerDefinition.h create mode 100644 src/definition/VegetationModelDefinition.cpp create mode 100644 src/definition/VegetationModelDefinition.h create mode 100644 src/render/software/VegetationInstance.cpp create mode 100644 src/render/software/VegetationInstance.h create mode 100644 src/render/software/VegetationModelRenderer.cpp create mode 100644 src/render/software/VegetationModelRenderer.h create mode 100644 src/render/software/VegetationRasterizer.cpp create mode 100644 src/render/software/VegetationRasterizer.h create mode 100644 src/render/software/VegetationRenderer.cpp create mode 100644 src/render/software/VegetationRenderer.h create mode 100644 src/render/software/VegetationResult.cpp create mode 100644 src/render/software/VegetationResult.h 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; From cd7f30ecaeab7af46d54a9ff635ef88991442c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 18 Oct 2015 17:35:42 +0200 Subject: [PATCH 02/17] Add ability to disable vegetation rendering It is disabled in opengl rendering for now --- src/render/opengl/OpenGLRenderer.cpp | 15 ++++++--- src/render/opengl/OpenGLRenderer.h | 2 ++ src/render/software/VegetationRenderer.cpp | 39 ++++++++++++++++------ src/render/software/VegetationRenderer.h | 6 ++++ 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 1a397a1..8362684 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -7,6 +7,7 @@ #include "OpenGLWater.h" #include "OpenGLTerrain.h" #include "CloudsRenderer.h" +#include "VegetationRenderer.h" #include "Color.h" #include "Scenery.h" #include "LightingManager.h" @@ -61,6 +62,16 @@ OpenGLRenderer::~OpenGLRenderer() delete shared_state; } +void OpenGLRenderer::prepare() +{ + SoftwareRenderer::prepare(); + + getCloudsRenderer()->setEnabled(false); + getLightingManager()->setSpecularity(false); + getGodRaysSampler()->setEnabled(false); + getVegetationRenderer()->setEnabled(false); +} + void OpenGLRenderer::initialize() { ready = functions->initializeOpenGLFunctions(); @@ -71,10 +82,6 @@ void OpenGLRenderer::initialize() prepare(); - getCloudsRenderer()->setEnabled(false); - getLightingManager()->setSpecularity(false); - getGodRaysSampler()->setEnabled(false); - skybox->initialize(); skybox->updateScenery(); diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index d40a4b4..af848a3 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -24,6 +24,8 @@ public: inline OpenGLTerrain *getTerrain() const {return terrain;} inline bool isDisplayed() const {return displayed;} + virtual void prepare() override; + void initialize(); void prepareOpenGLState(); void resize(int width, int height); diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index 489f2a4..e89230e 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -47,6 +47,11 @@ VegetationRenderer::VegetationRenderer(SoftwareRenderer *parent): { } +void VegetationRenderer::setEnabled(bool enabled) +{ + this->enabled = enabled; +} + RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit) { RayCastingResult final; @@ -71,12 +76,19 @@ RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment, 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)) + if (enabled) { - return it.getResult(); + // 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(); + } } else { @@ -86,12 +98,19 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool 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) + if (enabled) { - light.color = COLOR_BLACK; - return false; + // 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; + } } else { diff --git a/src/render/software/VegetationRenderer.h b/src/render/software/VegetationRenderer.h index eace514..42cab58 100644 --- a/src/render/software/VegetationRenderer.h +++ b/src/render/software/VegetationRenderer.h @@ -13,6 +13,11 @@ class SOFTWARESHARED_EXPORT VegetationRenderer: public LightFilter public: VegetationRenderer(SoftwareRenderer *parent); + /** + * Totally enable or disable the vegetation layers rendering. + */ + void setEnabled(bool enabled); + inline SoftwareRenderer *getParent() const {return parent;} /** @@ -29,6 +34,7 @@ public: private: SoftwareRenderer *parent; + bool enabled; }; } From 48dc1a8f25e2f11edf4cb53cd9f794ceb2b665a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 18 Oct 2015 22:15:19 +0200 Subject: [PATCH 03/17] Refactored vegetation presence --- src/definition/DefinitionNode.cpp | 2 +- src/definition/DefinitionNode.h | 2 +- src/definition/Scenery.cpp | 2 +- src/definition/Scenery.h | 2 +- src/definition/TextureLayerDefinition.cpp | 2 +- src/definition/VegetationDefinition.cpp | 6 -- src/definition/VegetationDefinition.h | 4 -- .../VegetationInstance.cpp | 5 ++ .../VegetationInstance.h | 11 +++- src/definition/VegetationLayerDefinition.cpp | 37 +---------- src/definition/VegetationLayerDefinition.h | 9 +-- .../VegetationPresenceDefinition.cpp | 21 ++++++ src/definition/VegetationPresenceDefinition.h | 34 ++++++++++ src/definition/definition.pro | 8 ++- src/definition/definition_global.h | 2 + src/render/software/VegetationRenderer.cpp | 66 ++++++++++++------- src/render/software/VegetationRenderer.h | 6 +- src/render/software/software.pro | 2 - src/render/software/software_global.h | 1 - 19 files changed, 134 insertions(+), 88 deletions(-) rename src/{render/software => definition}/VegetationInstance.cpp (56%) rename src/{render/software => definition}/VegetationInstance.h (71%) create mode 100644 src/definition/VegetationPresenceDefinition.cpp create mode 100644 src/definition/VegetationPresenceDefinition.h diff --git a/src/definition/DefinitionNode.cpp b/src/definition/DefinitionNode.cpp index e994903..e5580e8 100644 --- a/src/definition/DefinitionNode.cpp +++ b/src/definition/DefinitionNode.cpp @@ -54,7 +54,7 @@ void DefinitionNode::setName(const std::string &name) this->name = name; } -Scenery* DefinitionNode::getScenery() +const Scenery* DefinitionNode::getScenery() const { if (parent) { diff --git a/src/definition/DefinitionNode.h b/src/definition/DefinitionNode.h index 514631f..b24edec 100644 --- a/src/definition/DefinitionNode.h +++ b/src/definition/DefinitionNode.h @@ -26,7 +26,7 @@ public: inline const std::string &getTypeName() const {return type_name;} - virtual Scenery* getScenery(); + virtual const Scenery* getScenery() const; inline const DefinitionNode *getParent() const {return parent;} inline const DefinitionNode *getRoot() const {return root;} diff --git a/src/definition/Scenery.cpp b/src/definition/Scenery.cpp index 0ee7409..b60a5ed 100644 --- a/src/definition/Scenery.cpp +++ b/src/definition/Scenery.cpp @@ -102,7 +102,7 @@ Scenery::FileOperationResult Scenery::loadGlobal(const std::string &filepath) return FILE_OPERATION_OK; } -Scenery* Scenery::getScenery() +const Scenery* Scenery::getScenery() const { return this; } diff --git a/src/definition/Scenery.h b/src/definition/Scenery.h index 5513490..cf9ebe4 100644 --- a/src/definition/Scenery.h +++ b/src/definition/Scenery.h @@ -33,7 +33,7 @@ public: FileOperationResult saveGlobal(const std::string &filepath) const; FileOperationResult loadGlobal(const std::string &filepath); - virtual Scenery* getScenery() override; + virtual const Scenery* getScenery() const override; void autoPreset(int seed=0); diff --git a/src/definition/TextureLayerDefinition.cpp b/src/definition/TextureLayerDefinition.cpp index fce5217..3ae17b3 100644 --- a/src/definition/TextureLayerDefinition.cpp +++ b/src/definition/TextureLayerDefinition.cpp @@ -50,7 +50,7 @@ void TextureLayerDefinition::validate() material->validate(); /* Update zone height range */ - Scenery* scenery = getScenery(); + const Scenery* scenery = getScenery(); if (scenery) { TerrainDefinition* terrain = scenery->getTerrain(); diff --git a/src/definition/VegetationDefinition.cpp b/src/definition/VegetationDefinition.cpp index 8f4a121..9437560 100644 --- a/src/definition/VegetationDefinition.cpp +++ b/src/definition/VegetationDefinition.cpp @@ -11,12 +11,6 @@ static DefinitionNode* _layer_constructor(Layers* 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) diff --git a/src/definition/VegetationDefinition.h b/src/definition/VegetationDefinition.h index e8c8ece..aea9e11 100644 --- a/src/definition/VegetationDefinition.h +++ b/src/definition/VegetationDefinition.h @@ -15,7 +15,6 @@ class DEFINITIONSHARED_EXPORT VegetationDefinition : public Layers { public: VegetationDefinition(DefinitionNode* parent); - virtual ~VegetationDefinition(); /** * Get a vegetation layer by its position. @@ -27,9 +26,6 @@ public: VEGETATION_PRESET_TEMPERATE } VegetationPreset; void applyPreset(VegetationPreset preset); - - // TEMP - VegetationModelDefinition *debug_model; }; } diff --git a/src/render/software/VegetationInstance.cpp b/src/definition/VegetationInstance.cpp similarity index 56% rename from src/render/software/VegetationInstance.cpp rename to src/definition/VegetationInstance.cpp index 77edb69..8b95811 100644 --- a/src/render/software/VegetationInstance.cpp +++ b/src/definition/VegetationInstance.cpp @@ -4,3 +4,8 @@ VegetationInstance::VegetationInstance(const VegetationModelDefinition &model, c model(model), base(base), size(size), angle(angle) { } + +VegetationInstance VegetationInstance::displace(const Vector3 &location, const Vector3 &normal) const +{ + return VegetationInstance(model, location, size, angle); +} diff --git a/src/render/software/VegetationInstance.h b/src/definition/VegetationInstance.h similarity index 71% rename from src/render/software/VegetationInstance.h rename to src/definition/VegetationInstance.h index 2b2d1eb..fd54a21 100644 --- a/src/render/software/VegetationInstance.h +++ b/src/definition/VegetationInstance.h @@ -1,19 +1,19 @@ #ifndef VEGETATIONINSTANCE_H #define VEGETATIONINSTANCE_H -#include "software_global.h" +#include "definition_global.h" #include "Vector3.h" namespace paysages { -namespace software { +namespace definition { /** * Single instance of a vegetation layer (e.g. a single tree). * * This is used as potential hit on vegetation lookup. */ -class SOFTWARESHARED_EXPORT VegetationInstance +class DEFINITIONSHARED_EXPORT VegetationInstance { public: VegetationInstance(const VegetationModelDefinition &model, const Vector3 &base, double size=1.0, double angle=0.0); @@ -22,6 +22,11 @@ public: inline const Vector3 &getBase() const {return base;} inline double getSize() const {return size;} + /** + * Return a copy of this instance, with terrain displacement applied. + */ + VegetationInstance displace(const Vector3 &location, const Vector3 &normal) const; + private: const VegetationModelDefinition &model; Vector3 base; diff --git a/src/definition/VegetationLayerDefinition.cpp b/src/definition/VegetationLayerDefinition.cpp index 0480dea..79e53fa 100644 --- a/src/definition/VegetationLayerDefinition.cpp +++ b/src/definition/VegetationLayerDefinition.cpp @@ -1,49 +1,16 @@ #include "VegetationLayerDefinition.h" -#include "TerrainHeightMap.h" #include "VegetationModelDefinition.h" +#include "VegetationPresenceDefinition.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(); + presence = new VegetationPresenceDefinition(this); } void VegetationLayerDefinition::applyPreset(VegetationLayerPreset preset) { - // TODO model->randomize(); } diff --git a/src/definition/VegetationLayerDefinition.h b/src/definition/VegetationLayerDefinition.h index e3fbf08..8fe775b 100644 --- a/src/definition/VegetationLayerDefinition.h +++ b/src/definition/VegetationLayerDefinition.h @@ -15,12 +15,9 @@ 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; + inline const VegetationPresenceDefinition *getPresence() const {return presence;} + inline const VegetationModelDefinition *getModel() const {return model;} typedef enum { @@ -32,7 +29,7 @@ private: /** * Geographic area of presence of this layer. */ - TerrainHeightMap *area; + VegetationPresenceDefinition *presence; /** * Base vegetation model to use for this layer. diff --git a/src/definition/VegetationPresenceDefinition.cpp b/src/definition/VegetationPresenceDefinition.cpp new file mode 100644 index 0000000..e8c0e5d --- /dev/null +++ b/src/definition/VegetationPresenceDefinition.cpp @@ -0,0 +1,21 @@ +#include "VegetationPresenceDefinition.h" + +#include "Scenery.h" +#include "TerrainDefinition.h" +#include "VegetationLayerDefinition.h" +#include "VegetationInstance.h" + +VegetationPresenceDefinition::VegetationPresenceDefinition(VegetationLayerDefinition *parent): + DefinitionNode(parent, "presence") +{ +} + +bool VegetationPresenceDefinition::collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers) const +{ + // TEMP debug implementation + double x = (xmin + xmax) / 2.0; + double z = (zmax + zmin) / 2.0; + double y = getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, true); + result->push_back(VegetationInstance(model, Vector3(x, y, z), 0.2)); + return true; +} diff --git a/src/definition/VegetationPresenceDefinition.h b/src/definition/VegetationPresenceDefinition.h new file mode 100644 index 0000000..1a51f99 --- /dev/null +++ b/src/definition/VegetationPresenceDefinition.h @@ -0,0 +1,34 @@ +#ifndef VEGETATIONPRESENCEDEFINITION_H +#define VEGETATIONPRESENCEDEFINITION_H + +#include "definition_global.h" + +#include "DefinitionNode.h" + +namespace paysages { +namespace definition { + +/** + * Definition of the presence of vegetation at locations. + */ +class DEFINITIONSHARED_EXPORT VegetationPresenceDefinition: public DefinitionNode +{ +public: + VegetationPresenceDefinition(VegetationLayerDefinition *parent); + + /** + * Collect instances in a rectangle area. + * + * If *outcomers* is true, the rectangle area will be expanded to included potential instances that does not originate + * in the area, but may have leaves in it. + * + * The location vector set in collected instances is based on raw terrain height, without displacement. + * It's the renderer role to apply the correct displacement. + */ + bool collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers=true) const; +}; + +} +} + +#endif // VEGETATIONPRESENCEDEFINITION_H diff --git a/src/definition/definition.pro b/src/definition/definition.pro index d328baa..559d586 100644 --- a/src/definition/definition.pro +++ b/src/definition/definition.pro @@ -30,6 +30,7 @@ SOURCES += \ VegetationLayerDefinition.cpp \ VegetationDefinition.cpp \ VegetationModelDefinition.cpp \ + VegetationInstance.cpp \ PaintedGrid.cpp \ PaintedGridBrush.cpp \ PaintedGridData.cpp \ @@ -42,7 +43,8 @@ SOURCES += \ IntNode.cpp \ IntDiff.cpp \ GodRaysDefinition.cpp \ - TimeManager.cpp + TimeManager.cpp \ + VegetationPresenceDefinition.cpp HEADERS +=\ definition_global.h \ @@ -62,6 +64,7 @@ HEADERS +=\ VegetationLayerDefinition.h \ VegetationDefinition.h \ VegetationModelDefinition.h \ + VegetationInstance.h \ PaintedGrid.h \ PaintedGridBrush.h \ PaintedGridData.h \ @@ -74,7 +77,8 @@ HEADERS +=\ IntNode.h \ IntDiff.h \ GodRaysDefinition.h \ - TimeManager.h + TimeManager.h \ + VegetationPresenceDefinition.h unix:!symbian { maemo5 { diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index fffaa08..4565eee 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -38,6 +38,8 @@ namespace definition { class VegetationDefinition; class VegetationLayerDefinition; class VegetationModelDefinition; + class VegetationPresenceDefinition; + class VegetationInstance; class PaintedGrid; class PaintedGridData; class PaintedGridBrush; diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index e89230e..ee87693 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -9,17 +9,20 @@ #include "Scenery.h" #include "TerrainRenderer.h" #include "VegetationDefinition.h" +#include "VegetationLayerDefinition.h" +#include "VegetationPresenceDefinition.h" +#include "VegetationInstance.h" #include "VegetationResult.h" #include "LightComponent.h" -const double DEBUG_DENSITY_FACTOR = 0.5; - -// TEMP +/** + * Grid iterator to collect instances of a layer, in small squares. + */ 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) + VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, VegetationLayerDefinition *layer, bool only_hit): + segment(segment), renderer(renderer), layer(layer), only_hit(only_hit) { } @@ -27,17 +30,24 @@ public: 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; + std::vector instances; + + layer->getPresence()->collectInstances(&instances, *layer->getModel(), x - 0.5, z - 0.5, x + 0.5, z + 0.5); + + for (auto &instance: instances) + { + result = renderer->renderInstance(segment, instance, only_hit); + if (result.hit) + { + return false; + } + } + return true; } private: const SpaceSegment &segment; VegetationRenderer *renderer; - VegetationModelDefinition *model; + VegetationLayerDefinition *layer; RayCastingResult result; bool only_hit; }; @@ -52,10 +62,18 @@ void VegetationRenderer::setEnabled(bool enabled) this->enabled = enabled; } -RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit) +RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit, bool displaced) { - RayCastingResult final; + if (!displaced) + { + // Recursive call on displaced instance + const Vector3 &base = instance.getBase(); + TerrainRenderer::TerrainResult terrain = parent->getTerrainRenderer()->getResult(base.x, base.z, true, true); + VegetationInstance displaced_instance = instance.displace(terrain.location, terrain.normal); + return renderInstance(segment, displaced_instance, only_hit, true); + } + 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())); @@ -78,17 +96,19 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool { if (enabled) { - // 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)) + VegetationDefinition *vegetation = parent->getScenery()->getVegetation(); + int n = vegetation->count(); + // TODO Don't stop at first layer, find the nearest hit + for (int i = 0; i < n; i++) { - return it.getResult(); - } - else - { - return RayCastingResult(); + // Find instances potentially crossing the segment + VegetationGridIterator it(segment, this, vegetation->getVegetationLayer(i), only_hit); + if (not segment.projectedOnYPlane().iterateOnGrid(it)) + { + return it.getResult(); + } } + return RayCastingResult(); } else { diff --git a/src/render/software/VegetationRenderer.h b/src/render/software/VegetationRenderer.h index 42cab58..c7fd1b4 100644 --- a/src/render/software/VegetationRenderer.h +++ b/src/render/software/VegetationRenderer.h @@ -22,8 +22,12 @@ public: /** * Perform ray casting on a single instance. + * + * If *only_hit* is true, only care about hitting or not, do not compute the color. + * + * If *displaced* is true, *instance* is considered on already displaced terrain, else, terrain displacement is applied. */ - RayCastingResult renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit=false); + RayCastingResult renderInstance(const SpaceSegment &segment, const VegetationInstance &instance, bool only_hit=false, bool displaced=false); /** * Perform ray casting on a given segment. diff --git a/src/render/software/software.pro b/src/render/software/software.pro index 583ec81..a9cf031 100644 --- a/src/render/software/software.pro +++ b/src/render/software/software.pro @@ -59,7 +59,6 @@ SOURCES += SoftwareRenderer.cpp \ GodRaysResult.cpp \ VegetationRasterizer.cpp \ VegetationRenderer.cpp \ - VegetationInstance.cpp \ VegetationResult.cpp HEADERS += SoftwareRenderer.h\ @@ -109,7 +108,6 @@ HEADERS += SoftwareRenderer.h\ GodRaysResult.h \ VegetationRasterizer.h \ VegetationRenderer.h \ - VegetationInstance.h \ VegetationResult.h unix:!symbian { diff --git a/src/render/software/software_global.h b/src/render/software/software_global.h index 4996a99..e0f5855 100644 --- a/src/render/software/software_global.h +++ b/src/render/software/software_global.h @@ -55,7 +55,6 @@ namespace software { class GodRaysSampler; class GodRaysResult; class VegetationResult; - class VegetationInstance; class VegetationRenderer; class VegetationModelRenderer; From d937bd08f67dc8bc236ea688474c219100558a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 19 Oct 2015 00:30:20 +0200 Subject: [PATCH 04/17] vegetation: Added basic distribution algorithm --- src/definition/VegetationModelDefinition.cpp | 2 +- .../VegetationPresenceDefinition.cpp | 39 ++++++++++++++++--- src/definition/VegetationPresenceDefinition.h | 4 ++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp index ddb2dd4..81a32fe 100644 --- a/src/definition/VegetationModelDefinition.cpp +++ b/src/definition/VegetationModelDefinition.cpp @@ -155,7 +155,7 @@ void VegetationModelDefinition::randomize() } // Add foliage items - for (int i = 0; i < 150; i++) + for (int i = 0; i < 50; i++) { double radius = 0.15; Vector3 dir = Vector3::randomInSphere(1.0 - radius); diff --git a/src/definition/VegetationPresenceDefinition.cpp b/src/definition/VegetationPresenceDefinition.cpp index e8c0e5d..f45b301 100644 --- a/src/definition/VegetationPresenceDefinition.cpp +++ b/src/definition/VegetationPresenceDefinition.cpp @@ -3,19 +3,46 @@ #include "Scenery.h" #include "TerrainDefinition.h" #include "VegetationLayerDefinition.h" +#include "VegetationModelDefinition.h" #include "VegetationInstance.h" +#include "FloatNode.h" +#include "NoiseNode.h" +#include "NoiseGenerator.h" VegetationPresenceDefinition::VegetationPresenceDefinition(VegetationLayerDefinition *parent): DefinitionNode(parent, "presence") { + noise = new NoiseNode(this); + noise->setLevels(4); + interval = new FloatNode(this, "interval", 0.1); } bool VegetationPresenceDefinition::collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers) const { - // TEMP debug implementation - double x = (xmin + xmax) / 2.0; - double z = (zmax + zmin) / 2.0; - double y = getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, true); - result->push_back(VegetationInstance(model, Vector3(x, y, z), 0.2)); - return true; + bool added = 0; + + const NoiseGenerator *generator = noise->getGenerator(); + double interval_value = interval->getValue(); + + double xstart = xmin - fmod(xmin, interval_value); + double zstart = zmin - fmod(zmin, interval_value); + for (double x = xstart; x < xmax; x += interval_value) + { + for (double z = zstart; z < zmax; z += interval_value) + { + double noise_presence = generator->get2DTotal(x * 0.1, z * 0.1); + if (noise_presence > 0.0) + { + double size = 0.1 + 0.2 * fabs(generator->get2DTotal(z * 10.0, x * 10.0)) * (noise_presence * 0.5 + 0.5); + double angle = 3.0 * generator->get2DTotal(-x * 20.0, z * 20.0); // TODO balanced distribution + double xoffset = fabs(generator->get2DTotal(x * 12.0, -z * 12.0)); + double zoffset = fabs(generator->get2DTotal(-x * 27.0, -z * 27.0)); + double y = getScenery()->getTerrain()->getInterpolatedHeight(x + xoffset, z + zoffset, true, true); + result->push_back(VegetationInstance(model, Vector3(x + xoffset, y, z + zoffset), size, angle)); + added++; + } + } + } + + return added > 0; } diff --git a/src/definition/VegetationPresenceDefinition.h b/src/definition/VegetationPresenceDefinition.h index 1a51f99..776c21f 100644 --- a/src/definition/VegetationPresenceDefinition.h +++ b/src/definition/VegetationPresenceDefinition.h @@ -26,6 +26,10 @@ public: * It's the renderer role to apply the correct displacement. */ bool collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers=true) const; + +private: + NoiseNode *noise; + FloatNode *interval; }; } From cd006e10935d2ba9f2e782e2b819e46417ea437b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 19 Oct 2015 19:14:45 +0200 Subject: [PATCH 05/17] Fixed vegetation renderer being randomly enabled --- src/render/software/VegetationRenderer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index ee87693..d974600 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -55,6 +55,7 @@ private: VegetationRenderer::VegetationRenderer(SoftwareRenderer *parent): parent(parent) { + enabled = true; } void VegetationRenderer::setEnabled(bool enabled) From 68945111d10d10e9b07980e017c1636da1080545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 2 Nov 2015 20:14:35 +0100 Subject: [PATCH 06/17] Improved vegetation rendering - Added early check on terrain height range during iteration - Added test render of vegetation model - Tweaked basic tree model --- src/basics/Disk.cpp | 4 -- src/basics/Disk.h | 2 +- src/basics/SpaceSegment.cpp | 39 +++++++++++ src/basics/SpaceSegment.h | 5 ++ src/definition/VegetationModelDefinition.cpp | 10 +-- src/interface/commandline/tests.cpp | 51 ++++++++++++++ src/render/software/OverlayRasterizer.cpp | 52 ++++++++++++++ src/render/software/OverlayRasterizer.h | 35 ++++++++++ src/render/software/TerrainRenderer.cpp | 15 ++++ src/render/software/TerrainRenderer.h | 5 ++ .../software/VegetationModelRenderer.cpp | 1 - src/render/software/VegetationRenderer.cpp | 70 ++++++++++++------- src/render/software/VegetationRenderer.h | 5 ++ src/render/software/software_global.h | 1 + src/tests/OverlayRasterizer_Test.cpp | 65 +++++++++++++++++ src/tests/SpaceSegment_Test.cpp | 15 ++++ 16 files changed, 338 insertions(+), 37 deletions(-) create mode 100644 src/render/software/OverlayRasterizer.cpp create mode 100644 src/render/software/OverlayRasterizer.h create mode 100644 src/tests/OverlayRasterizer_Test.cpp diff --git a/src/basics/Disk.cpp b/src/basics/Disk.cpp index 3b1cbba..e90146e 100644 --- a/src/basics/Disk.cpp +++ b/src/basics/Disk.cpp @@ -2,10 +2,6 @@ #include "PackStream.h" -Disk::Disk() -{ -} - Disk::Disk(const Vector3 &point, const Vector3 &normal, double radius): InfinitePlane(point, normal), radius(radius) { diff --git a/src/basics/Disk.h b/src/basics/Disk.h index 5493314..738c2bf 100644 --- a/src/basics/Disk.h +++ b/src/basics/Disk.h @@ -14,7 +14,7 @@ namespace basics { class BASICSSHARED_EXPORT Disk: public InfinitePlane { public: - Disk(); + Disk() = default; Disk(const Vector3 &point, const Vector3 &normal, double radius); inline double getRadius() const {return radius;} diff --git a/src/basics/SpaceSegment.cpp b/src/basics/SpaceSegment.cpp index bfec17e..783a515 100644 --- a/src/basics/SpaceSegment.cpp +++ b/src/basics/SpaceSegment.cpp @@ -2,6 +2,7 @@ #include #include "SpaceGridIterator.h" +using namespace std; SpaceSegment::SpaceSegment(const Vector3& start, const Vector3& end): start(start), end(end) @@ -58,6 +59,44 @@ bool SpaceSegment::intersectYInterval(double ymin, double ymax) return true; } +bool SpaceSegment::intersectBoundingBox(const SpaceSegment &bbox) const +{ + Vector3 dir = getDirection(); + // r.dir is unit direction vector of ray + double dfx = 1.0 / dir.x; + double dfy = 1.0 / dir.y; + double dfz = 1.0 / dir.z; + // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner + // r.org is origin of ray + double t1 = (bbox.start.x - start.x) * dfx; + double t2 = (bbox.end.x - start.x) * dfx; + double t3 = (bbox.start.y - start.y) * dfy; + double t4 = (bbox.end.y - start.y) * dfy; + double t5 = (bbox.start.z - start.z) * dfz; + double t6 = (bbox.end.z - start.z) * dfz; + + double tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6)); + double tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); + + // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us + double t; + if (tmax < 0.0) + { + t = tmax; + return false; + } + + // if tmin > tmax, ray doesn't intersect AABB + if (tmin > tmax) + { + t = tmax; + return false; + } + + t = tmin; + return true; +} + SpaceSegment SpaceSegment::projectedOnXPlane(double x) const { return SpaceSegment(Vector3(x, start.y, start.z), Vector3(x, end.y, end.z)); diff --git a/src/basics/SpaceSegment.h b/src/basics/SpaceSegment.h index e6e0f4a..53aea88 100644 --- a/src/basics/SpaceSegment.h +++ b/src/basics/SpaceSegment.h @@ -33,6 +33,11 @@ public: */ bool intersectYInterval(double ymin, double ymax); + /** + * Return true if the segment intersects a bounding box, represented by another segment (crossing diagonal). + */ + bool intersectBoundingBox(const SpaceSegment &bbox) const; + /** * Return a version of this segment, projected on a X plane. */ diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp index 81a32fe..f30baa4 100644 --- a/src/definition/VegetationModelDefinition.cpp +++ b/src/definition/VegetationModelDefinition.cpp @@ -119,7 +119,6 @@ static void addBranchRecurse(std::vector &branches, const Vector 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++) @@ -127,7 +126,8 @@ static void addBranchRecurse(std::vector &branches, const Vector 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)); + Vector3 new_base = base.add(direction.scale(randomizeValue(length, 0.4, 1.0))); + addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.45, 0.6), randomizeValue(length, 0.55, 0.85)); } } } @@ -140,7 +140,7 @@ void VegetationModelDefinition::randomize() foliage_items.clear(); // Add trunk and branches - addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.05, 0.5); + addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.04, 0.5); // Add foliage groups for (const auto &branch: solid_volumes) @@ -150,7 +150,7 @@ void VegetationModelDefinition::randomize() { 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)); + foliage_groups.push_back(Sphere(center, radius * 3.0)); } } @@ -159,7 +159,7 @@ void VegetationModelDefinition::randomize() { 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(); + Vector3 normal = dir.add(Vector3::randomInSphere(0.4)).add(Vector3(0.0, 0.3, 0.0)).normalize(); Disk leaf(dir, normal, randomizeValue(radius, 0.8, 1.0)); foliage_items.push_back(leaf); } diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index eff1c13..10d04cd 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -17,6 +17,12 @@ #include "LightFilter.h" #include "GodRaysSampler.h" #include "Rasterizer.h" +#include "SpaceSegment.h" +#include "OverlayRasterizer.h" +#include "VegetationModelDefinition.h" +#include "VegetationInstance.h" +#include "VegetationRenderer.h" +#include "RayCastingResult.h" #include @@ -251,6 +257,50 @@ static void testSunNearHorizon() } } +static void testVegetationModels() +{ + class TestRasterizer: public OverlayRasterizer + { + public: + TestRasterizer(SoftwareCanvasRenderer *renderer, const VegetationModelDefinition &model): + OverlayRasterizer(renderer, renderer->getProgressHelper()), + instance(model, VECTOR_ZERO), + vegetation(renderer->getVegetationRenderer()) + { + } + + virtual Color processPixel(int, int, double relx, double rely) const override + { + relx *= 0.75; + rely *= 0.75; + SpaceSegment segment(Vector3(relx, rely + 0.5, -5.0), Vector3(relx, rely + 0.5, 5.0)); + RayCastingResult result = vegetation->renderInstance(segment, instance, false, true); + return result.hit ? result.hit_color : Color(0.6, 0.7, 0.9); + } + + VegetationInstance instance; + VegetationRenderer *vegetation; + }; + + Scenery scenery; + scenery.autoPreset(1); + scenery.getClouds()->clear(); + scenery.getTerrain()->propWaterHeight()->setValue(1.0); + scenery.getCamera()->setTarget(VECTOR_ZERO); + scenery.getCamera()->setLocation(Vector3(0.0, 0.0, -5.0)); + int width = 800; + int height = 800; + + SoftwareCanvasRenderer renderer(&scenery); + renderer.setSize(width, height); + renderer.setQuality(0.5); + + VegetationModelDefinition model(NULL); + renderer.setSoloRasterizer(new TestRasterizer(&renderer, model)); + + startTestRender(&renderer, "vegetation_model_basic"); +} + void runTestSuite() { testGroundShadowQuality(); @@ -260,4 +310,5 @@ void runTestSuite() testNearFrustum(); testCloudsNearGround(); testSunNearHorizon(); + testVegetationModels(); } diff --git a/src/render/software/OverlayRasterizer.cpp b/src/render/software/OverlayRasterizer.cpp new file mode 100644 index 0000000..5fcf056 --- /dev/null +++ b/src/render/software/OverlayRasterizer.cpp @@ -0,0 +1,52 @@ +#include "OverlayRasterizer.h" + +#include "Color.h" +#include "SoftwareRenderer.h" +#include "CameraDefinition.h" +#include "CanvasFragment.h" + +OverlayRasterizer::OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress): + Rasterizer(renderer, progress, 0, COLOR_WHITE) +{ +} + +int OverlayRasterizer::prepareRasterization() +{ + return 1; +} + +void OverlayRasterizer::rasterizeToCanvas(CanvasPortion *canvas) +{ + double width = (double)renderer->render_camera->getWidth(); + double height = (double)renderer->render_camera->getHeight(); + Vector3 topleft = renderer->unprojectPoint(Vector3(height, 0.0, 1.0)); + Vector3 bottomleft = renderer->unprojectPoint(Vector3(0.0, 0.0, 1.0)); + Vector3 topright = renderer->unprojectPoint(Vector3(height, width, 1.0)); + Vector3 bottomright = renderer->unprojectPoint(Vector3(0.0, width, 1.0)); + + pushQuad(canvas, topleft, bottomleft, bottomright, topright); +} + +Color OverlayRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const +{ + double width = (double)renderer->render_camera->getWidth() - 1.0; + double height = (double)renderer->render_camera->getHeight() - 1.0; + double relx; + double rely; + double x = floor(fragment.getPixel().x); + double y = floor(fragment.getPixel().y); + + if (width > height) + { + relx = 2.0 * ((x - (width - height) * 0.5) / height - 0.5); + rely = 2.0 * (y / height - 0.5); + } + else + { + relx = 2.0 * (x / height - 0.5); + rely = 2.0 * ((y - (height - width) * 0.5) / height - 0.5); + } + + return processPixel((int)x, (int)y, relx, rely); +} + diff --git a/src/render/software/OverlayRasterizer.h b/src/render/software/OverlayRasterizer.h new file mode 100644 index 0000000..65b0d64 --- /dev/null +++ b/src/render/software/OverlayRasterizer.h @@ -0,0 +1,35 @@ +#ifndef OVERLAYRASTERIZER_H +#define OVERLAYRASTERIZER_H + +#include "software_global.h" + +#include "Rasterizer.h" + +namespace paysages { +namespace software { + +/** + * Base class for overlay rasterizer. + * + * It's a rasterizer that puts a single quad in front of camera, in order to apply a shader on each pixel. + */ +class SOFTWARESHARED_EXPORT OverlayRasterizer: public Rasterizer +{ +public: + OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress); + + /** + * Abstract method to implement to shade each pixel. + */ + virtual Color processPixel(int x, int y, double relx, double rely) const = 0; + +private: + virtual int prepareRasterization(); + virtual void rasterizeToCanvas(CanvasPortion *canvas); + virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const; +}; + +} +} + +#endif // OVERLAYRASTERIZER_H diff --git a/src/render/software/TerrainRenderer.cpp b/src/render/software/TerrainRenderer.cpp index 033a5c0..3553c85 100644 --- a/src/render/software/TerrainRenderer.cpp +++ b/src/render/software/TerrainRenderer.cpp @@ -220,3 +220,18 @@ bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at) return true; } } + +void TerrainRenderer::estimateMinMaxHeight(double x1, double z1, double x2, double z2, double *ymin, double *ymax) +{ + double y1 = getHeight(x1, z1, true); + double y2 = getHeight(x2, z2, true); + + // TODO Add quality factor + // TODO Use all 4 corners + // TODO Apply max slope + // TODO Estimate displacement + + std::pairminmax = std::minmax(y1, y2); + *ymin = minmax.first; + *ymax = minmax.second; +} diff --git a/src/render/software/TerrainRenderer.h b/src/render/software/TerrainRenderer.h index ec462ba..d901724 100644 --- a/src/render/software/TerrainRenderer.h +++ b/src/render/software/TerrainRenderer.h @@ -34,6 +34,11 @@ public: virtual Color getFinalColor(const Vector3 &location, double precision); virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override; + /** + * Estimate a probable range of altitudes, given a rectangle area. + */ + void estimateMinMaxHeight(double x1, double z1, double x2, double z2, double *ymin, double *ymax); + private: SoftwareRenderer *parent; TerrainRayWalker *walker_ray; diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp index 1446112..1fe6eb2 100644 --- a/src/render/software/VegetationModelRenderer.cpp +++ b/src/render/software/VegetationModelRenderer.cpp @@ -113,7 +113,6 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, material.shininess = 3.0; material.hardness = 0.3; material.validate(); - // FIXME Can't use reference to temporary material return VegetationResult(location, normal, material); } else diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index d974600..89ff530 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -21,8 +21,8 @@ class VegetationGridIterator: public SpaceGridIterator { public: - VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, VegetationLayerDefinition *layer, bool only_hit): - segment(segment), renderer(renderer), layer(layer), only_hit(only_hit) + VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, bool only_hit): + segment(segment), renderer(renderer), only_hit(only_hit) { } @@ -30,24 +30,12 @@ public: virtual bool onCell(int x, int, int z) override { - std::vector instances; - - layer->getPresence()->collectInstances(&instances, *layer->getModel(), x - 0.5, z - 0.5, x + 0.5, z + 0.5); - - for (auto &instance: instances) - { - result = renderer->renderInstance(segment, instance, only_hit); - if (result.hit) - { - return false; - } - } - return true; + result = renderer->getBoundResult(segment, (double)x, (double)z, only_hit); + return not result.hit; } private: const SpaceSegment &segment; VegetationRenderer *renderer; - VegetationLayerDefinition *layer; RayCastingResult result; bool only_hit; }; @@ -97,17 +85,11 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool { if (enabled) { - VegetationDefinition *vegetation = parent->getScenery()->getVegetation(); - int n = vegetation->count(); - // TODO Don't stop at first layer, find the nearest hit - for (int i = 0; i < n; i++) + // Find instances potentially crossing the segment + VegetationGridIterator it(segment, this, only_hit); + if (not segment.projectedOnYPlane().iterateOnGrid(it)) { - // Find instances potentially crossing the segment - VegetationGridIterator it(segment, this, vegetation->getVegetationLayer(i), only_hit); - if (not segment.projectedOnYPlane().iterateOnGrid(it)) - { - return it.getResult(); - } + return it.getResult(); } return RayCastingResult(); } @@ -117,6 +99,42 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool } } +RayCastingResult VegetationRenderer::getBoundResult(const SpaceSegment &segment, double x, double z, bool only_hit, double xsize, double zsize) +{ + // Early check if we may cross any vegetation + double ymin, ymax; + double vegetation_max_height = 0.0; // TODO + parent->getTerrainRenderer()->estimateMinMaxHeight(x, z, x + xsize, z + zsize, &ymin, &ymax); + ymax += vegetation_max_height; + SpaceSegment bbox(Vector3(x, ymin, z), Vector3(x + xsize, ymax, z + zsize)); + if (not segment.intersectBoundingBox(bbox)) { + return RayCastingResult(); + } + + // Iterate all layers and instances + VegetationDefinition *vegetation = parent->getScenery()->getVegetation(); + int n = vegetation->count(); + for (int i = 0; i < n; i++) + { + VegetationLayerDefinition *layer = vegetation->getVegetationLayer(i); + + std::vector instances; + layer->getPresence()->collectInstances(&instances, *layer->getModel(), x, z, x + xsize, z + zsize); + + for (auto &instance: instances) + { + RayCastingResult result = renderInstance(segment, instance, only_hit); + if (result.hit) + { + // TODO Don't stop at first hit, find the nearest one + return result; + } + } + } + + return RayCastingResult(); +} + bool VegetationRenderer::applyLightFilter(LightComponent &light, const Vector3 &at) { if (enabled) diff --git a/src/render/software/VegetationRenderer.h b/src/render/software/VegetationRenderer.h index c7fd1b4..ffb3fcb 100644 --- a/src/render/software/VegetationRenderer.h +++ b/src/render/software/VegetationRenderer.h @@ -34,6 +34,11 @@ public: */ RayCastingResult getResult(const SpaceSegment &segment, bool only_hit=false); + /** + * Perform ray casting on a squared region. + */ + RayCastingResult getBoundResult(const SpaceSegment &segment, double x, double z, bool only_hit=false, double xsize=1.0, double zsize=1.0); + virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override; private: diff --git a/src/render/software/software_global.h b/src/render/software/software_global.h index e0f5855..b1ca866 100644 --- a/src/render/software/software_global.h +++ b/src/render/software/software_global.h @@ -36,6 +36,7 @@ namespace software { class WaterRenderer; class Rasterizer; + class OverlayRasterizer; class SkyRasterizer; class TerrainRasterizer; diff --git a/src/tests/OverlayRasterizer_Test.cpp b/src/tests/OverlayRasterizer_Test.cpp new file mode 100644 index 0000000..6bb6acc --- /dev/null +++ b/src/tests/OverlayRasterizer_Test.cpp @@ -0,0 +1,65 @@ +#include "BaseTestCase.h" +#include "OverlayRasterizer.h" + +#include +#include "Scenery.h" +#include "SoftwareCanvasRenderer.h" +#include "Color.h" + +typedef struct { + int x; + int y; + double relx; + double rely; +} PixelCall; + +std::vector calls; + +class MockOverlayRasterizer: public OverlayRasterizer +{ +public: + MockOverlayRasterizer(SoftwareCanvasRenderer *renderer): + OverlayRasterizer(renderer, renderer->getProgressHelper()) + { + } + + virtual Color processPixel(int x, int y, double relx, double rely) const override + { + PixelCall call = {x, y, relx, rely}; + calls.push_back(call); + return COLOR_BLUE; + } +}; + +void checkCall(const PixelCall &call, int x, int y, double relx, double rely) +{ + EXPECT_EQ(x, call.x); + EXPECT_EQ(y, call.y); + EXPECT_DOUBLE_EQ(relx, call.relx); + EXPECT_DOUBLE_EQ(rely, call.rely); +} + +TEST(OverlayRasterizer, pixelProcessing) +{ + calls.clear(); + + Scenery scenery; + SoftwareCanvasRenderer renderer(&scenery); + renderer.setSize(4, 3); + renderer.setSoloRasterizer(new MockOverlayRasterizer(&renderer)); + renderer.render(); + + ASSERT_EQ(12, (int)calls.size()); + checkCall(calls[0], 0, 0, -1.5, -1.0); + checkCall(calls[1], 0, 2, -1.5, 1.0); + checkCall(calls[2], 2, 0, 0.5, -1.0); + checkCall(calls[3], 2, 2, 0.5, 1.0); + checkCall(calls[4], 0, 1, -1.5, 0.0); + checkCall(calls[5], 1, 0, -0.5, -1.0); + checkCall(calls[6], 1, 1, -0.5, 0.0); + checkCall(calls[7], 1, 2, -0.5, 1.0); + checkCall(calls[8], 2, 1, 0.5, 0.0); + checkCall(calls[9], 3, 0, 1.5, -1.0); + checkCall(calls[10], 3, 1, 1.5, 0.0); + checkCall(calls[11], 3, 2, 1.5, 1.0); +} diff --git a/src/tests/SpaceSegment_Test.cpp b/src/tests/SpaceSegment_Test.cpp index c7c219f..90876fe 100644 --- a/src/tests/SpaceSegment_Test.cpp +++ b/src/tests/SpaceSegment_Test.cpp @@ -59,3 +59,18 @@ TEST(SpaceSegment, iterateOnGrid_Negative) ASSERT_EQ(1, (int)it.locations.size()); EXPECT_VECTOR3_COORDS(it.locations[0], -9.0, -9.0, -9.0); } + +TEST(SpaceSegment, intersectBoundingBox) +{ + SpaceSegment bbox(Vector3(-1.0, -1.0, -1.0), Vector3(1.0, 4.0, 1.0)); + + EXPECT_TRUE(SpaceSegment(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0)).intersectBoundingBox(bbox)); + + EXPECT_TRUE(SpaceSegment(Vector3(-5.0, 0.0, 0.0), Vector3(5.0, 0.0, 0.0)).intersectBoundingBox(bbox)); + EXPECT_FALSE(SpaceSegment(Vector3(-5.0, -2.0, 0.0), Vector3(5.0, -2.0, 0.0)).intersectBoundingBox(bbox)); + + EXPECT_TRUE(SpaceSegment(Vector3(-5.0, 0.0, -5.0), Vector3(5.0, 0.0, 5.0)).intersectBoundingBox(bbox)); + EXPECT_FALSE(SpaceSegment(Vector3(-5.0, -2.0, 0.0), Vector3(5.0, -2.0, 5.0)).intersectBoundingBox(bbox)); + + EXPECT_FALSE(SpaceSegment(Vector3(-10.0, 0.0, 0.0), Vector3(10.0, -6.0, 0.0)).intersectBoundingBox(bbox)); +} From 2560fe2c53c87593b02f389632e8893fcb4c7ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 2 Nov 2015 23:00:02 +0100 Subject: [PATCH 07/17] Fixed holes in vegetation rendering --- src/definition/VegetationDefinition.cpp | 17 +++++++++++++++++ src/definition/VegetationDefinition.h | 5 +++++ src/definition/VegetationLayerDefinition.cpp | 5 +++++ src/definition/VegetationLayerDefinition.h | 2 ++ src/definition/VegetationPresenceDefinition.cpp | 15 +++++++++++++++ src/definition/VegetationPresenceDefinition.h | 2 ++ src/interface/commandline/tests.cpp | 10 +++++++--- src/render/software/VegetationRenderer.cpp | 6 +++--- 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/definition/VegetationDefinition.cpp b/src/definition/VegetationDefinition.cpp index 9437560..845a8fa 100644 --- a/src/definition/VegetationDefinition.cpp +++ b/src/definition/VegetationDefinition.cpp @@ -13,6 +13,23 @@ VegetationDefinition::VegetationDefinition(DefinitionNode* parent) : { } +double VegetationDefinition::getMaxHeight() const +{ + double max_height = 0.0; + int n = count(); + + for (int i = 0; i < n; i++) + { + double layer_height = getVegetationLayer(i)->getMaxHeight(); + if (layer_height > max_height) + { + max_height = layer_height; + } + } + + return max_height; +} + void VegetationDefinition::applyPreset(VegetationPreset preset) { VegetationLayerDefinition *layer; diff --git a/src/definition/VegetationDefinition.h b/src/definition/VegetationDefinition.h index aea9e11..9480a0a 100644 --- a/src/definition/VegetationDefinition.h +++ b/src/definition/VegetationDefinition.h @@ -21,6 +21,11 @@ public: */ inline VegetationLayerDefinition* getVegetationLayer(int position) const {return (VegetationLayerDefinition*)getLayer(position);} + /** + * Get the max height of all layers assembled. + */ + double getMaxHeight() const; + typedef enum { VEGETATION_PRESET_TEMPERATE diff --git a/src/definition/VegetationLayerDefinition.cpp b/src/definition/VegetationLayerDefinition.cpp index 79e53fa..f906f44 100644 --- a/src/definition/VegetationLayerDefinition.cpp +++ b/src/definition/VegetationLayerDefinition.cpp @@ -10,6 +10,11 @@ VegetationLayerDefinition::VegetationLayerDefinition(DefinitionNode* parent) : presence = new VegetationPresenceDefinition(this); } +double VegetationLayerDefinition::getMaxHeight() const +{ + return presence->getMaxHeight(); +} + void VegetationLayerDefinition::applyPreset(VegetationLayerPreset preset) { model->randomize(); diff --git a/src/definition/VegetationLayerDefinition.h b/src/definition/VegetationLayerDefinition.h index 8fe775b..6a9e41d 100644 --- a/src/definition/VegetationLayerDefinition.h +++ b/src/definition/VegetationLayerDefinition.h @@ -19,6 +19,8 @@ public: inline const VegetationPresenceDefinition *getPresence() const {return presence;} inline const VegetationModelDefinition *getModel() const {return model;} + double getMaxHeight() const; + typedef enum { VEGETATION_BASIC_TREES diff --git a/src/definition/VegetationPresenceDefinition.cpp b/src/definition/VegetationPresenceDefinition.cpp index f45b301..c77e7d0 100644 --- a/src/definition/VegetationPresenceDefinition.cpp +++ b/src/definition/VegetationPresenceDefinition.cpp @@ -19,6 +19,16 @@ VegetationPresenceDefinition::VegetationPresenceDefinition(VegetationLayerDefini bool VegetationPresenceDefinition::collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers) const { + if (outcomers) + { + // Expand the area to include outcoming instances + double max_radius = getMaxHeight(); + xmin -= max_radius; + zmin -= max_radius; + xmax += max_radius; + zmax += max_radius; + } + bool added = 0; const NoiseGenerator *generator = noise->getGenerator(); @@ -46,3 +56,8 @@ bool VegetationPresenceDefinition::collectInstances(std::vector 0; } + +double VegetationPresenceDefinition::getMaxHeight() const +{ + return 0.3; +} diff --git a/src/definition/VegetationPresenceDefinition.h b/src/definition/VegetationPresenceDefinition.h index 776c21f..8983c3f 100644 --- a/src/definition/VegetationPresenceDefinition.h +++ b/src/definition/VegetationPresenceDefinition.h @@ -27,6 +27,8 @@ public: */ bool collectInstances(std::vector *result, const VegetationModelDefinition &model, double xmin, double zmin, double xmax, double zmax, bool outcomers=true) const; + double getMaxHeight() const; + private: NoiseNode *noise; FloatNode *interval; diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index 10d04cd..b6ad79b 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -295,10 +295,14 @@ static void testVegetationModels() renderer.setSize(width, height); renderer.setQuality(0.5); - VegetationModelDefinition model(NULL); - renderer.setSoloRasterizer(new TestRasterizer(&renderer, model)); + for (int i = 0; i < 10; i++) + { + // TODO Make random sequence repeatable + VegetationModelDefinition model(NULL); + renderer.setSoloRasterizer(new TestRasterizer(&renderer, model)); - startTestRender(&renderer, "vegetation_model_basic"); + startTestRender(&renderer, "vegetation_model_basic", i); + } } void runTestSuite() diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index 89ff530..a04dff2 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -101,18 +101,18 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool RayCastingResult VegetationRenderer::getBoundResult(const SpaceSegment &segment, double x, double z, bool only_hit, double xsize, double zsize) { + VegetationDefinition *vegetation = parent->getScenery()->getVegetation(); + // Early check if we may cross any vegetation double ymin, ymax; - double vegetation_max_height = 0.0; // TODO parent->getTerrainRenderer()->estimateMinMaxHeight(x, z, x + xsize, z + zsize, &ymin, &ymax); - ymax += vegetation_max_height; + ymax += vegetation->getMaxHeight(); SpaceSegment bbox(Vector3(x, ymin, z), Vector3(x + xsize, ymax, z + zsize)); if (not segment.intersectBoundingBox(bbox)) { return RayCastingResult(); } // Iterate all layers and instances - VegetationDefinition *vegetation = parent->getScenery()->getVegetation(); int n = vegetation->count(); for (int i = 0; i < n; i++) { From 1e12b8ac9c5df71b3a0b84bd1c1b7685250292a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 4 Nov 2015 01:08:48 +0100 Subject: [PATCH 08/17] vegetation: Now using sphere cap for leaves, to avoid flat effect --- src/definition/VegetationModelDefinition.cpp | 3 +- .../software/VegetationModelRenderer.cpp | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp index f30baa4..78f917e 100644 --- a/src/definition/VegetationModelDefinition.cpp +++ b/src/definition/VegetationModelDefinition.cpp @@ -158,9 +158,10 @@ void VegetationModelDefinition::randomize() for (int i = 0; i < 50; i++) { double radius = 0.15; + double scale = randomizeValue(radius, 0.5, 1.0); Vector3 dir = Vector3::randomInSphere(1.0 - radius); Vector3 normal = dir.add(Vector3::randomInSphere(0.4)).add(Vector3(0.0, 0.3, 0.0)).normalize(); - Disk leaf(dir, normal, randomizeValue(radius, 0.8, 1.0)); + Disk leaf(dir, normal, scale); foliage_items.push_back(leaf); } } diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp index 1fe6eb2..8ca8ab3 100644 --- a/src/render/software/VegetationModelRenderer.cpp +++ b/src/render/software/VegetationModelRenderer.cpp @@ -69,11 +69,26 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, 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) + Sphere leafcap(leaf.getPoint(), leaf.getRadius() * leaf.getRadius() / foliage.getRadius()); + // TODO Add cap intersection to Sphere class + Vector3 capnear, capfar; + if (leafcap.checkRayIntersection(subray, &capnear, &capfar) == 2) { - near = near.scale(foliage.getRadius()).add(foliage.getCenter()); - distance = ray.getCursor(near); + if (capnear.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) + { + if (capfar.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) + { + continue; + } + else + { + capnear = capfar; + } + } + + Vector3 capnormal = capnear.sub(leaf.getPoint()).normalize(); + capnear = capnear.scale(foliage.getRadius()).add(foliage.getCenter()); + distance = ray.getCursor(capnear); if (distance >= 0.0 and distance <= maximal) { @@ -89,8 +104,8 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, nearest = distance; hit = true; - location = near; - normal = sized_leaf.getNormal(); + location = capnear; + normal = capnormal; if (normal.dotProduct(location.sub(segment.getStart())) > 0.0) { @@ -109,9 +124,9 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, if (hit) { SurfaceMaterial material(result); - material.reflection = 0.003; - material.shininess = 3.0; - material.hardness = 0.3; + material.reflection = 0.001; + material.shininess = 2.0; + material.hardness = 0.1; material.validate(); return VegetationResult(location, normal, material); } From 72877c4361e3284433c3764ddb839bcd3daa79fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 9 Nov 2015 01:07:33 +0100 Subject: [PATCH 09/17] Tweaked vegetation rendering, and improved render tests --- src/definition/VegetationModelDefinition.cpp | 23 +++++++-- src/definition/VegetationModelDefinition.h | 3 ++ src/interface/commandline/tests.cpp | 50 ++++++++++++------- .../software/SoftwareCanvasRenderer.cpp | 20 +++----- src/render/software/SoftwareCanvasRenderer.h | 17 ++++--- .../software/VegetationModelRenderer.cpp | 14 ++---- 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp index 78f917e..7ac3fb9 100644 --- a/src/definition/VegetationModelDefinition.cpp +++ b/src/definition/VegetationModelDefinition.cpp @@ -4,13 +4,23 @@ #include "RandomGenerator.h" #include "Matrix4.h" #include "SurfaceMaterial.h" +#include "Color.h" #include "PackStream.h" VegetationModelDefinition::VegetationModelDefinition(DefinitionNode *parent): DefinitionNode(parent, "model") { - solid_material = new SurfaceMaterial(); - foliage_material = new SurfaceMaterial(); + solid_material = new SurfaceMaterial(Color(0.2, 0.15, 0.15)); + solid_material->reflection = 0.002; + solid_material->shininess = 2.0; + solid_material->hardness = 0.3; + solid_material->validate(); + + foliage_material = new SurfaceMaterial(Color(0.4, 0.8, 0.45)); + foliage_material->reflection = 0.007; + foliage_material->shininess = 12.0; + foliage_material->hardness = 0.2; + foliage_material->validate(); randomize(); } @@ -127,7 +137,10 @@ static void addBranchRecurse(std::vector &branches, const Vector new_direction = pivot2.multPoint(new_direction); Vector3 new_base = base.add(direction.scale(randomizeValue(length, 0.4, 1.0))); - addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.45, 0.6), randomizeValue(length, 0.55, 0.85)); + if (new_base.add(new_direction).y > 0.1) + { + addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.45, 0.6), randomizeValue(length, 0.55, 0.85)); + } } } } @@ -140,7 +153,7 @@ void VegetationModelDefinition::randomize() foliage_items.clear(); // Add trunk and branches - addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.04, 0.5); + addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, randomizeValue(0.05, 0.6, 1.0), randomizeValue(0.5, 0.8, 1.0)); // Add foliage groups for (const auto &branch: solid_volumes) @@ -155,7 +168,7 @@ void VegetationModelDefinition::randomize() } // Add foliage items - for (int i = 0; i < 50; i++) + for (int i = 0; i < 30; i++) { double radius = 0.15; double scale = randomizeValue(radius, 0.5, 1.0); diff --git a/src/definition/VegetationModelDefinition.h b/src/definition/VegetationModelDefinition.h index 6ed0823..dc2efd3 100644 --- a/src/definition/VegetationModelDefinition.h +++ b/src/definition/VegetationModelDefinition.h @@ -21,6 +21,9 @@ public: VegetationModelDefinition(DefinitionNode *parent); virtual ~VegetationModelDefinition(); + inline const SurfaceMaterial &getSolidMaterial() const {return *solid_material;} + inline const SurfaceMaterial &getFoliageMaterial() const {return *foliage_material;} + 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;} diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index b6ad79b..77280f5 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -110,12 +110,12 @@ static void testCloudQuality() SoftwareCanvasRenderer renderer(&scenery); renderer.setSize(600, 800); - SkyRasterizer *rasterizer = new SkyRasterizer(&renderer, renderer.getProgressHelper(), 0); - renderer.setSoloRasterizer(rasterizer); + SkyRasterizer rasterizer(&renderer, renderer.getProgressHelper(), 0); + renderer.setSoloRasterizer(&rasterizer); for (int i = 0; i < 6; i++) { renderer.setQuality((double)i / 5.0); - rasterizer->setQuality(0.2); + rasterizer.setQuality(0.2); startTestRender(&renderer, "cloud_quality", i); } } @@ -164,8 +164,8 @@ static void testGodRays() TestRenderer renderer(&scenery); renderer.setSize(500, 300); - SkyRasterizer *rasterizer = new SkyRasterizer(&renderer, renderer.getProgressHelper(), 0); - renderer.setSoloRasterizer(rasterizer); + SkyRasterizer rasterizer(&renderer, renderer.getProgressHelper(), 0); + renderer.setSoloRasterizer(&rasterizer); TestLightFilter filter; renderer.getLightingManager()->clearFilters(); renderer.getLightingManager()->registerFilter(&filter); @@ -174,7 +174,7 @@ static void testGodRays() for (int i = 0; i < 6; i++) { renderer.setQuality((double)i / 5.0); - rasterizer->setQuality(0.2); + rasterizer.setQuality(0.2); startTestRender(&renderer, "god_rays_quality", i); } renderer.setQuality(0.5); @@ -259,25 +259,42 @@ static void testSunNearHorizon() static void testVegetationModels() { - class TestRasterizer: public OverlayRasterizer + class InstanceRenderer: public SoftwareCanvasRenderer, public OverlayRasterizer, public LightFilter { public: - TestRasterizer(SoftwareCanvasRenderer *renderer, const VegetationModelDefinition &model): - OverlayRasterizer(renderer, renderer->getProgressHelper()), + InstanceRenderer(Scenery *scenery, const VegetationModelDefinition &model): + SoftwareCanvasRenderer(scenery), + OverlayRasterizer(this, this->getProgressHelper()), instance(model, VECTOR_ZERO), vegetation(renderer->getVegetationRenderer()) { } + virtual void prepare() override + { + SoftwareCanvasRenderer::prepare(); + getLightingManager()->clearFilters(); + getLightingManager()->registerFilter(this); + // TODO Add filter for vegetation instance (for self shadows) + } + virtual Color applyMediumTraversal(const Vector3&, const Color &color) override + { + return color; + } virtual Color processPixel(int, int, double relx, double rely) const override { relx *= 0.75; rely *= 0.75; - SpaceSegment segment(Vector3(relx, rely + 0.5, -5.0), Vector3(relx, rely + 0.5, 5.0)); + SpaceSegment segment(Vector3(relx, rely + 0.5, 5.0), Vector3(relx, rely + 0.5, -5.0)); RayCastingResult result = vegetation->renderInstance(segment, instance, false, true); return result.hit ? result.hit_color : Color(0.6, 0.7, 0.9); } - + virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override + { + SpaceSegment segment(at, at.add(light.direction.scale(-5.0))); + RayCastingResult result = vegetation->renderInstance(segment, instance, true, true); + return not result.hit; + } VegetationInstance instance; VegetationRenderer *vegetation; }; @@ -285,21 +302,18 @@ static void testVegetationModels() Scenery scenery; scenery.autoPreset(1); scenery.getClouds()->clear(); - scenery.getTerrain()->propWaterHeight()->setValue(1.0); scenery.getCamera()->setTarget(VECTOR_ZERO); - scenery.getCamera()->setLocation(Vector3(0.0, 0.0, -5.0)); + scenery.getCamera()->setLocation(Vector3(0.0, 0.0, 5.0)); int width = 800; int height = 800; - SoftwareCanvasRenderer renderer(&scenery); - renderer.setSize(width, height); - renderer.setQuality(0.5); - for (int i = 0; i < 10; i++) { // TODO Make random sequence repeatable VegetationModelDefinition model(NULL); - renderer.setSoloRasterizer(new TestRasterizer(&renderer, model)); + InstanceRenderer renderer(&scenery, model); + renderer.setSize(width, height); + renderer.setSoloRasterizer(&renderer); startTestRender(&renderer, "vegetation_model_basic", i); } diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index 0e88a12..47b7735 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -28,10 +28,10 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery): postprocess_enabled = true; - 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)); + rasterizers.push_back(rasterizer_sky = new SkyRasterizer(this, progress, RASTERIZER_CLIENT_SKY)); + rasterizers.push_back(rasterizer_water = new WaterRasterizer(this, progress, RASTERIZER_CLIENT_WATER)); + rasterizers.push_back(rasterizer_terrain = new TerrainRasterizer(this, progress, RASTERIZER_CLIENT_TERRAIN)); + rasterizers.push_back(rasterizer_vegetation = new VegetationRasterizer(this, progress, RASTERIZER_CLIENT_VEGETATION)); current_work = NULL; } @@ -41,10 +41,10 @@ SoftwareCanvasRenderer::~SoftwareCanvasRenderer() delete canvas; delete progress; - for (auto &rasterizer: rasterizers) - { - delete rasterizer; - } + delete rasterizer_sky; + delete rasterizer_water; + delete rasterizer_terrain; + delete rasterizer_vegetation; } void SoftwareCanvasRenderer::setQuality(double factor) @@ -59,10 +59,6 @@ void SoftwareCanvasRenderer::setQuality(double factor) void SoftwareCanvasRenderer::setSoloRasterizer(Rasterizer *rasterizer) { - for (auto &rast: rasterizers) - { - delete rast; - } rasterizers.clear(); rasterizers.push_back(rasterizer); } diff --git a/src/render/software/SoftwareCanvasRenderer.h b/src/render/software/SoftwareCanvasRenderer.h index 6eea200..7e0713a 100644 --- a/src/render/software/SoftwareCanvasRenderer.h +++ b/src/render/software/SoftwareCanvasRenderer.h @@ -26,16 +26,15 @@ public: inline RenderProgress *getProgressHelper() const {return progress;} inline bool isFinished() const {return finished;} - inline Rasterizer *getSkyRasterizer() const {return rasterizers[0];} - inline Rasterizer *getWaterRasterizer() const {return rasterizers[1];} - inline Rasterizer *getTerrainRasterizer() const {return rasterizers[2];} + inline Rasterizer *getSkyRasterizer() const {return rasterizer_sky;} + inline Rasterizer *getWaterRasterizer() const {return rasterizer_water;} + inline Rasterizer *getTerrainRasterizer() const {return rasterizer_terrain;} + inline Rasterizer *getVegetationRasterizer() const {return rasterizer_vegetation;} virtual void setQuality(double factor) override; /** * Clear the rasterizers list, and put a single one. - * - * The renderer takes ownership of the rasterizer. */ void setSoloRasterizer(Rasterizer *rasterizer); @@ -99,7 +98,13 @@ private: Canvas *canvas; int samples; - std::vector rasterizers; + + std::vector rasterizers; + Rasterizer *rasterizer_sky; + Rasterizer *rasterizer_water; + Rasterizer *rasterizer_terrain; + Rasterizer *rasterizer_vegetation; + bool started; bool finished; bool interrupted; diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp index 8ca8ab3..b6161a6 100644 --- a/src/render/software/VegetationModelRenderer.cpp +++ b/src/render/software/VegetationModelRenderer.cpp @@ -23,7 +23,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, { InfiniteRay ray(segment.getStart(), segment.getDirection()); int intersections; - Color result = COLOR_TRANSPARENT; + const SurfaceMaterial *material = &SurfaceMaterial::getDefault(); bool hit = false; Vector3 location, normal; double distance, nearest, maximal; @@ -47,7 +47,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, if (distance < nearest) { - result = Color(0.2, 0.15, 0.15); + material = &model->getSolidMaterial(); nearest = distance; hit = true; @@ -100,7 +100,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, if (distance < nearest) { - result = Color(0.3, 0.5, 0.3); + material = &model->getFoliageMaterial(); nearest = distance; hit = true; @@ -110,7 +110,6 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, 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); } } @@ -123,12 +122,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, if (hit) { - SurfaceMaterial material(result); - material.reflection = 0.001; - material.shininess = 2.0; - material.hardness = 0.1; - material.validate(); - return VegetationResult(location, normal, material); + return VegetationResult(location, normal, *material); } else { From 159e0f7e817c6b639bbbef026600524f339a2b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 10 Nov 2015 00:15:30 +0100 Subject: [PATCH 10/17] Optimized vegetation branches rendering --- src/basics/CappedCylinder.cpp | 3 - src/basics/CappedCylinder.h | 2 +- src/basics/InfiniteCylinder.cpp | 145 +++++++++---------- src/basics/InfiniteCylinder.h | 14 +- src/basics/InfiniteRay.cpp | 3 - src/basics/InfiniteRay.h | 2 +- src/basics/SpaceSegment.cpp | 8 +- src/basics/Sphere.cpp | 3 - src/basics/Sphere.h | 2 +- src/definition/VegetationInstance.cpp | 2 +- src/definition/VegetationLayerDefinition.cpp | 2 +- 11 files changed, 91 insertions(+), 95 deletions(-) diff --git a/src/basics/CappedCylinder.cpp b/src/basics/CappedCylinder.cpp index 8997e97..d508284 100644 --- a/src/basics/CappedCylinder.cpp +++ b/src/basics/CappedCylinder.cpp @@ -3,9 +3,6 @@ #include "Vector3.h" #include "PackStream.h" -CappedCylinder::CappedCylinder() { -} - CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length) : InfiniteCylinder(InfiniteRay(base, direction), radius), length(length) { } diff --git a/src/basics/CappedCylinder.h b/src/basics/CappedCylinder.h index 0b92956..dddf1b5 100644 --- a/src/basics/CappedCylinder.h +++ b/src/basics/CappedCylinder.h @@ -13,7 +13,7 @@ namespace basics { */ class BASICSSHARED_EXPORT CappedCylinder : public InfiniteCylinder { public: - CappedCylinder(); + CappedCylinder() = default; CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length); inline double getLength() const { diff --git a/src/basics/InfiniteCylinder.cpp b/src/basics/InfiniteCylinder.cpp index 4a7ae64..d13e230 100644 --- a/src/basics/InfiniteCylinder.cpp +++ b/src/basics/InfiniteCylinder.cpp @@ -4,10 +4,8 @@ #define EPS 1E-8 -InfiniteCylinder::InfiniteCylinder() { -} - InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius) : axis(axis), radius(radius) { + validate(); } int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, @@ -16,11 +14,75 @@ int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *firs * Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin. * Maybe some optimizations could be made from this. */ + Vector3 Q, G, AG, AQ; + double c0, c1, c2, discr, invC2, root0, root1, root; - Vector3 U, V, F = axis.getDirection(), P, B, Q, G, AG, AQ; + /* line */ + Q = ray.getOrigin().sub(axis.getOrigin()); + G = ray.getDirection(); + + /* compute A*G */ + AG.x = A[0][0] * G.x + A[0][1] * G.y + A[0][2] * G.z; + AG.y = A[1][0] * G.x + A[1][1] * G.y + A[1][2] * G.z; + AG.z = A[2][0] * G.x + A[2][1] * G.y + A[2][2] * G.z; + + /* compute A*Q */ + AQ.x = A[0][0] * Q.x + A[0][1] * Q.y + A[0][2] * Q.z; + AQ.y = A[1][0] * Q.x + A[1][1] * Q.y + A[1][2] * Q.z; + AQ.z = A[2][0] * Q.x + A[2][1] * Q.y + A[2][2] * Q.z; + + /* compute quadratic equation c0+c1*t+c2*t^2 = 0 */ + c2 = G.x * AG.x + G.y * AG.y + G.z * AG.z; + c1 = 2.0f * (Q.x * AG.x + Q.y * AG.y + Q.z * AG.z); + c0 = Q.x * AQ.x + Q.y * AQ.y + Q.z * AQ.z + C; + + /* solve for intersections */ + int numIntersections; + discr = c1 * c1 - 4.0 * c0 * c2; + if (discr > EPS) { + numIntersections = 2; + discr = sqrt(discr); + invC2 = 1.0 / c2; + root0 = -0.5 * (c1 + discr) * invC2; + root1 = -0.5 * (c1 - discr) * invC2; + first_intersection->x = Q.x + root0 * G.x + ox; + first_intersection->y = Q.y + root0 * G.y + oy; + first_intersection->z = Q.z + root0 * G.z + oz; + second_intersection->x = Q.x + root1 * G.x + ox; + second_intersection->y = Q.y + root1 * G.y + oy; + second_intersection->z = Q.z + root1 * G.z + oz; + } else if (discr < -EPS) { + numIntersections = 0; + } else { + numIntersections = 1; + root = -0.5 * c1 / c2; + first_intersection->x = Q.x + root * G.x + ox; + first_intersection->y = Q.y + root * G.y + oy; + first_intersection->z = Q.z + root * G.z + oz; + } + + return numIntersections; +} + +void InfiniteCylinder::save(PackStream *stream) const { + axis.save(stream); + stream->write(&radius); +} + +void InfiniteCylinder::load(PackStream *stream) { + axis.load(stream); + stream->read(&radius); + + validate(); +} + +void InfiniteCylinder::validate() { + Vector3 U, V, F = axis.getDirection(); double length, invLength, prod; - double R[3][3], A[3][3]; - double e0, e1, C, c0, c1, c2, discr, invC2, root0, root1, root; + + ox = axis.getOrigin().x; + oy = axis.getOrigin().y; + oz = axis.getOrigin().z; /* choose U, V so that U,V,F is orthonormal set */ @@ -70,75 +132,6 @@ int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *firs A[2][1] = R[0][2] * R[0][1] + R[1][2] * R[1][1]; A[2][2] = R[0][2] * R[0][2] + R[1][2] * R[1][2]; - /* vector B */ - P = Vector3(0.0, 0.0, 0.0); - B.x = -2.0 * P.x; - B.y = -2.0 * P.y; - B.z = -2.0 * P.z; - /* constant C */ - e0 = -2.0 * (R[0][0] * P.x + R[0][1] * P.y + R[0][2] * P.z); - e1 = -2.0 * (R[1][0] * P.x + R[1][1] * P.y + R[1][2] * P.z); - C = 0.25 * (e0 * e0 + e1 * e1) - radius * radius; - - /* line */ - Q = ray.getOrigin().sub(axis.getOrigin()); - G = ray.getDirection(); - - /* compute A*G */ - AG.x = A[0][0] * G.x + A[0][1] * G.y + A[0][2] * G.z; - AG.y = A[1][0] * G.x + A[1][1] * G.y + A[1][2] * G.z; - AG.z = A[2][0] * G.x + A[2][1] * G.y + A[2][2] * G.z; - - /* compute A*Q */ - AQ.x = A[0][0] * Q.x + A[0][1] * Q.y + A[0][2] * Q.z; - AQ.y = A[1][0] * Q.x + A[1][1] * Q.y + A[1][2] * Q.z; - AQ.z = A[2][0] * Q.x + A[2][1] * Q.y + A[2][2] * Q.z; - - /* compute quadratic equation c0+c1*t+c2*t^2 = 0 */ - c2 = G.x * AG.x + G.y * AG.y + G.z * AG.z; - c1 = B.x * G.x + B.y * G.y + B.z * G.z + 2.0f * (Q.x * AG.x + Q.y * AG.y + Q.z * AG.z); - c0 = Q.x * AQ.x + Q.y * AQ.y + Q.z * AQ.z + B.x * Q.x + B.y * Q.y + B.z * Q.z + C; - - /* solve for intersections */ - int numIntersections; - discr = c1 * c1 - 4.0 * c0 * c2; - if (discr > EPS) { - numIntersections = 2; - discr = sqrt(discr); - invC2 = 1.0 / c2; - root0 = -0.5 * (c1 + discr) * invC2; - root1 = -0.5 * (c1 - discr) * invC2; - first_intersection->x = Q.x + root0 * G.x; - first_intersection->y = Q.y + root0 * G.y; - first_intersection->z = Q.z + root0 * G.z; - second_intersection->x = Q.x + root1 * G.x; - second_intersection->y = Q.y + root1 * G.y; - second_intersection->z = Q.z + root1 * G.z; - - *first_intersection = first_intersection->add(axis.getOrigin()); - *second_intersection = second_intersection->add(axis.getOrigin()); - } else if (discr < -EPS) { - numIntersections = 0; - } else { - numIntersections = 1; - root = -0.5 * c1 / c2; - first_intersection->x = Q.x + root * G.x; - first_intersection->y = Q.y + root * G.y; - first_intersection->z = Q.z + root * G.z; - - *first_intersection = first_intersection->add(axis.getOrigin()); - } - - return numIntersections; -} - -void InfiniteCylinder::save(PackStream *stream) const { - axis.save(stream); - stream->write(&radius); -} - -void InfiniteCylinder::load(PackStream *stream) { - axis.load(stream); - stream->read(&radius); + C = -radius * radius; } diff --git a/src/basics/InfiniteCylinder.h b/src/basics/InfiniteCylinder.h index dcb053a..affbeca 100644 --- a/src/basics/InfiniteCylinder.h +++ b/src/basics/InfiniteCylinder.h @@ -13,7 +13,7 @@ namespace basics { */ class BASICSSHARED_EXPORT InfiniteCylinder { public: - InfiniteCylinder(); + InfiniteCylinder() = default; InfiniteCylinder(const InfiniteRay &axis, double radius); inline const InfiniteRay &getAxis() const { @@ -33,9 +33,21 @@ class BASICSSHARED_EXPORT InfiniteCylinder { virtual void save(PackStream *stream) const; virtual void load(PackStream *stream); + private: + void validate(); + protected: InfiniteRay axis; double radius; + + private: + // Stored equation factors, to speed up ray intersection + double R[3][3]; + double A[3][3]; + double C; + double ox; + double oy; + double oz; }; } } diff --git a/src/basics/InfiniteRay.cpp b/src/basics/InfiniteRay.cpp index 8bf68b8..0797076 100644 --- a/src/basics/InfiniteRay.cpp +++ b/src/basics/InfiniteRay.cpp @@ -1,8 +1,5 @@ #include "InfiniteRay.h" -InfiniteRay::InfiniteRay() { -} - InfiniteRay::InfiniteRay(const Vector3 &origin, const Vector3 &direction) : origin(origin), direction(direction.normalize()) { } diff --git a/src/basics/InfiniteRay.h b/src/basics/InfiniteRay.h index cbae484..dce5bed 100644 --- a/src/basics/InfiniteRay.h +++ b/src/basics/InfiniteRay.h @@ -13,7 +13,7 @@ namespace basics { */ class BASICSSHARED_EXPORT InfiniteRay { public: - InfiniteRay(); + InfiniteRay() = default; InfiniteRay(const Vector3 &origin, const Vector3 &direction); static InfiniteRay fromPoints(const Vector3 &point1, const Vector3 &point2); diff --git a/src/basics/SpaceSegment.cpp b/src/basics/SpaceSegment.cpp index ea1fd0b..aeebfb6 100644 --- a/src/basics/SpaceSegment.cpp +++ b/src/basics/SpaceSegment.cpp @@ -60,19 +60,19 @@ bool SpaceSegment::intersectBoundingBox(const SpaceSegment &bbox) const { double tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us - double t; + //double t; if (tmax < 0.0) { - t = tmax; + //t = tmax; return false; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { - t = tmax; + //t = tmax; return false; } - t = tmin; + //t = tmin; return true; } diff --git a/src/basics/Sphere.cpp b/src/basics/Sphere.cpp index b1cda67..5a1a035 100644 --- a/src/basics/Sphere.cpp +++ b/src/basics/Sphere.cpp @@ -3,9 +3,6 @@ #include "PackStream.h" #include "InfiniteRay.h" -Sphere::Sphere() { -} - Sphere::Sphere(const Vector3 ¢er, double radius) : center(center), radius(radius) { radius2 = radius * radius; } diff --git a/src/basics/Sphere.h b/src/basics/Sphere.h index c1d0954..a9961ca 100644 --- a/src/basics/Sphere.h +++ b/src/basics/Sphere.h @@ -13,7 +13,7 @@ namespace basics { */ class BASICSSHARED_EXPORT Sphere { public: - Sphere(); + Sphere() = default; Sphere(const Vector3 ¢er, double radius); inline const Vector3 &getCenter() const { diff --git a/src/definition/VegetationInstance.cpp b/src/definition/VegetationInstance.cpp index ef68454..6e90ac7 100644 --- a/src/definition/VegetationInstance.cpp +++ b/src/definition/VegetationInstance.cpp @@ -5,6 +5,6 @@ VegetationInstance::VegetationInstance(const VegetationModelDefinition &model, c : model(model), base(base), size(size), angle(angle) { } -VegetationInstance VegetationInstance::displace(const Vector3 &location, const Vector3 &normal) const { +VegetationInstance VegetationInstance::displace(const Vector3 &location, const Vector3 &) const { return VegetationInstance(model, location, size, angle); } diff --git a/src/definition/VegetationLayerDefinition.cpp b/src/definition/VegetationLayerDefinition.cpp index bd932ec..ba60f46 100644 --- a/src/definition/VegetationLayerDefinition.cpp +++ b/src/definition/VegetationLayerDefinition.cpp @@ -12,6 +12,6 @@ double VegetationLayerDefinition::getMaxHeight() const { return presence->getMaxHeight(); } -void VegetationLayerDefinition::applyPreset(VegetationLayerPreset preset) { +void VegetationLayerDefinition::applyPreset(VegetationLayerPreset) { model->randomize(); } From f990ec403282d842608edc3f5f8ef5dffca50867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 10 Nov 2015 00:34:19 +0100 Subject: [PATCH 11/17] Added control of perspective correction This allows for some optimization in render tests and improves low-quality render times --- src/render/software/OverlayRasterizer.cpp | 1 + src/render/software/Rasterizer.cpp | 43 ++++++++++++++++------- src/render/software/Rasterizer.h | 2 ++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/render/software/OverlayRasterizer.cpp b/src/render/software/OverlayRasterizer.cpp index 5b65804..cb27093 100644 --- a/src/render/software/OverlayRasterizer.cpp +++ b/src/render/software/OverlayRasterizer.cpp @@ -10,6 +10,7 @@ OverlayRasterizer::OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress } int OverlayRasterizer::prepareRasterization() { + setPerspectiveCorrection(false); return 1; } diff --git a/src/render/software/Rasterizer.cpp b/src/render/software/Rasterizer.cpp index 0c5dbeb..df233ef 100644 --- a/src/render/software/Rasterizer.cpp +++ b/src/render/software/Rasterizer.cpp @@ -37,6 +37,7 @@ Rasterizer::Rasterizer(SoftwareRenderer *renderer, RenderProgress *progress, int interrupted = false; backface_culling = false; + perspective_correction = true; triangle_count = 0; auto_cut_limit = 0.01; @@ -51,7 +52,8 @@ void Rasterizer::interrupt() { interrupted = true; } -void Rasterizer::setQuality(double) { +void Rasterizer::setQuality(double quality) { + this->perspective_correction = (quality > 0.4); } void Rasterizer::setColor(const Color &color) { @@ -62,6 +64,10 @@ void Rasterizer::setBackFaceCulling(bool cull) { this->backface_culling = cull; } +void Rasterizer::setPerspectiveCorrection(bool active) { + this->perspective_correction = active; +} + void Rasterizer::setAutoCutLimit(double limit) { this->auto_cut_limit = limit; } @@ -226,21 +232,32 @@ void Rasterizer::scanGetDiff(ScanPoint *v1, ScanPoint *v2, ScanPoint *result) { void Rasterizer::scanInterpolate(CameraDefinition *camera, ScanPoint *v1, ScanPoint *diff, double value, ScanPoint *result) { - Vector3 vec1(v1->pixel.x, v1->pixel.y, v1->pixel.z); - Vector3 vecdiff(diff->pixel.x, diff->pixel.y, diff->pixel.z); - double v1depth = 1.0 / camera->getRealDepth(vec1); - double v2depth = 1.0 / camera->getRealDepth(vec1.add(vecdiff)); - double factor = 1.0 / ((1.0 - value) * v1depth + value * v2depth); - result->pixel.x = v1->pixel.x + diff->pixel.x * value; result->pixel.y = v1->pixel.y + diff->pixel.y * value; result->pixel.z = v1->pixel.z + diff->pixel.z * value; - result->location.x = - ((1.0 - value) * (v1->location.x * v1depth) + value * (v1->location.x + diff->location.x) * v2depth) * factor; - result->location.y = - ((1.0 - value) * (v1->location.y * v1depth) + value * (v1->location.y + diff->location.y) * v2depth) * factor; - result->location.z = - ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * factor; + + if (perspective_correction) { + Vector3 vec1(v1->pixel.x, v1->pixel.y, v1->pixel.z); + Vector3 vecdiff(diff->pixel.x, diff->pixel.y, diff->pixel.z); + double v1depth = 1.0 / camera->getRealDepth(vec1); + double v2depth = 1.0 / camera->getRealDepth(vec1.add(vecdiff)); + double factor = 1.0 / ((1.0 - value) * v1depth + value * v2depth); + + result->location.x = + ((1.0 - value) * (v1->location.x * v1depth) + value * (v1->location.x + diff->location.x) * v2depth) * + factor; + result->location.y = + ((1.0 - value) * (v1->location.y * v1depth) + value * (v1->location.y + diff->location.y) * v2depth) * + factor; + result->location.z = + ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * + factor; + } else { + result->location.x = v1->location.x + diff->location.x * value; + result->location.y = v1->location.y + diff->location.y * value; + result->location.z = v1->location.z + diff->location.z * value; + } + result->client = v1->client; result->front_facing = v1->front_facing; } diff --git a/src/render/software/Rasterizer.h b/src/render/software/Rasterizer.h index c229d4b..f899388 100644 --- a/src/render/software/Rasterizer.h +++ b/src/render/software/Rasterizer.h @@ -61,6 +61,7 @@ class SOFTWARESHARED_EXPORT Rasterizer { void setColor(const Color &color); void setBackFaceCulling(bool cull); + void setPerspectiveCorrection(bool active); /** * Reset the internal triangle counter to 0. @@ -97,6 +98,7 @@ class SOFTWARESHARED_EXPORT Rasterizer { int triangle_count; double auto_cut_limit; bool backface_culling; + bool perspective_correction; }; } } From e2d03642f43f453523b3c08060985c42652c8496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 10 Nov 2015 01:12:14 +0100 Subject: [PATCH 12/17] Added some vegetation rendering optimizations --- src/basics/CappedCylinder.cpp | 48 +++++++++++-------- src/basics/CappedCylinder.h | 6 ++- src/basics/InfiniteCylinder.cpp | 2 +- src/basics/InfiniteCylinder.h | 2 +- src/basics/Sphere.cpp | 19 +++++++- src/basics/Sphere.h | 11 ++++- .../software/VegetationModelRenderer.cpp | 23 +++++---- src/tests/CappedCylinder_Test.cpp | 16 +++---- src/tests/InfiniteCylinder_Test.cpp | 12 ++--- src/tests/Sphere_Test.cpp | 16 +++++-- 10 files changed, 96 insertions(+), 59 deletions(-) diff --git a/src/basics/CappedCylinder.cpp b/src/basics/CappedCylinder.cpp index d508284..9cd731e 100644 --- a/src/basics/CappedCylinder.cpp +++ b/src/basics/CappedCylinder.cpp @@ -4,38 +4,44 @@ #include "PackStream.h" CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length) - : InfiniteCylinder(InfiniteRay(base, direction), radius), length(length) { + : InfiniteCylinder(InfiniteRay(base, direction), radius), length(length), + container(base.add(direction.scale(length * 0.5)), length * 0.5) { } -int CappedCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, +int CappedCylinder::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const { - // TODO Apply the caps - int count = InfiniteCylinder::checkRayIntersection(ray, first_intersection, second_intersection); - - if (count == 0) { + if (not container.checkRayIntersection(ray)) { + // We don't hit the containing sphere at all return 0; - } else if (count == 2) { - if (checkPointProjection(first_intersection)) { - if (checkPointProjection(second_intersection)) { - return 2; + } else { + // TODO Apply the caps + int count = InfiniteCylinder::findRayIntersection(ray, first_intersection, second_intersection); + + if (count == 0) { + return 0; + } else if (count == 2) { + if (checkPointProjection(first_intersection)) { + if (checkPointProjection(second_intersection)) { + return 2; + } else { + return 1; + } } else { - return 1; + if (checkPointProjection(second_intersection)) { + *first_intersection = *second_intersection; + return 1; + } else { + return 0; + } } - } else { - if (checkPointProjection(second_intersection)) { - *first_intersection = *second_intersection; + } else // count == 1 + { + if (checkPointProjection(first_intersection)) { return 1; } else { return 0; } } - } else // count == 1 - { - if (checkPointProjection(first_intersection)) { - return 1; - } else { - return 0; - } } } diff --git a/src/basics/CappedCylinder.h b/src/basics/CappedCylinder.h index dddf1b5..185379c 100644 --- a/src/basics/CappedCylinder.h +++ b/src/basics/CappedCylinder.h @@ -4,6 +4,7 @@ #include "basics_global.h" #include "InfiniteCylinder.h" +#include "Sphere.h" namespace paysages { namespace basics { @@ -22,8 +23,10 @@ class BASICSSHARED_EXPORT CappedCylinder : public InfiniteCylinder { /** * Check the intersection between the cylinder and an infinite ray. + * + * Returns the number of intersections (0, 1 or 2) and fill the intersection points. */ - int checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + int findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; /** * Check if a point projects in the length of the finite cylinder. @@ -35,6 +38,7 @@ class BASICSSHARED_EXPORT CappedCylinder : public InfiniteCylinder { private: double length; + Sphere container; }; } } diff --git a/src/basics/InfiniteCylinder.cpp b/src/basics/InfiniteCylinder.cpp index d13e230..b5c2bc9 100644 --- a/src/basics/InfiniteCylinder.cpp +++ b/src/basics/InfiniteCylinder.cpp @@ -8,7 +8,7 @@ InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius) : axi validate(); } -int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, +int InfiniteCylinder::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const { /* * Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin. diff --git a/src/basics/InfiniteCylinder.h b/src/basics/InfiniteCylinder.h index affbeca..bcdeef7 100644 --- a/src/basics/InfiniteCylinder.h +++ b/src/basics/InfiniteCylinder.h @@ -28,7 +28,7 @@ class BASICSSHARED_EXPORT InfiniteCylinder { * * Returns the number of intersections (0, 1 or 2) and fill the intersection points. */ - int checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + int findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; virtual void save(PackStream *stream) const; virtual void load(PackStream *stream); diff --git a/src/basics/Sphere.cpp b/src/basics/Sphere.cpp index 5a1a035..2ee9196 100644 --- a/src/basics/Sphere.cpp +++ b/src/basics/Sphere.cpp @@ -7,8 +7,23 @@ Sphere::Sphere(const Vector3 ¢er, double radius) : center(center), radius(ra radius2 = radius * radius; } -int Sphere::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, - Vector3 *second_intersection) const { +int Sphere::checkRayIntersection(const InfiniteRay &ray) const { + Vector3 L = ray.getOrigin().sub(center); + double b = 2.0 * ray.getDirection().dotProduct(L); + double c = L.dotProduct(L) - radius2; + + double discr = b * b - 4.0 * c; + if (discr < 0) { + return 0; + } else if (discr == 0) { + return 1; + } else { + return 2; + } +} + +int Sphere::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, + Vector3 *second_intersection) const { Vector3 L = ray.getOrigin().sub(center); double b = 2.0 * ray.getDirection().dotProduct(L); double c = L.dotProduct(L) - radius2; diff --git a/src/basics/Sphere.h b/src/basics/Sphere.h index a9961ca..6067f35 100644 --- a/src/basics/Sphere.h +++ b/src/basics/Sphere.h @@ -24,11 +24,18 @@ class BASICSSHARED_EXPORT Sphere { } /** - * Check the intersection between the sphere and an infinite ray. + * Check for intersections between the sphere and an infinite ray. + * + * Returns the number of intersections (0, 1 or 2). + */ + int checkRayIntersection(const InfiniteRay &ray) const; + + /** + * Get the intersections between the sphere and an infinite ray. * * Returns the number of intersections (0, 1 or 2) and fill the intersection points. */ - int checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + int findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; void save(PackStream *stream) const; void load(PackStream *stream); diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp index 7a1b7f1..987a8e1 100644 --- a/src/render/software/VegetationModelRenderer.cpp +++ b/src/render/software/VegetationModelRenderer.cpp @@ -30,7 +30,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, for (const auto &branch : model->getSolidVolumes()) { Vector3 near, far; - if (branch.checkRayIntersection(ray, &near, &far)) { + if (branch.findRayIntersection(ray, &near, &far)) { distance = ray.getCursor(near); if (distance >= 0.0 and distance <= maximal) { // Got a branch hit @@ -54,8 +54,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, } for (const auto &foliage : model->getFoliageGroups()) { - Vector3 near, far; - intersections = foliage.checkRayIntersection(ray, &near, &far); + intersections = foliage.checkRayIntersection(ray); if (intersections == 2) { InfiniteRay subray(ray.getOrigin().sub(foliage.getCenter()).scale(1.0 / foliage.getRadius()), ray.getDirection()); @@ -63,19 +62,19 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, for (const auto &leaf : model->getFoliageItems()) { Sphere leafcap(leaf.getPoint(), leaf.getRadius() * leaf.getRadius() / foliage.getRadius()); // TODO Add cap intersection to Sphere class - Vector3 capnear, capfar; - if (leafcap.checkRayIntersection(subray, &capnear, &capfar) == 2) { - if (capnear.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) { - if (capfar.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) { + Vector3 near, far; + if (leafcap.findRayIntersection(subray, &near, &far) == 2) { + if (near.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) { + if (far.sub(leaf.getPoint()).normalize().dotProduct(leaf.getNormal()) < 0.5) { continue; } else { - capnear = capfar; + near = far; } } - Vector3 capnormal = capnear.sub(leaf.getPoint()).normalize(); - capnear = capnear.scale(foliage.getRadius()).add(foliage.getCenter()); - distance = ray.getCursor(capnear); + Vector3 capnormal = near.sub(leaf.getPoint()).normalize(); + near = near.scale(foliage.getRadius()).add(foliage.getCenter()); + distance = ray.getCursor(near); if (distance >= 0.0 and distance <= maximal) { // Got a foliage hit @@ -88,7 +87,7 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, nearest = distance; hit = true; - location = capnear; + location = near; normal = capnormal; if (normal.dotProduct(location.sub(segment.getStart())) > 0.0) { diff --git a/src/tests/CappedCylinder_Test.cpp b/src/tests/CappedCylinder_Test.cpp index 1a04463..a9aa57c 100644 --- a/src/tests/CappedCylinder_Test.cpp +++ b/src/tests/CappedCylinder_Test.cpp @@ -2,43 +2,43 @@ #include "CappedCylinder.h" -TEST(CappedCylinder, checkRayIntersection) { +TEST(CappedCylinder, findRayIntersection) { CappedCylinder cylinder(VECTOR_DOWN, VECTOR_UP, 1.0, 2.0); int intersect_count; Vector3 p1, p2; intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(0, intersect_count); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(1, intersect_count); EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(2, intersect_count); EXPECT_VECTOR3_COORDS(p1, 0.5, 0.0, -cos(asin(0.5))); EXPECT_VECTOR3_COORDS(p2, 0.5, 0.0, cos(asin(0.5))); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, -2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(0.5, -2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(0, intersect_count); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(0.5, 2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(0, intersect_count); // diagonal cases (through a cap) intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(-2.0, -1.0, 0.0), Vector3(1.0, 1.0, 0.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(-2.0, -1.0, 0.0), Vector3(1.0, 1.0, 0.0)), &p1, &p2); EXPECT_EQ(1, intersect_count); EXPECT_VECTOR3_COORDS(p1, -1.0, 0.0, 0.0); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(-2.0, 3.0, 0.0), Vector3(1.0, -1.0, 0.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(-2.0, 3.0, 0.0), Vector3(1.0, -1.0, 0.0)), &p1, &p2); EXPECT_EQ(1, intersect_count); EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); } diff --git a/src/tests/InfiniteCylinder_Test.cpp b/src/tests/InfiniteCylinder_Test.cpp index 1d836d9..dc3516e 100644 --- a/src/tests/InfiniteCylinder_Test.cpp +++ b/src/tests/InfiniteCylinder_Test.cpp @@ -4,7 +4,7 @@ #include "InfiniteCylinder.h" #include -TEST(InfiniteCylinder, checkRayIntersection) { +TEST(InfiniteCylinder, findRayIntersection) { InfiniteRay ray(VECTOR_ZERO, VECTOR_UP); InfiniteCylinder cylinder(ray, 1.0); @@ -12,29 +12,29 @@ TEST(InfiniteCylinder, checkRayIntersection) { Vector3 p1, p2; intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(0, intersect_count); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(1, intersect_count); EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); intersect_count = - cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + cylinder.findRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); EXPECT_EQ(2, intersect_count); EXPECT_VECTOR3_COORDS(p1, 0.5, 0.0, -cos(asin(0.5))); EXPECT_VECTOR3_COORDS(p2, 0.5, 0.0, cos(asin(0.5))); } -TEST(InfiniteCylinder, checkRayIntersection2) { +TEST(InfiniteCylinder, getRayIntersection2) { InfiniteRay ray(Vector3(-1.4, 1.5, 1.0), Vector3(1.0, 0.0, 0.0)); InfiniteCylinder cylinder(ray, 0.5); int intersect_count; Vector3 p1, p2; - intersect_count = cylinder.checkRayIntersection( + intersect_count = cylinder.findRayIntersection( InfiniteRay::fromPoints(Vector3(0.0, 1.5, 0.0), Vector3(0.0, 1.5, 2.0)), &p1, &p2); EXPECT_EQ(2, intersect_count); EXPECT_VECTOR3_COORDS(p1, 0.0, 1.5, 0.5); diff --git a/src/tests/Sphere_Test.cpp b/src/tests/Sphere_Test.cpp index 47a15ea..dd73058 100644 --- a/src/tests/Sphere_Test.cpp +++ b/src/tests/Sphere_Test.cpp @@ -3,20 +3,26 @@ #include "InfiniteRay.h" #include "Sphere.h" -TEST(Sphere, checkRayIntersection) { +TEST(Sphere, findRayIntersection) { Sphere sphere(Vector3(2.0, 1.0, 1.0), 0.5); int intersect_count; Vector3 p1, p2; - intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(0.0, 0.0, 0.0), VECTOR_SOUTH), &p1, &p2); - ASSERT_EQ(0, intersect_count); + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(0.0, 0.0, 0.0), VECTOR_SOUTH)); + EXPECT_EQ(0, intersect_count); + intersect_count = sphere.findRayIntersection(InfiniteRay(Vector3(0.0, 0.0, 0.0), VECTOR_SOUTH), &p1, &p2); + EXPECT_EQ(0, intersect_count); - intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(1.5, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(1.5, 1.0, 0.0), VECTOR_SOUTH)); + EXPECT_EQ(1, intersect_count); + intersect_count = sphere.findRayIntersection(InfiniteRay(Vector3(1.5, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); ASSERT_EQ(1, intersect_count); EXPECT_VECTOR3_COORDS(p1, 1.5, 1.0, 1.0); - intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(2.0, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(2.0, 1.0, 0.0), VECTOR_SOUTH)); + EXPECT_EQ(2, intersect_count); + intersect_count = sphere.findRayIntersection(InfiniteRay(Vector3(2.0, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); ASSERT_EQ(2, intersect_count); EXPECT_VECTOR3_COORDS(p1, 2.0, 1.0, 0.5); EXPECT_VECTOR3_COORDS(p2, 2.0, 1.0, 1.5); From 394ff3b0aee645b13c7186425981c548b165a708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 18 Nov 2015 17:22:33 +0100 Subject: [PATCH 13/17] Format --- src/basics/CappedCylinder.cpp | 6 +++--- src/basics/InfiniteCylinder.cpp | 2 +- src/basics/SpaceSegment.cpp | 8 ++++---- src/basics/Sphere.cpp | 2 +- src/definition/VegetationModelDefinition.cpp | 4 ++-- src/experiments/bruneton/Main.cpp | 14 +++++++------- src/tests/InfiniteCylinder_Test.cpp | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/basics/CappedCylinder.cpp b/src/basics/CappedCylinder.cpp index 9cd731e..8c24c39 100644 --- a/src/basics/CappedCylinder.cpp +++ b/src/basics/CappedCylinder.cpp @@ -9,7 +9,7 @@ CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, do } int CappedCylinder::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, - Vector3 *second_intersection) const { + Vector3 *second_intersection) const { if (not container.checkRayIntersection(ray)) { // We don't hit the containing sphere at all return 0; @@ -34,8 +34,8 @@ int CappedCylinder::findRayIntersection(const InfiniteRay &ray, Vector3 *first_i return 0; } } - } else // count == 1 - { + } else { + // count == 1 if (checkPointProjection(first_intersection)) { return 1; } else { diff --git a/src/basics/InfiniteCylinder.cpp b/src/basics/InfiniteCylinder.cpp index b5c2bc9..ff508a7 100644 --- a/src/basics/InfiniteCylinder.cpp +++ b/src/basics/InfiniteCylinder.cpp @@ -9,7 +9,7 @@ InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius) : axi } int InfiniteCylinder::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, - Vector3 *second_intersection) const { + Vector3 *second_intersection) const { /* * Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin. * Maybe some optimizations could be made from this. diff --git a/src/basics/SpaceSegment.cpp b/src/basics/SpaceSegment.cpp index aeebfb6..30fa145 100644 --- a/src/basics/SpaceSegment.cpp +++ b/src/basics/SpaceSegment.cpp @@ -60,19 +60,19 @@ bool SpaceSegment::intersectBoundingBox(const SpaceSegment &bbox) const { double tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us - //double t; + // double t; if (tmax < 0.0) { - //t = tmax; + // t = tmax; return false; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { - //t = tmax; + // t = tmax; return false; } - //t = tmin; + // t = tmin; return true; } diff --git a/src/basics/Sphere.cpp b/src/basics/Sphere.cpp index 2ee9196..8395dfc 100644 --- a/src/basics/Sphere.cpp +++ b/src/basics/Sphere.cpp @@ -23,7 +23,7 @@ int Sphere::checkRayIntersection(const InfiniteRay &ray) const { } int Sphere::findRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, - Vector3 *second_intersection) const { + Vector3 *second_intersection) const { Vector3 L = ray.getOrigin().sub(center); double b = 2.0 * ray.getDirection().dotProduct(L); double c = L.dotProduct(L) - radius2; diff --git a/src/definition/VegetationModelDefinition.cpp b/src/definition/VegetationModelDefinition.cpp index ad11f8d..11dfba3 100644 --- a/src/definition/VegetationModelDefinition.cpp +++ b/src/definition/VegetationModelDefinition.cpp @@ -10,13 +10,13 @@ VegetationModelDefinition::VegetationModelDefinition(DefinitionNode *parent) : DefinitionNode(parent, "model") { solid_material = new SurfaceMaterial(Color(0.2, 0.15, 0.15)); solid_material->reflection = 0.002; - solid_material->shininess = 2.0; + solid_material->shininess = 1.0; solid_material->hardness = 0.3; solid_material->validate(); foliage_material = new SurfaceMaterial(Color(0.4, 0.8, 0.45)); foliage_material->reflection = 0.007; - foliage_material->shininess = 12.0; + foliage_material->shininess = 2.0; foliage_material->hardness = 0.2; foliage_material->validate(); diff --git a/src/experiments/bruneton/Main.cpp b/src/experiments/bruneton/Main.cpp index 8273ce1..b11a229 100644 --- a/src/experiments/bruneton/Main.cpp +++ b/src/experiments/bruneton/Main.cpp @@ -169,14 +169,14 @@ const int deltaSRUnit = 5; const int deltaSMUnit = 6; const int deltaJUnit = 7; -unsigned int reflectanceTexture; // unit 0, ground reflectance texture +unsigned int reflectanceTexture; // unit 0, ground reflectance texture unsigned int transmittanceTexture; // unit 1, T table -unsigned int irradianceTexture; // unit 2, E table -unsigned int inscatterTexture; // unit 3, S table -unsigned int deltaETexture; // unit 4, deltaE table -unsigned int deltaSRTexture; // unit 5, deltaS table (Rayleigh part) -unsigned int deltaSMTexture; // unit 6, deltaS table (Mie part) -unsigned int deltaJTexture; // unit 7, deltaJ table +unsigned int irradianceTexture; // unit 2, E table +unsigned int inscatterTexture; // unit 3, S table +unsigned int deltaETexture; // unit 4, deltaE table +unsigned int deltaSRTexture; // unit 5, deltaS table (Rayleigh part) +unsigned int deltaSMTexture; // unit 6, deltaS table (Mie part) +unsigned int deltaJTexture; // unit 7, deltaJ table unsigned int transmittanceProg; unsigned int irradiance1Prog; diff --git a/src/tests/InfiniteCylinder_Test.cpp b/src/tests/InfiniteCylinder_Test.cpp index dc3516e..cb24069 100644 --- a/src/tests/InfiniteCylinder_Test.cpp +++ b/src/tests/InfiniteCylinder_Test.cpp @@ -34,8 +34,8 @@ TEST(InfiniteCylinder, getRayIntersection2) { int intersect_count; Vector3 p1, p2; - intersect_count = cylinder.findRayIntersection( - InfiniteRay::fromPoints(Vector3(0.0, 1.5, 0.0), Vector3(0.0, 1.5, 2.0)), &p1, &p2); + intersect_count = + cylinder.findRayIntersection(InfiniteRay::fromPoints(Vector3(0.0, 1.5, 0.0), Vector3(0.0, 1.5, 2.0)), &p1, &p2); EXPECT_EQ(2, intersect_count); EXPECT_VECTOR3_COORDS(p1, 0.0, 1.5, 0.5); EXPECT_VECTOR3_COORDS(p2, 0.0, 1.5, 1.5); From 01992a0019c9751fea2b54c282aec2bb6f6f79dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Fri, 20 Nov 2015 01:16:48 +0100 Subject: [PATCH 14/17] Fixed vegetation definition, due to layers refactoring in master --- src/definition/Layers.cpp | 2 +- src/definition/VegetationDefinition.cpp | 14 +++++++------- src/definition/VegetationLayerDefinition.cpp | 3 ++- src/definition/VegetationLayerDefinition.h | 2 +- src/render/software/VegetationRenderer.cpp | 2 +- src/tests/Layers_Test.cpp | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/definition/Layers.cpp b/src/definition/Layers.cpp index 3ace25a..4c31883 100644 --- a/src/definition/Layers.cpp +++ b/src/definition/Layers.cpp @@ -119,7 +119,7 @@ bool Layers::applyDiff(const DefinitionDiff *diff, bool backward) { void Layers::generateInitDiffs(std::vector *diffs) const { int i = 0; - for (auto layer: layers) { + for (auto layer : layers) { auto diff = new LayersDiff(this, LayersDiff::LAYER_ADDED, i++); diff->saveLayer(*layer); diffs->push_back(diff); diff --git a/src/definition/VegetationDefinition.cpp b/src/definition/VegetationDefinition.cpp index d5683cf..07f86bb 100644 --- a/src/definition/VegetationDefinition.cpp +++ b/src/definition/VegetationDefinition.cpp @@ -3,8 +3,8 @@ #include "VegetationLayerDefinition.h" #include "VegetationModelDefinition.h" -static DefinitionNode *_layer_constructor(Layers *parent) { - return new VegetationLayerDefinition(parent); +static DefinitionNode *_layer_constructor(Layers *parent, const std::string &name) { + return new VegetationLayerDefinition(parent, name); } VegetationDefinition::VegetationDefinition(DefinitionNode *parent) : Layers(parent, "vegetation", _layer_constructor) { @@ -12,7 +12,7 @@ VegetationDefinition::VegetationDefinition(DefinitionNode *parent) : Layers(pare double VegetationDefinition::getMaxHeight() const { double max_height = 0.0; - int n = count(); + int n = getLayerCount(); for (int i = 0; i < n; i++) { double layer_height = getVegetationLayer(i)->getMaxHeight(); @@ -25,13 +25,13 @@ double VegetationDefinition::getMaxHeight() const { } void VegetationDefinition::applyPreset(VegetationPreset preset) { - VegetationLayerDefinition *layer; + VegetationLayerDefinition layer(this, "temp"); clear(); if (preset == VEGETATION_PRESET_TEMPERATE) { - layer = getVegetationLayer(addLayer()); - layer->applyPreset(VegetationLayerDefinition::VEGETATION_BASIC_TREES); - layer->setName("Basic tree"); + layer.applyPreset(VegetationLayerDefinition::VEGETATION_BASIC_TREES); + layer.setName("Basic tree"); + addLayer(layer); } } diff --git a/src/definition/VegetationLayerDefinition.cpp b/src/definition/VegetationLayerDefinition.cpp index ba60f46..661b4fa 100644 --- a/src/definition/VegetationLayerDefinition.cpp +++ b/src/definition/VegetationLayerDefinition.cpp @@ -3,7 +3,8 @@ #include "VegetationModelDefinition.h" #include "VegetationPresenceDefinition.h" -VegetationLayerDefinition::VegetationLayerDefinition(DefinitionNode *parent) : DefinitionNode(parent, "layer") { +VegetationLayerDefinition::VegetationLayerDefinition(DefinitionNode *parent, const std::string &name) + : DefinitionNode(parent, name, "vegetationlayer") { model = new VegetationModelDefinition(this); presence = new VegetationPresenceDefinition(this); } diff --git a/src/definition/VegetationLayerDefinition.h b/src/definition/VegetationLayerDefinition.h index fd0046c..d1e559e 100644 --- a/src/definition/VegetationLayerDefinition.h +++ b/src/definition/VegetationLayerDefinition.h @@ -13,7 +13,7 @@ namespace definition { */ class DEFINITIONSHARED_EXPORT VegetationLayerDefinition : public DefinitionNode { public: - VegetationLayerDefinition(DefinitionNode *parent); + VegetationLayerDefinition(DefinitionNode *parent, const std::string &name); inline const VegetationPresenceDefinition *getPresence() const { return presence; diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index 0bbfc3a..e15d696 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -103,7 +103,7 @@ RayCastingResult VegetationRenderer::getBoundResult(const SpaceSegment &segment, } // Iterate all layers and instances - int n = vegetation->count(); + int n = vegetation->getLayerCount(); for (int i = 0; i < n; i++) { VegetationLayerDefinition *layer = vegetation->getVegetationLayer(i); diff --git a/src/tests/Layers_Test.cpp b/src/tests/Layers_Test.cpp index 25d1419..03752bc 100644 --- a/src/tests/Layers_Test.cpp +++ b/src/tests/Layers_Test.cpp @@ -186,7 +186,7 @@ TEST(Layers, generateInitDiffs) { EXPECT_EQ(3, (int)diffs.size()); Layers layers1(NULL, "layers", _construc1); - for (auto diff: diffs) { + for (auto diff : diffs) { layers1.applyDiff(diff); } ASSERT_EQ(3, layers1.getLayerCount()); From 9d7a7a0ff7289199978d35a98e2880c0ce86965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 25 Nov 2015 23:15:58 +0100 Subject: [PATCH 15/17] Added vegetation impostors to OpenGL renderer This is currently an unoptimized and broken version, to be improved --- src/basics/Color.h | 1 + src/basics/Color.inline.cpp | 6 + .../VegetationPresenceDefinition.cpp | 13 +- src/interface/commandline/commandline.pro | 6 + src/interface/commandline/tests.cpp | 25 ++- src/render/opengl/OpenGLPart.cpp | 10 ++ src/render/opengl/OpenGLPart.h | 10 ++ src/render/opengl/OpenGLRenderer.cpp | 20 ++- src/render/opengl/OpenGLRenderer.h | 4 + src/render/opengl/OpenGLShaderProgram.cpp | 3 + src/render/opengl/OpenGLShaderProgram.h | 5 + src/render/opengl/OpenGLSharedState.cpp | 2 +- src/render/opengl/OpenGLSharedState.h | 9 +- src/render/opengl/OpenGLVariable.cpp | 11 ++ src/render/opengl/OpenGLVariable.h | 3 + src/render/opengl/OpenGLVegetation.cpp | 155 ++++++++++++++++++ src/render/opengl/OpenGLVegetation.h | 89 ++++++++++ .../opengl/OpenGLVegetationImpostor.cpp | 106 ++++++++++++ src/render/opengl/OpenGLVegetationImpostor.h | 43 +++++ .../opengl/OpenGLVegetationInstance.cpp | 11 ++ src/render/opengl/OpenGLVegetationInstance.h | 43 +++++ src/render/opengl/OpenGLVegetationLayer.cpp | 141 ++++++++++++++++ src/render/opengl/OpenGLVegetationLayer.h | 82 +++++++++ src/render/opengl/opengl.pro | 4 + src/render/opengl/opengl_global.h | 4 + src/render/opengl/shaders/resources.qrc | 2 + src/render/opengl/shaders/vegetation.frag | 15 ++ src/render/opengl/shaders/vegetation.vert | 16 ++ src/render/software/SoftwareRenderer.cpp | 1 + .../software/VegetationModelRenderer.cpp | 17 ++ src/render/software/VegetationRenderer.cpp | 3 + src/render/software/VegetationRenderer.h | 1 + src/tests/OpenGLVegetationLayer_Test.cpp | 67 ++++++++ src/tests/OpenGLVegetation_Test.cpp | 19 +++ src/tests/OverlayRasterizer_Test.cpp | 3 +- 35 files changed, 935 insertions(+), 15 deletions(-) create mode 100644 src/render/opengl/OpenGLVegetation.cpp create mode 100644 src/render/opengl/OpenGLVegetation.h create mode 100644 src/render/opengl/OpenGLVegetationImpostor.cpp create mode 100644 src/render/opengl/OpenGLVegetationImpostor.h create mode 100644 src/render/opengl/OpenGLVegetationInstance.cpp create mode 100644 src/render/opengl/OpenGLVegetationInstance.h create mode 100644 src/render/opengl/OpenGLVegetationLayer.cpp create mode 100644 src/render/opengl/OpenGLVegetationLayer.h create mode 100644 src/render/opengl/shaders/vegetation.frag create mode 100644 src/render/opengl/shaders/vegetation.vert create mode 100644 src/tests/OpenGLVegetationLayer_Test.cpp create mode 100644 src/tests/OpenGLVegetation_Test.cpp diff --git a/src/basics/Color.h b/src/basics/Color.h index a305fdb..9af2225 100644 --- a/src/basics/Color.h +++ b/src/basics/Color.h @@ -27,6 +27,7 @@ class BASICSSHARED_EXPORT Color { void mask(const Color &mask); double normalize(); + Color normalized(); double getValue() const; double getPower() const; void limitPower(double max_power); diff --git a/src/basics/Color.inline.cpp b/src/basics/Color.inline.cpp index 5e1cfcd..1a4330e 100644 --- a/src/basics/Color.inline.cpp +++ b/src/basics/Color.inline.cpp @@ -118,6 +118,12 @@ METHSPEC double Color::normalize() { return max;*/ } +METHSPEC Color Color::normalized() { + Color col = *this; + col.normalize(); + return col; +} + METHSPEC double Color::getValue() const { double max; diff --git a/src/definition/VegetationPresenceDefinition.cpp b/src/definition/VegetationPresenceDefinition.cpp index 1dea116..ccb212e 100644 --- a/src/definition/VegetationPresenceDefinition.cpp +++ b/src/definition/VegetationPresenceDefinition.cpp @@ -42,11 +42,14 @@ bool VegetationPresenceDefinition::collectInstances(std::vectorget2DTotal(z * 10.0, x * 10.0)) * (noise_presence * 0.5 + 0.5); double angle = 3.0 * generator->get2DTotal(-x * 20.0, z * 20.0); // TODO balanced distribution - double xoffset = fabs(generator->get2DTotal(x * 12.0, -z * 12.0)); - double zoffset = fabs(generator->get2DTotal(-x * 27.0, -z * 27.0)); - double y = getScenery()->getTerrain()->getInterpolatedHeight(x + xoffset, z + zoffset, true, true); - result->push_back(VegetationInstance(model, Vector3(x + xoffset, y, z + zoffset), size, angle)); - added++; + double xo = x + fabs(generator->get2DTotal(x * 12.0, -z * 12.0)); + double zo = z + fabs(generator->get2DTotal(-x * 27.0, -z * 27.0)); + if (xo >= xmin and xo < xmax and zo >= zmin and zo < zmax) + { + double y = getScenery()->getTerrain()->getInterpolatedHeight(xo, zo, true, true); + result->push_back(VegetationInstance(model, Vector3(xo, y, zo), size, angle)); + added++; + } } } } diff --git a/src/interface/commandline/commandline.pro b/src/interface/commandline/commandline.pro index d34d556..3b8b1f9 100644 --- a/src/interface/commandline/commandline.pro +++ b/src/interface/commandline/commandline.pro @@ -32,3 +32,9 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../render/softwa else:unix: LIBS += -L$$OUT_PWD/../../render/software/ -lpaysages_render_software INCLUDEPATH += $$PWD/../../render/software DEPENDPATH += $$PWD/../../render/software + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../render/opengl/release/ -lpaysages_render_opengl +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../render/opengl/debug/ -lpaysages_render_opengl +else:unix: LIBS += -L$$OUT_PWD/../../render/opengl/ -lpaysages_render_opengl +INCLUDEPATH += $$PWD/../../render/opengl +DEPENDPATH += $$PWD/../../render/opengl diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index 06b671a..9d624f3 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -23,12 +23,15 @@ #include "VegetationInstance.h" #include "VegetationRenderer.h" #include "RayCastingResult.h" +#include "OpenGLVegetationImpostor.h" +#include "Texture2D.h" #include +#include void startRender(SoftwareCanvasRenderer *renderer, const char *outputpath); -static void startTestRender(SoftwareCanvasRenderer *renderer, const std::string &name, int iteration = -1) { +static std::string getFileName(const std::string &name, int iteration = -1) { std::ostringstream stream; stream << "pic_test_" << name; @@ -40,7 +43,11 @@ static void startTestRender(SoftwareCanvasRenderer *renderer, const std::string } stream << ".png"; - startRender(renderer, stream.str().data()); + return stream.str(); +} + +static void startTestRender(SoftwareCanvasRenderer *renderer, const std::string &name, int iteration = -1) { + startRender(renderer, getFileName(name, iteration).data()); } static void testGroundShadowQuality() { @@ -288,6 +295,19 @@ static void testVegetationModels() { } } +static void testOpenGLVegetationImpostor() { + std::string filename = getFileName("opengl_vegetation_impostor"); + std::cout << "Rendering " << filename << "..." << std::endl; + + Scenery scenery; + scenery.autoPreset(1); + OpenGLVegetationImpostor impostor(200); + VegetationModelDefinition model(NULL); + bool interrupted = false; + impostor.prepareTexture(model, scenery, &interrupted); + impostor.getTexture()->saveToFile(filename); +} + void runTestSuite() { testGroundShadowQuality(); testRasterizationQuality(); @@ -297,4 +317,5 @@ void runTestSuite() { testCloudsNearGround(); testSunNearHorizon(); testVegetationModels(); + testOpenGLVegetationImpostor(); } diff --git a/src/render/opengl/OpenGLPart.cpp b/src/render/opengl/OpenGLPart.cpp index cfefc62..026ad19 100644 --- a/src/render/opengl/OpenGLPart.cpp +++ b/src/render/opengl/OpenGLPart.cpp @@ -40,3 +40,13 @@ void OpenGLPart::updateScenery(bool onlyCommon) { update(); } } + +Scenery *OpenGLPart::getScenery() const +{ + return renderer->getScenery(); +} + +OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const +{ + return renderer->getOpenGlFunctions(); +} diff --git a/src/render/opengl/OpenGLPart.h b/src/render/opengl/OpenGLPart.h index fedef49..6a97893 100644 --- a/src/render/opengl/OpenGLPart.h +++ b/src/render/opengl/OpenGLPart.h @@ -30,6 +30,16 @@ class OPENGLSHARED_EXPORT OpenGLPart { void updateScenery(bool onlyCommon = false); + /** + * Get access to rendered scenery. + */ + Scenery *getScenery() const; + + /** + * Get access to OpenGL functions. + */ + OpenGLFunctions *getOpenGlFunctions() const; + protected: // Create a shader program OpenGLShaderProgram *createShader(QString name); diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 8ba3b19..085afab 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -6,6 +6,7 @@ #include "OpenGLSkybox.h" #include "OpenGLWater.h" #include "OpenGLTerrain.h" +#include "OpenGLVegetation.h" #include "CloudsRenderer.h" #include "VegetationRenderer.h" #include "Color.h" @@ -40,9 +41,11 @@ OpenGLRenderer::OpenGLRenderer(Scenery *scenery) : SoftwareRenderer(scenery) { skybox = new OpenGLSkybox(this); water = new OpenGLWater(this); terrain = new OpenGLTerrain(this); + vegetation = new OpenGLVegetation(this); } OpenGLRenderer::~OpenGLRenderer() { + vegetation->interrupt(); terrain->interrupt(); water->interrupt(); skybox->interrupt(); @@ -54,6 +57,7 @@ OpenGLRenderer::~OpenGLRenderer() { delete skybox; delete water; delete terrain; + delete vegetation; delete functions; delete shared_state; @@ -69,9 +73,9 @@ void OpenGLRenderer::prepare() { } void OpenGLRenderer::initialize() { - ready = functions->initializeOpenGLFunctions(); + bool init = functions->initializeOpenGLFunctions(); - if (ready) { + if (init) { prepareOpenGLState(); prepare(); @@ -85,7 +89,12 @@ void OpenGLRenderer::initialize() { terrain->initialize(); terrain->updateScenery(); + vegetation->initialize(); + vegetation->updateScenery(); + cameraChangeEvent(render_camera); + + ready = true; } else { Logs::error() << "Failed to initialize OpenGL bindings" << std::endl; } @@ -100,7 +109,7 @@ void OpenGLRenderer::prepareOpenGLState() { functions->glEnable(GL_CULL_FACE); functions->glDepthFunc(GL_LESS); - functions->glDepthMask(1); + functions->glDepthMask(GL_TRUE); functions->glEnable(GL_DEPTH_TEST); functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -145,6 +154,7 @@ void OpenGLRenderer::paint() { skybox->render(); terrain->render(); water->render(); + vegetation->render(); if (mouse_tracking) { updateMouseProjection(); @@ -164,6 +174,7 @@ void OpenGLRenderer::reset() { skybox->updateScenery(); water->updateScenery(); terrain->updateScenery(); + vegetation->updateScenery(); cameraChangeEvent(render_camera); } @@ -210,6 +221,9 @@ void OpenGLRenderer::cameraChangeEvent(CameraDefinition *camera) { // Set in shaders shared_state->set("cameraLocation", location); shared_state->set("viewMatrix", *view_matrix); + + // Broadcast to parts + vegetation->cameraChanged(camera); } double OpenGLRenderer::getPrecision(const Vector3 &) { diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index b8c4074..e367cf1 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -27,6 +27,9 @@ class OPENGLSHARED_EXPORT OpenGLRenderer : public SoftwareRenderer { inline OpenGLTerrain *getTerrain() const { return terrain; } + inline OpenGLVegetation *getVegetation() const { + return vegetation; + } inline bool isDisplayed() const { return displayed; } @@ -106,6 +109,7 @@ class OPENGLSHARED_EXPORT OpenGLRenderer : public SoftwareRenderer { OpenGLSkybox *skybox; OpenGLWater *water; OpenGLTerrain *terrain; + OpenGLVegetation *vegetation; }; } } diff --git a/src/render/opengl/OpenGLShaderProgram.cpp b/src/render/opengl/OpenGLShaderProgram.cpp index 817b97f..8e0387b 100644 --- a/src/render/opengl/OpenGLShaderProgram.cpp +++ b/src/render/opengl/OpenGLShaderProgram.cpp @@ -15,11 +15,13 @@ OpenGLShaderProgram::OpenGLShaderProgram(const std::string &name, OpenGLRenderer : renderer(renderer), name(name) { program = new QOpenGLShaderProgram(); functions = renderer->getOpenGlFunctions(); + state = new OpenGLSharedState(); compiled = false; } OpenGLShaderProgram::~OpenGLShaderProgram() { delete program; + delete state; } void OpenGLShaderProgram::addVertexSource(QString path) { @@ -63,6 +65,7 @@ void OpenGLShaderProgram::bind() { int texture_unit = 0; renderer->getSharedState()->apply(this, texture_unit); + state->apply(this, texture_unit); } void OpenGLShaderProgram::release() { diff --git a/src/render/opengl/OpenGLShaderProgram.h b/src/render/opengl/OpenGLShaderProgram.h index c833e5d..74482fc 100644 --- a/src/render/opengl/OpenGLShaderProgram.h +++ b/src/render/opengl/OpenGLShaderProgram.h @@ -30,6 +30,9 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram { inline OpenGLRenderer *getRenderer() const { return renderer; } + inline OpenGLSharedState *getState() const { + return state; + } protected: friend class OpenGLVariable; @@ -45,6 +48,8 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram { QOpenGLShaderProgram *program; OpenGLFunctions *functions; + OpenGLSharedState *state; + std::string source_vertex; std::string source_fragment; }; diff --git a/src/render/opengl/OpenGLSharedState.cpp b/src/render/opengl/OpenGLSharedState.cpp index f5a9d8c..5af618e 100644 --- a/src/render/opengl/OpenGLSharedState.cpp +++ b/src/render/opengl/OpenGLSharedState.cpp @@ -3,7 +3,7 @@ OpenGLSharedState::OpenGLSharedState() { } -paysages::opengl::OpenGLSharedState::~OpenGLSharedState() +OpenGLSharedState::~OpenGLSharedState() { for (const auto &pair : variables) { delete pair.second; diff --git a/src/render/opengl/OpenGLSharedState.h b/src/render/opengl/OpenGLSharedState.h index 095448c..742ea01 100644 --- a/src/render/opengl/OpenGLSharedState.h +++ b/src/render/opengl/OpenGLSharedState.h @@ -28,6 +28,12 @@ class OPENGLSHARED_EXPORT OpenGLSharedState { OpenGLVariable *get(const std::string &name); // Shortcuts + inline void setInt(const std::string &name, int value) { + get(name)->set(value); + } + inline void set(const std::string &name, float value) { + get(name)->set(value); + } inline void set(const std::string &name, const Texture2D *texture, bool repeat = false, bool color = true) { get(name)->set(texture, repeat, color); } @@ -37,9 +43,6 @@ class OPENGLSHARED_EXPORT OpenGLSharedState { inline void set(const std::string &name, const Texture4D *texture, bool repeat = false, bool color = true) { get(name)->set(texture, repeat, color); } - inline void set(const std::string &name, float value) { - get(name)->set(value); - } inline void set(const std::string &name, const Vector3 &vector) { get(name)->set(vector); } diff --git a/src/render/opengl/OpenGLVariable.cpp b/src/render/opengl/OpenGLVariable.cpp index e1df6d6..1176cca 100644 --- a/src/render/opengl/OpenGLVariable.cpp +++ b/src/render/opengl/OpenGLVariable.cpp @@ -28,6 +28,9 @@ void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) { } switch (type) { + case TYPE_INTEGER: + pr->setUniformValue(name.c_str(), value_int); + break; case TYPE_FLOAT: pr->setUniformValue(name.c_str(), value_float); break; @@ -88,6 +91,14 @@ void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) { texture_color = color; } +void OpenGLVariable::set(int value) +{ + assert(type == TYPE_NONE or type == TYPE_INTEGER); + + type = TYPE_INTEGER; + value_int = value; +} + void OpenGLVariable::set(float value) { assert(type == TYPE_NONE or type == TYPE_FLOAT); diff --git a/src/render/opengl/OpenGLVariable.h b/src/render/opengl/OpenGLVariable.h index 5da2869..c805b38 100644 --- a/src/render/opengl/OpenGLVariable.h +++ b/src/render/opengl/OpenGLVariable.h @@ -20,6 +20,7 @@ class OpenGLVariable { TYPE_TEXTURE_2D, TYPE_TEXTURE_3D, TYPE_TEXTURE_4D, + TYPE_INTEGER, TYPE_FLOAT, TYPE_VECTOR3, TYPE_MATRIX4, @@ -34,6 +35,7 @@ class OpenGLVariable { void set(const Texture2D *texture, bool repeat = false, bool color = true); void set(const Texture3D *texture, bool repeat = false, bool color = true); void set(const Texture4D *texture, bool repeat = false, bool color = true); + void set(int value); void set(float value); void set(const Vector3 &vector); void set(const QVector3D &vector); @@ -48,6 +50,7 @@ class OpenGLVariable { std::string name; OpenGLVariableType type; + int value_int; float value_float; QColor value_color; QVector3D value_vector3; diff --git a/src/render/opengl/OpenGLVegetation.cpp b/src/render/opengl/OpenGLVegetation.cpp new file mode 100644 index 0000000..c64ce14 --- /dev/null +++ b/src/render/opengl/OpenGLVegetation.cpp @@ -0,0 +1,155 @@ +#include "OpenGLVegetation.h" + +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "OpenGLVegetationLayer.h" +#include "Thread.h" +#include "Mutex.h" +#include "Scenery.h" +#include "VegetationDefinition.h" +#include "VegetationLayerDefinition.h" + +class paysages::opengl::VegetationUpdater : public Thread { + public: + VegetationUpdater(OpenGLVegetation *vegetation) : vegetation(vegetation) { + interrupted = false; + } + + void interrupt(bool wait = true) { + interrupted = true; + if (wait) { + join(); + } + } + + protected: + virtual void run() override { + while (not interrupted) { + std::vector layers; + vegetation->acquireLayers(layers); + for (auto layer: layers) { + layer->threadedUpdate(); + } + vegetation->releaseLayers(layers); + timeSleepMs(100); + } + } + + private: + bool interrupted; + OpenGLVegetation *vegetation; +}; + +OpenGLVegetation::OpenGLVegetation(OpenGLRenderer *renderer) : OpenGLPart(renderer) { + layers_lock = new Mutex(); + updater = new VegetationUpdater(this); + enabled = true; + + // Watch scenery changes + renderer->getScenery()->getVegetation()->addWatcher(this, true); +} + +OpenGLVegetation::~OpenGLVegetation() { + for (auto layer: layers) { + delete layer; + } + layers.clear(); + + delete layers_lock; + + updater->interrupt(); + delete updater; +} + +void OpenGLVegetation::initialize() { + // Start the threaded updater + updater->start(); + + // Prepare shader programs + program = createShader("vegetation"); + program->addVertexSource("vegetation"); + program->addFragmentSource("atmosphere"); + program->addFragmentSource("tonemapping"); + program->addFragmentSource("ui"); + program->addFragmentSource("vegetation"); +} + +void OpenGLVegetation::update() { +} + +void OpenGLVegetation::render() { + if (enabled) { + std::vector layers; + acquireLayers(layers); + for (auto layer: layers) { + layer->render(); + } + releaseLayers(layers); + } +} + +void OpenGLVegetation::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) { + if (node->getPath() == "/vegetation") { + updateLayers(); + } +} + +Scenery *OpenGLVegetation::getScenery() const +{ + return renderer->getScenery(); +} + +void OpenGLVegetation::cameraChanged(const CameraDefinition *camera) +{ + std::vector layers; + acquireLayers(layers); + for (auto layer: layers) { + layer->setCamera(camera); + } + releaseLayers(layers); +} + +void OpenGLVegetation::acquireLayers(std::vector &layers) { + layers_lock->acquire(); + + for (auto layer : this->layers) { + // TODO Reference count + layers.push_back(layer); + } + + layers_lock->release(); +} + +void OpenGLVegetation::releaseLayers(const std::vector &layers) { + // TODO Reference count +} + +OpenGLVegetationLayer *OpenGLVegetation::findLayer(VegetationLayerDefinition *layer) { + for (auto &l : layers) { + if (l->getDefinition() == layer) { + return l; + } + } + return NULL; +} + +void OpenGLVegetation::setEnabled(bool enabled) { + this->enabled = enabled; +} + +void OpenGLVegetation::updateLayers() { + layers_lock->acquire(); + + // Add missing layers + int n = renderer->getScenery()->getVegetation()->getLayerCount(); + for (int i = 0; i < n; i++) { + VegetationLayerDefinition *layer = renderer->getScenery()->getVegetation()->getVegetationLayer(i); + if (!findLayer(layer)) { + layers.push_back(new OpenGLVegetationLayer(this, layer)); + } + } + + // TODO Mark extraneous layers for deletion + + layers_lock->release(); +} diff --git a/src/render/opengl/OpenGLVegetation.h b/src/render/opengl/OpenGLVegetation.h new file mode 100644 index 0000000..c91424e --- /dev/null +++ b/src/render/opengl/OpenGLVegetation.h @@ -0,0 +1,89 @@ +#ifndef OPENGLVEGETATION_H +#define OPENGLVEGETATION_H + +#include "opengl_global.h" + +#include "OpenGLPart.h" +#include "DefinitionWatcher.h" + +#include + +namespace paysages { +namespace opengl { + +class VegetationUpdater; + +class OPENGLSHARED_EXPORT OpenGLVegetation : public OpenGLPart, public DefinitionWatcher { + public: + OpenGLVegetation(OpenGLRenderer *renderer); + virtual ~OpenGLVegetation(); + + inline int getLayerCount() { + return layers.size(); + } + inline OpenGLVegetationLayer *getLayer(int i) { + return layers[i]; + } + inline OpenGLShaderProgram *getProgram() { + return program; + } + + virtual void initialize() override; + virtual void update() override; + virtual void render() override; + + virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override; + + /** + * Get the currently rendered scenery. + */ + Scenery *getScenery() const; + + /** + * Call this when the dynamic camera (not the scenery one) changed. + */ + void cameraChanged(const CameraDefinition *camera); + + /** + * Acquire the current layers for processing. + * + * Don't forget to call releaseLayers once done with them. + * + * This will not hold a lock on them, but increment a reference counter to not delete them while in use. + */ + void acquireLayers(std::vector &layers); + + /** + * Release the layers acquired by acquireLayers. + */ + void releaseLayers(const std::vector &layers); + + /** + * Find a rendering layer, by the matching definition layer. + */ + OpenGLVegetationLayer *findLayer(VegetationLayerDefinition *layer); + + /** + * Enable or disable the whole vegetation rendering. + */ + void setEnabled(bool enabled); + + /** + * Update the *layers* member from the scenery. + * + * This will create missing layers, and mark extraneous ones for deletion. + * This will not update existing layers (they should update themselves by watching their definition). + */ + void updateLayers(); + + private: + OpenGLShaderProgram *program; + bool enabled; + std::vector layers; + Mutex *layers_lock; + VegetationUpdater *updater; +}; +} +} + +#endif // OPENGLVEGETATION_H diff --git a/src/render/opengl/OpenGLVegetationImpostor.cpp b/src/render/opengl/OpenGLVegetationImpostor.cpp new file mode 100644 index 0000000..5cd2331 --- /dev/null +++ b/src/render/opengl/OpenGLVegetationImpostor.cpp @@ -0,0 +1,106 @@ +#include "OpenGLVegetationImpostor.h" + +#include "OpenGLShaderProgram.h" +#include "OpenGLSharedState.h" +#include "OpenGLVegetationInstance.h" +#include "Texture2D.h" +#include "SoftwareRenderer.h" +#include "Scenery.h" +#include "AtmosphereDefinition.h" +#include "GodRaysSampler.h" +#include "VegetationRenderer.h" +#include "VegetationInstance.h" +#include "RayCastingResult.h" +#include "SpaceSegment.h" +#include "Matrix4.h" +#include "LightingManager.h" +#include "CameraDefinition.h" + +OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) { + vertices = new float[4 * 3]; + texture_size = partsize * 4; + texture = new Texture2D(texture_size, texture_size); + texture_changed = true; + + setVertex(0, 0.0f, 0.0f, 0.0f); + setVertex(1, 0.0f, 0.0f, 1.0f); + setVertex(2, 0.0f, 1.0f, 0.0f); + setVertex(3, 0.0f, 1.0f, 1.0f); +} + +OpenGLVegetationImpostor::~OpenGLVegetationImpostor() { + delete[] vertices; + delete texture; +} + +void OpenGLVegetationImpostor::render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, + int index) { + if (index == 0 or texture_changed) { + texture_changed = false; + program->getState()->set("impostorTexture", texture); + } + program->getState()->setInt("index", 15); // TODO + program->getState()->set("offset", instance->getBase()); + program->getState()->set("size", instance->getSize()); + program->drawTriangleStrip(vertices, 4); +} + +void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, + bool *interrupt) { + Scenery scenery; + environment.getAtmosphere()->copy(scenery.getAtmosphere()); + SoftwareRenderer renderer(&scenery); + // FIXME Self light filtering + renderer.getLightingManager()->clearFilters(); + renderer.getGodRaysSampler()->setEnabled(false); + VegetationRenderer *vegetation = renderer.getVegetationRenderer(); + VegetationInstance instance(model, VECTOR_ZERO); + + int parts = 4; + int partsize = texture_size / parts; + Matrix4 rotation; + for (int py = 0; py < parts; py++) { + for (int px = 0; px < parts; px++) { + int index = py * parts + px; + if (index == 0) { + rotation = Matrix4::newRotateX(-M_PI_2); + } else if (index < 6) { + rotation = Matrix4::newRotateY(M_2PI * (double)(index - 1) * 0.2).mult(Matrix4::newRotateX(-M_PI_4)); + } else { + rotation = Matrix4::newRotateY(M_2PI * (double)(index - 6) * 0.1); + } + + Vector3 cam(0.0, 0.0, 5.0); + scenery.getCamera()->setLocation(rotation.multPoint(cam)); + scenery.getCamera()->setTarget(VECTOR_ZERO); + renderer.prepare(); + + int startx = px * partsize; + int starty = py * partsize; + + for (int x = 0; x < partsize; x++) { + double dx = (double)x / (double)partsize; + for (int y = 0; y < partsize; y++) { + double dy = (double)y / (double)partsize; + + Vector3 near(dx - 0.5, dy - 0.5, 5.0); + Vector3 far(dx - 0.5, dy - 0.5, -5.0); + SpaceSegment segment(rotation.multPoint(near.scale(1.3)).add(VECTOR_UP.scale(0.5)), + rotation.multPoint(far.scale(1.3)).add(VECTOR_UP.scale(0.5))); + + RayCastingResult result = vegetation->renderInstance(segment, instance, false, true); + texture->setPixel(startx + x, starty + y, + result.hit ? result.hit_color.normalized() : COLOR_TRANSPARENT); + } + } + } + } + + texture_changed = true; +} + +void OpenGLVegetationImpostor::setVertex(int i, float x, float y, float z) { + vertices[i * 3] = x; + vertices[i * 3 + 1] = y; + vertices[i * 3 + 2] = z; +} diff --git a/src/render/opengl/OpenGLVegetationImpostor.h b/src/render/opengl/OpenGLVegetationImpostor.h new file mode 100644 index 0000000..f37136d --- /dev/null +++ b/src/render/opengl/OpenGLVegetationImpostor.h @@ -0,0 +1,43 @@ +#ifndef OPENGLVEGETATIONIMPOSTOR_H +#define OPENGLVEGETATIONIMPOSTOR_H + +#include "opengl_global.h" + +namespace paysages { +namespace opengl { + +/** + * A tool to render an "impostor" of a vegetation layer. + */ +class OPENGLSHARED_EXPORT OpenGLVegetationImpostor { + public: + OpenGLVegetationImpostor(int partsize = 64); + ~OpenGLVegetationImpostor(); + + inline const Texture2D *getTexture() const { + return texture; + } + + /** + * Render a single instance using this impostor. + */ + void render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, int index); + + /** + * Prepare the texture grid for a given model. + */ + void prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, bool *interrupt); + + private: + void setVertex(int i, float x, float y, float z); + + private: + float *vertices; + int texture_size; + bool texture_changed; + Texture2D *texture; +}; +} +} + +#endif // OPENGLVEGETATIONIMPOSTOR_H diff --git a/src/render/opengl/OpenGLVegetationInstance.cpp b/src/render/opengl/OpenGLVegetationInstance.cpp new file mode 100644 index 0000000..4c72296 --- /dev/null +++ b/src/render/opengl/OpenGLVegetationInstance.cpp @@ -0,0 +1,11 @@ +#include "OpenGLVegetationInstance.h" + +#include "VegetationInstance.h" + +OpenGLVegetationInstance::OpenGLVegetationInstance(const VegetationInstance &wrapped) : wrapped(wrapped) { +} + +void OpenGLVegetationInstance::setDistance(double distance) +{ + this->distance = distance; +} diff --git a/src/render/opengl/OpenGLVegetationInstance.h b/src/render/opengl/OpenGLVegetationInstance.h new file mode 100644 index 0000000..a4262ac --- /dev/null +++ b/src/render/opengl/OpenGLVegetationInstance.h @@ -0,0 +1,43 @@ +#ifndef OPENGLVEGETATIONINSTANCE_H +#define OPENGLVEGETATIONINSTANCE_H + +#include "opengl_global.h" + +#include "VegetationInstance.h" + +namespace paysages { +namespace opengl { + +/** + * A single instance of vegetation. + */ +class OPENGLSHARED_EXPORT OpenGLVegetationInstance { + public: + OpenGLVegetationInstance(const VegetationInstance &wrapped); + + inline const VegetationModelDefinition &getModel() const { + return wrapped.getModel(); + } + inline const Vector3 &getBase() const { + return wrapped.getBase(); + } + inline double getSize() const { + return wrapped.getSize(); + } + inline double getDistance() const { + return distance; + } + + /** + * Set the distance to camera, mainly for sorting. + */ + void setDistance(double distance); + +private: + VegetationInstance wrapped; + double distance; +}; +} +} + +#endif // OPENGLVEGETATIONINSTANCE_H diff --git a/src/render/opengl/OpenGLVegetationLayer.cpp b/src/render/opengl/OpenGLVegetationLayer.cpp new file mode 100644 index 0000000..a0d9c43 --- /dev/null +++ b/src/render/opengl/OpenGLVegetationLayer.cpp @@ -0,0 +1,141 @@ +#include "OpenGLVegetationLayer.h" + +#include OPENGL_FUNCTIONS_INCLUDE +#include +#include "Vector3.h" +#include "CameraDefinition.h" +#include "Mutex.h" +#include "OpenGLVegetation.h" +#include "OpenGLVegetationInstance.h" +#include "OpenGLVegetationImpostor.h" +#include "VegetationLayerDefinition.h" +#include "VegetationPresenceDefinition.h" + +OpenGLVegetationLayer::OpenGLVegetationLayer(OpenGLVegetation *parent, VegetationLayerDefinition *definition, + bool own_instances) + : parent(parent), definition(definition), own_instances(own_instances) { + lock_instances = new Mutex; + camera_location = new Vector3(0.0, 0.0, 0.0); + impostor = new OpenGLVegetationImpostor(); + range = 10.0; + + reset(); +} + +OpenGLVegetationLayer::~OpenGLVegetationLayer() { + delete camera_location; + delete lock_instances; + delete impostor; +} + +void OpenGLVegetationLayer::produceInstancesInArea(double xmin, double xmax, double zmin, double zmax, + std::vector *instances) const { + std::vector result; + definition->getPresence()->collectInstances(&result, *definition->getModel(), xmin, zmin, xmax, zmax, false); + for (auto raw_instance : result) { + instances->push_back(new OpenGLVegetationInstance(raw_instance)); + } +} + +static bool isNull(void *ptr) { + return ptr == NULL; +} + +static bool compareInstances(OpenGLVegetationInstance *instance1, OpenGLVegetationInstance *instance2) { + return instance1->getDistance() > instance2->getDistance(); +} + +void OpenGLVegetationLayer::removeInstancesOutsideArea(double xmin, double xmax, double zmin, double zmax, + std::vector *instances) const { + for (auto &instance : *instances) { + Vector3 base = instance->getBase(); + if (base.x < xmin or base.x >= xmax or base.z < zmin or base.z >= zmax) { + if (own_instances) { + delete instance; + } + instance = NULL; + } + } + instances->erase(std::remove_if(instances->begin(), instances->end(), isNull), instances->end()); +} + +void OpenGLVegetationLayer::threadedUpdate() { + if (camera_changed) { + camera_changed = false; + + // Compute new area around camera + double newxmin, newxmax, newzmin, newzmax; + newxmin = camera_location->x - range; + newxmax = camera_location->x + range; + newzmin = camera_location->z - range; + newzmax = camera_location->z + range; + + // Prepare instances where area grew + std::vector new_instances; + if (newxmin < xmin) { + produceInstancesInArea(newxmin, xmin, newzmin, newzmax, &new_instances); + } + if (newxmax > xmax) { + produceInstancesInArea(xmax, newxmax, newzmin, newzmax, &new_instances); + } + if (newzmin < zmin) { + produceInstancesInArea(xmin, xmax, newzmin, zmin, &new_instances); + } + if (newzmax > zmax) { + produceInstancesInArea(xmin, xmax, zmax, newzmax, &new_instances); + } + + // Apply the changes + lock_instances->acquire(); + xmin = newxmin; + xmax = newxmax; + zmin = newzmin; + zmax = newzmax; + removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances); + instances.insert(instances.end(), new_instances.begin(), new_instances.end()); + for (auto instance: instances) { + instance->setDistance(instance->getBase().sub(*camera_location).getNorm()); + } + std::sort(instances.begin(), instances.end(), compareInstances); + lock_instances->release(); + + // Update impostor texture + bool interrupted = false; + impostor->prepareTexture(*definition->getModel(), *parent->getScenery(), &interrupted); + } +} + +void OpenGLVegetationLayer::render() { + lock_instances->acquire(); + + // TODO Instanced rendering + int index = 0; + for (auto instance : instances) { + impostor->render(parent->getProgram(), instance, index++); + } + + lock_instances->release(); +} + +void OpenGLVegetationLayer::reset() { + lock_instances->acquire(); + camera_changed = true; + xmin = 0.0; + xmax = 0.0; + zmin = 0.0; + zmax = 0.0; + if (own_instances) { + for (auto instance : instances) { + delete instance; + } + } + instances.clear(); + lock_instances->release(); +} + +void OpenGLVegetationLayer::setCamera(const CameraDefinition *camera) { + if (camera_location->sub(camera->getLocation()).getNorm() > 1.0) { + *camera_location = camera->getLocation(); + camera_changed = true; + } +} diff --git a/src/render/opengl/OpenGLVegetationLayer.h b/src/render/opengl/OpenGLVegetationLayer.h new file mode 100644 index 0000000..a3d0139 --- /dev/null +++ b/src/render/opengl/OpenGLVegetationLayer.h @@ -0,0 +1,82 @@ +#ifndef OPENGLVEGETATIONLAYER_H +#define OPENGLVEGETATIONLAYER_H + +#include "opengl_global.h" + +#include + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLVegetationLayer { + public: + OpenGLVegetationLayer(OpenGLVegetation *parent, VegetationLayerDefinition *definition, bool own_instances = true); + virtual ~OpenGLVegetationLayer(); + + inline auto getDefinition() const { + return definition; + } + inline int getInstanceCount() const { + return (int)instances.size(); + } + + /** + * Collect instances in a given area, and add them to the array *instances*. + * + * The array is not checked for already present instances. + */ + virtual void produceInstancesInArea(double xmin, double xmax, double zmin, double zmax, + std::vector *instances) const; + + /** + * Remove instances outside of a given area. + */ + virtual void removeInstancesOutsideArea(double xmin, double xmax, double zmin, double zmax, + std::vector *instances) const; + + /** + * Perform maintenance tasks that can be perform in a thread. + * + * This will be called from a thread separate from the main GUI thread, + * so it should not call OpenGL functions. + */ + void threadedUpdate(); + + /** + * Render the vegetation layer. + * + * This is called from the GUI thread. + */ + void render(); + + /** + * Reset to an initial state. + * + * It is only useful (and safe) from unit testing. + */ + void reset(); + + /** + * Set the current camera in use. + */ + void setCamera(const CameraDefinition *camera); + + private: + OpenGLVegetation *parent; + VegetationLayerDefinition *definition; + double xmin; + double xmax; + double zmin; + double zmax; + double range; + bool own_instances; + std::vector instances; + Mutex *lock_instances; + OpenGLVegetationImpostor *impostor; + Vector3 *camera_location; + bool camera_changed; +}; +} +} + +#endif // OPENGLVEGETATIONLAYER_H diff --git a/src/render/opengl/opengl.pro b/src/render/opengl/opengl.pro index c1034e4..26740cf 100644 --- a/src/render/opengl/opengl.pro +++ b/src/render/opengl/opengl.pro @@ -55,3 +55,7 @@ RESOURCES += \ OTHER_FILES += \ shaders/*.frag \ shaders/*.vert + +DISTFILES += \ + shaders/vegetation.frag \ + shaders/vegetation.vert diff --git a/src/render/opengl/opengl_global.h b/src/render/opengl/opengl_global.h index 1d1c56c..b0a7223 100644 --- a/src/render/opengl/opengl_global.h +++ b/src/render/opengl/opengl_global.h @@ -19,6 +19,10 @@ class OpenGLVariable; class OpenGLSkybox; class OpenGLWater; class OpenGLTerrain; +class OpenGLVegetation; +class OpenGLVegetationLayer; +class OpenGLVegetationInstance; +class OpenGLVegetationImpostor; class ExplorerChunkTerrain; template class VertexArray; } diff --git a/src/render/opengl/shaders/resources.qrc b/src/render/opengl/shaders/resources.qrc index 68f3f23..7c4e792 100644 --- a/src/render/opengl/shaders/resources.qrc +++ b/src/render/opengl/shaders/resources.qrc @@ -11,5 +11,7 @@ fadeout.frag noise.frag ui.frag + vegetation.vert + vegetation.frag diff --git a/src/render/opengl/shaders/vegetation.frag b/src/render/opengl/shaders/vegetation.frag new file mode 100644 index 0000000..e0ee288 --- /dev/null +++ b/src/render/opengl/shaders/vegetation.frag @@ -0,0 +1,15 @@ +varying vec2 texcoord; +uniform sampler2D impostorTexture; + +void main(void) +{ + gl_FragColor = texture2D(impostorTexture, texcoord); + float alpha = gl_FragColor.a; + + gl_FragColor = applyAerialPerspective(gl_FragColor); + + gl_FragColor = applyToneMapping(gl_FragColor); + + gl_FragColor = applyMouseTracking(unprojected, gl_FragColor); + gl_FragColor.a = alpha; +} diff --git a/src/render/opengl/shaders/vegetation.vert b/src/render/opengl/shaders/vegetation.vert new file mode 100644 index 0000000..a7915d3 --- /dev/null +++ b/src/render/opengl/shaders/vegetation.vert @@ -0,0 +1,16 @@ +attribute highp vec4 vertex; +uniform highp mat4 viewMatrix; +uniform highp vec3 offset; +uniform float size; +uniform int index; +varying vec3 unprojected; +varying vec2 texcoord; +uniform float waterOffset; + +void main(void) +{ + vec3 final = offset + size * (vertex.xyz - vec3(0.0, 0.0, 0.5)); // + vec3(0, waterOffset, 0) + unprojected = final.xyz; + texcoord = vec2(0.25 * (vertex.z + float(mod(index, 4))), 0.25 * (vertex.y + float(index / 4))); + gl_Position = viewMatrix * vec4(final.xyz, 1.0); +} diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index 22a04e6..86b3048 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -64,6 +64,7 @@ SoftwareRenderer::~SoftwareRenderer() { delete clouds_renderer; delete terrain_renderer; delete textures_renderer; + delete vegetation_renderer; delete water_renderer; } diff --git a/src/render/software/VegetationModelRenderer.cpp b/src/render/software/VegetationModelRenderer.cpp index 987a8e1..841c948 100644 --- a/src/render/software/VegetationModelRenderer.cpp +++ b/src/render/software/VegetationModelRenderer.cpp @@ -10,6 +10,13 @@ #include "VegetationModelDefinition.h" #include "VegetationResult.h" +#ifndef NDEBUG +//#define DEBUG_VEGETATION_CONTAINERS 1 +#endif +#ifdef DEBUG_VEGETATION_CONTAINERS +SurfaceMaterial DEBUG_MATERIAL1(Color(1.0, 0.0, 0.0)); +#endif + VegetationModelRenderer::VegetationModelRenderer(SoftwareRenderer *parent, const VegetationModelDefinition *model) : parent(parent), model(model) { } @@ -98,6 +105,16 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, } } } +#ifdef DEBUG_VEGETATION_CONTAINERS + if (!hit) { + Vector3 near, far; + intersections = foliage.findRayIntersection(ray, &near, &far); + location = near; + normal = VECTOR_ZERO; + material = &DEBUG_MATERIAL1; + hit = true; + } +#endif } } diff --git a/src/render/software/VegetationRenderer.cpp b/src/render/software/VegetationRenderer.cpp index e15d696..b859c70 100644 --- a/src/render/software/VegetationRenderer.cpp +++ b/src/render/software/VegetationRenderer.cpp @@ -44,6 +44,9 @@ VegetationRenderer::VegetationRenderer(SoftwareRenderer *parent) : parent(parent enabled = true; } +VegetationRenderer::~VegetationRenderer() { +} + void VegetationRenderer::setEnabled(bool enabled) { this->enabled = enabled; } diff --git a/src/render/software/VegetationRenderer.h b/src/render/software/VegetationRenderer.h index eaaf2f7..41efad2 100644 --- a/src/render/software/VegetationRenderer.h +++ b/src/render/software/VegetationRenderer.h @@ -11,6 +11,7 @@ namespace software { class SOFTWARESHARED_EXPORT VegetationRenderer : public LightFilter { public: VegetationRenderer(SoftwareRenderer *parent); + virtual ~VegetationRenderer(); /** * Totally enable or disable the vegetation layers rendering. diff --git a/src/tests/OpenGLVegetationLayer_Test.cpp b/src/tests/OpenGLVegetationLayer_Test.cpp new file mode 100644 index 0000000..52736c9 --- /dev/null +++ b/src/tests/OpenGLVegetationLayer_Test.cpp @@ -0,0 +1,67 @@ +#include "BaseTestCase.h" +#include "OpenGLVegetationLayer.h" + +#include "VegetationLayerDefinition.h" +#include "VegetationModelDefinition.h" +#include "VegetationInstance.h" +#include "OpenGLVegetationInstance.h" +#include "CameraDefinition.h" + +class FakeLayerRenderer : public OpenGLVegetationLayer { + public: + FakeLayerRenderer(VegetationLayerDefinition *definition) : OpenGLVegetationLayer(NULL, definition, false) { + } + virtual ~FakeLayerRenderer() { + for (auto instance : static_instances) { + delete instance; + } + } + virtual void produceInstancesInArea(double xmin, double xmax, double zmin, double zmax, + std::vector *instances) const override { + for (auto instance : static_instances) { + Vector3 location = instance->getBase(); + if (location.x >= xmin and location.z >= zmin and location.x < xmax and location.z < zmax) { + instances->push_back(instance); + } + } + } + std::vector static_instances; +}; + +TEST(OpenGLVegetationLayer, threadedUpdate) { + CameraDefinition camera; + VegetationLayerDefinition definition(NULL, "test"); + FakeLayerRenderer rendering(&definition); + VegetationModelDefinition model(NULL); + + EXPECT_EQ(0, rendering.getInstanceCount()); + + rendering.threadedUpdate(); + EXPECT_EQ(0, rendering.getInstanceCount()); + + rendering.static_instances.push_back( + new OpenGLVegetationInstance(VegetationInstance(model, Vector3(0.0, 0.0, 0.0)))); + rendering.reset(); + rendering.threadedUpdate(); + EXPECT_EQ(1, rendering.getInstanceCount()); + + camera.setLocation(Vector3(-5.0, 0.0, 0.0)); + rendering.setCamera(&camera); + rendering.threadedUpdate(); + EXPECT_EQ(1, rendering.getInstanceCount()); + + camera.setLocation(Vector3(-11.0, 0.0, 0.0)); + rendering.setCamera(&camera); + rendering.threadedUpdate(); + EXPECT_EQ(0, rendering.getInstanceCount()); + + camera.setLocation(Vector3(0.0, 0.0, 5.0)); + rendering.setCamera(&camera); + rendering.threadedUpdate(); + EXPECT_EQ(1, rendering.getInstanceCount()); + + camera.setLocation(Vector3(0.0, 0.0, 15.0)); + rendering.setCamera(&camera); + rendering.threadedUpdate(); + EXPECT_EQ(0, rendering.getInstanceCount()); +} diff --git a/src/tests/OpenGLVegetation_Test.cpp b/src/tests/OpenGLVegetation_Test.cpp new file mode 100644 index 0000000..a662b13 --- /dev/null +++ b/src/tests/OpenGLVegetation_Test.cpp @@ -0,0 +1,19 @@ +#include "BaseTestCase.h" +#include "OpenGLVegetation.h" + +#include "Scenery.h" +#include "VegetationDefinition.h" +#include "VegetationLayerDefinition.h" +#include "OpenGLRenderer.h" + +TEST(OpenGLVegetation, updateLayers) { + Scenery scenery; + OpenGLRenderer renderer(&scenery); + OpenGLVegetation glvegetation(&renderer); + + EXPECT_EQ(0, glvegetation.getLayerCount()); + + scenery.getVegetation()->addLayer("test"); + + EXPECT_EQ(1, glvegetation.getLayerCount()); +} diff --git a/src/tests/OverlayRasterizer_Test.cpp b/src/tests/OverlayRasterizer_Test.cpp index a598cb4..931d733 100644 --- a/src/tests/OverlayRasterizer_Test.cpp +++ b/src/tests/OverlayRasterizer_Test.cpp @@ -40,8 +40,9 @@ TEST(OverlayRasterizer, pixelProcessing) { Scenery scenery; SoftwareCanvasRenderer renderer(&scenery); + MockOverlayRasterizer rasterizer(&renderer); renderer.setSize(4, 3); - renderer.setSoloRasterizer(new MockOverlayRasterizer(&renderer)); + renderer.setSoloRasterizer(&rasterizer); renderer.render(); ASSERT_EQ(12, (int)calls.size()); From d2efb599d98e9219dedc896f3ad7d910fe683024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 29 Nov 2015 19:18:36 +0100 Subject: [PATCH 16/17] Made the vegetation impostors face the camera --- Makefile | 5 + .../VegetationPresenceDefinition.cpp | 3 +- src/interface/commandline/tests.cpp | 23 ++-- src/render/opengl/OpenGLPart.cpp | 6 +- src/render/opengl/OpenGLShaderProgram.cpp | 54 +++++++-- src/render/opengl/OpenGLShaderProgram.h | 3 + src/render/opengl/OpenGLSharedState.cpp | 3 +- src/render/opengl/OpenGLSharedState.h | 2 +- src/render/opengl/OpenGLVariable.cpp | 3 +- src/render/opengl/OpenGLVegetation.cpp | 14 +-- .../opengl/OpenGLVegetationImpostor.cpp | 104 ++++++++++++------ src/render/opengl/OpenGLVegetationImpostor.h | 11 +- .../opengl/OpenGLVegetationInstance.cpp | 3 +- src/render/opengl/OpenGLVegetationInstance.h | 2 +- src/render/opengl/OpenGLVegetationLayer.cpp | 4 +- src/render/opengl/shaders/vegetation.vert | 10 +- src/tests/OpenGLVegetationImpostor_Test.cpp | 21 ++++ 17 files changed, 192 insertions(+), 79 deletions(-) create mode 100644 src/tests/OpenGLVegetationImpostor_Test.cpp diff --git a/Makefile b/Makefile index 61156ac..4ded77d 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,11 @@ profile_cli:build LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS) perf report -g +gltrace:build + rm -f *.trace + LD_PRELOAD=/usr/lib/x86_64-linux-gnu/apitrace/wrappers/glxtrace.so LD_LIBRARY_PATH=$(LIBRARY_PATH) ${BUILDPATH}/interface/modeler/quickapp/paysages-modeler $(ARGS) + qapitrace paysages-modeler.trace + package:build rm -rf paysages3d-linux rm -f paysages3d-linux.tar.bz2 diff --git a/src/definition/VegetationPresenceDefinition.cpp b/src/definition/VegetationPresenceDefinition.cpp index ccb212e..3645af5 100644 --- a/src/definition/VegetationPresenceDefinition.cpp +++ b/src/definition/VegetationPresenceDefinition.cpp @@ -44,8 +44,7 @@ bool VegetationPresenceDefinition::collectInstances(std::vectorget2DTotal(-x * 20.0, z * 20.0); // TODO balanced distribution double xo = x + fabs(generator->get2DTotal(x * 12.0, -z * 12.0)); double zo = z + fabs(generator->get2DTotal(-x * 27.0, -z * 27.0)); - if (xo >= xmin and xo < xmax and zo >= zmin and zo < zmax) - { + if (xo >= xmin and xo < xmax and zo >= zmin and zo < zmax) { double y = getScenery()->getTerrain()->getInterpolatedHeight(xo, zo, true, true); result->push_back(VegetationInstance(model, Vector3(xo, y, zo), size, angle)); added++; diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index 9d624f3..3a919af 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -296,16 +296,21 @@ static void testVegetationModels() { } static void testOpenGLVegetationImpostor() { - std::string filename = getFileName("opengl_vegetation_impostor"); - std::cout << "Rendering " << filename << "..." << std::endl; + for (int i = 0; i < 4; i++) { + std::string filename = getFileName("opengl_vegetation_impostor", i); + std::cout << "Rendering " << filename << "..." << std::endl; - Scenery scenery; - scenery.autoPreset(1); - OpenGLVegetationImpostor impostor(200); - VegetationModelDefinition model(NULL); - bool interrupted = false; - impostor.prepareTexture(model, scenery, &interrupted); - impostor.getTexture()->saveToFile(filename); + Scenery scenery; + scenery.autoPreset(i); + + OpenGLVegetationImpostor impostor(128); + VegetationModelDefinition model(NULL); + + bool interrupted = false; + impostor.prepareTexture(model, scenery, &interrupted); + + impostor.getTexture()->saveToFile(filename); + } } void runTestSuite() { diff --git a/src/render/opengl/OpenGLPart.cpp b/src/render/opengl/OpenGLPart.cpp index 026ad19..1c27a55 100644 --- a/src/render/opengl/OpenGLPart.cpp +++ b/src/render/opengl/OpenGLPart.cpp @@ -41,12 +41,10 @@ void OpenGLPart::updateScenery(bool onlyCommon) { } } -Scenery *OpenGLPart::getScenery() const -{ +Scenery *OpenGLPart::getScenery() const { return renderer->getScenery(); } -OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const -{ +OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const { return renderer->getOpenGlFunctions(); } diff --git a/src/render/opengl/OpenGLShaderProgram.cpp b/src/render/opengl/OpenGLShaderProgram.cpp index 8e0387b..185f0c4 100644 --- a/src/render/opengl/OpenGLShaderProgram.cpp +++ b/src/render/opengl/OpenGLShaderProgram.cpp @@ -75,13 +75,13 @@ void OpenGLShaderProgram::release() { void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) { bind(); - GLuint vertex = program->attributeLocation("vertex"); - program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); - program->enableAttributeArray(vertex); + GLuint array_vertex = program->attributeLocation("vertex"); + program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(array_vertex); functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3); - program->disableAttributeArray(vertex); + program->disableAttributeArray(array_vertex); release(); } @@ -89,13 +89,51 @@ void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) { void OpenGLShaderProgram::drawTriangleStrip(float *vertices, int vertex_count) { bind(); - GLuint vertex = program->attributeLocation("vertex"); - program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); - program->enableAttributeArray(vertex); + GLuint array_vertex = program->attributeLocation("vertex"); + program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(array_vertex); functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); - program->disableAttributeArray(vertex); + program->disableAttributeArray(array_vertex); + + release(); +} + +void OpenGLShaderProgram::drawTrianglesUV(float *vertices, float *uv, int triangle_count) { + bind(); + + GLuint array_vertex = program->attributeLocation("vertex"); + program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(array_vertex); + + GLuint array_uv = program->attributeLocation("uv"); + program->setAttributeArray(array_uv, GL_FLOAT, uv, 2); + program->enableAttributeArray(array_uv); + + functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3); + + program->disableAttributeArray(array_vertex); + program->disableAttributeArray(array_uv); + + release(); +} + +void OpenGLShaderProgram::drawTriangleStripUV(float *vertices, float *uv, int vertex_count) { + bind(); + + GLuint array_vertex = program->attributeLocation("vertex"); + program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(array_vertex); + + GLuint array_uv = program->attributeLocation("uv"); + program->setAttributeArray(array_uv, GL_FLOAT, uv, 2); + program->enableAttributeArray(array_uv); + + functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); + + program->disableAttributeArray(array_vertex); + program->disableAttributeArray(array_uv); release(); } diff --git a/src/render/opengl/OpenGLShaderProgram.h b/src/render/opengl/OpenGLShaderProgram.h index 74482fc..8a85e73 100644 --- a/src/render/opengl/OpenGLShaderProgram.h +++ b/src/render/opengl/OpenGLShaderProgram.h @@ -21,6 +21,9 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram { void drawTriangles(float *vertices, int triangle_count); void drawTriangleStrip(float *vertices, int vertex_count); + void drawTrianglesUV(float *vertices, float *uv, int triangle_count); + void drawTriangleStripUV(float *vertices, float *uv, int vertex_count); + void bind(); void release(); diff --git a/src/render/opengl/OpenGLSharedState.cpp b/src/render/opengl/OpenGLSharedState.cpp index 5af618e..76c2f72 100644 --- a/src/render/opengl/OpenGLSharedState.cpp +++ b/src/render/opengl/OpenGLSharedState.cpp @@ -3,8 +3,7 @@ OpenGLSharedState::OpenGLSharedState() { } -OpenGLSharedState::~OpenGLSharedState() -{ +OpenGLSharedState::~OpenGLSharedState() { for (const auto &pair : variables) { delete pair.second; } diff --git a/src/render/opengl/OpenGLSharedState.h b/src/render/opengl/OpenGLSharedState.h index 742ea01..1aa72a2 100644 --- a/src/render/opengl/OpenGLSharedState.h +++ b/src/render/opengl/OpenGLSharedState.h @@ -15,7 +15,7 @@ namespace opengl { class OPENGLSHARED_EXPORT OpenGLSharedState { public: OpenGLSharedState(); - ~OpenGLSharedState(); + ~OpenGLSharedState(); /*! * \brief Apply the stored variables to the bound program. diff --git a/src/render/opengl/OpenGLVariable.cpp b/src/render/opengl/OpenGLVariable.cpp index 1176cca..db8f39d 100644 --- a/src/render/opengl/OpenGLVariable.cpp +++ b/src/render/opengl/OpenGLVariable.cpp @@ -91,8 +91,7 @@ void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) { texture_color = color; } -void OpenGLVariable::set(int value) -{ +void OpenGLVariable::set(int value) { assert(type == TYPE_NONE or type == TYPE_INTEGER); type = TYPE_INTEGER; diff --git a/src/render/opengl/OpenGLVegetation.cpp b/src/render/opengl/OpenGLVegetation.cpp index c64ce14..2cf24c5 100644 --- a/src/render/opengl/OpenGLVegetation.cpp +++ b/src/render/opengl/OpenGLVegetation.cpp @@ -27,7 +27,7 @@ class paysages::opengl::VegetationUpdater : public Thread { while (not interrupted) { std::vector layers; vegetation->acquireLayers(layers); - for (auto layer: layers) { + for (auto layer : layers) { layer->threadedUpdate(); } vegetation->releaseLayers(layers); @@ -50,7 +50,7 @@ OpenGLVegetation::OpenGLVegetation(OpenGLRenderer *renderer) : OpenGLPart(render } OpenGLVegetation::~OpenGLVegetation() { - for (auto layer: layers) { + for (auto layer : layers) { delete layer; } layers.clear(); @@ -81,7 +81,7 @@ void OpenGLVegetation::render() { if (enabled) { std::vector layers; acquireLayers(layers); - for (auto layer: layers) { + for (auto layer : layers) { layer->render(); } releaseLayers(layers); @@ -94,16 +94,14 @@ void OpenGLVegetation::nodeChanged(const DefinitionNode *node, const DefinitionD } } -Scenery *OpenGLVegetation::getScenery() const -{ +Scenery *OpenGLVegetation::getScenery() const { return renderer->getScenery(); } -void OpenGLVegetation::cameraChanged(const CameraDefinition *camera) -{ +void OpenGLVegetation::cameraChanged(const CameraDefinition *camera) { std::vector layers; acquireLayers(layers); - for (auto layer: layers) { + for (auto layer : layers) { layer->setCamera(camera); } releaseLayers(layers); diff --git a/src/render/opengl/OpenGLVegetationImpostor.cpp b/src/render/opengl/OpenGLVegetationImpostor.cpp index 5cd2331..7cc3510 100644 --- a/src/render/opengl/OpenGLVegetationImpostor.cpp +++ b/src/render/opengl/OpenGLVegetationImpostor.cpp @@ -1,5 +1,6 @@ #include "OpenGLVegetationImpostor.h" +#include #include "OpenGLShaderProgram.h" #include "OpenGLSharedState.h" #include "OpenGLVegetationInstance.h" @@ -16,16 +17,30 @@ #include "LightingManager.h" #include "CameraDefinition.h" -OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) { - vertices = new float[4 * 3]; - texture_size = partsize * 4; - texture = new Texture2D(texture_size, texture_size); - texture_changed = true; +// Get the rotation matrix for an impostor grid index +static inline Matrix4 matrixForIndex(int index) { + if (index == 0) { + return Matrix4::newRotateZ(M_PI_2); + } else if (index < 6) { + return Matrix4::newRotateY(M_2PI * (double)(index - 1) * 0.2).mult(Matrix4::newRotateZ(M_PI_4)); + } else { + return Matrix4::newRotateY(M_2PI * (double)(index - 6) * 0.1); + } +} - setVertex(0, 0.0f, 0.0f, 0.0f); - setVertex(1, 0.0f, 0.0f, 1.0f); - setVertex(2, 0.0f, 1.0f, 0.0f); - setVertex(3, 0.0f, 1.0f, 1.0f); +OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) { + int parts = 4; + + vertices = new float[4 * parts * parts * 3]; + uv = new float[4 * 2]; + texture_size = partsize * parts; + texture = new Texture2D(texture_size, texture_size); + texture_changed = false; + + setVertex(0, 0.0f, 0.0f); + setVertex(1, 0.0f, 1.0f); + setVertex(2, 1.0f, 0.0f); + setVertex(3, 1.0f, 1.0f); } OpenGLVegetationImpostor::~OpenGLVegetationImpostor() { @@ -34,15 +49,17 @@ OpenGLVegetationImpostor::~OpenGLVegetationImpostor() { } void OpenGLVegetationImpostor::render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, - int index) { - if (index == 0 or texture_changed) { + int instance_index, const Vector3 &camera_location) { + if (instance_index == 0 or texture_changed) { texture_changed = false; program->getState()->set("impostorTexture", texture); } - program->getState()->setInt("index", 15); // TODO + + int index = getIndex(camera_location, instance->getBase()); + program->getState()->setInt("index", index); program->getState()->set("offset", instance->getBase()); - program->getState()->set("size", instance->getSize()); - program->drawTriangleStrip(vertices, 4); + program->getState()->set("size", 2.0 * instance->getSize()); + program->drawTriangleStripUV(vertices + index * 4 * 3, uv, 4); } void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, @@ -58,19 +75,12 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m int parts = 4; int partsize = texture_size / parts; - Matrix4 rotation; for (int py = 0; py < parts; py++) { for (int px = 0; px < parts; px++) { int index = py * parts + px; - if (index == 0) { - rotation = Matrix4::newRotateX(-M_PI_2); - } else if (index < 6) { - rotation = Matrix4::newRotateY(M_2PI * (double)(index - 1) * 0.2).mult(Matrix4::newRotateX(-M_PI_4)); - } else { - rotation = Matrix4::newRotateY(M_2PI * (double)(index - 6) * 0.1); - } + Matrix4 rotation = matrixForIndex(index); - Vector3 cam(0.0, 0.0, 5.0); + Vector3 cam(5.0, 0.0, 0.0); scenery.getCamera()->setLocation(rotation.multPoint(cam)); scenery.getCamera()->setTarget(VECTOR_ZERO); renderer.prepare(); @@ -83,10 +93,10 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m for (int y = 0; y < partsize; y++) { double dy = (double)y / (double)partsize; - Vector3 near(dx - 0.5, dy - 0.5, 5.0); - Vector3 far(dx - 0.5, dy - 0.5, -5.0); - SpaceSegment segment(rotation.multPoint(near.scale(1.3)).add(VECTOR_UP.scale(0.5)), - rotation.multPoint(far.scale(1.3)).add(VECTOR_UP.scale(0.5))); + Vector3 near(5.0, dy - 0.5, -(dx - 0.5)); + Vector3 far(-5.0, dy - 0.5, -(dx - 0.5)); + SpaceSegment segment(rotation.multPoint(near.scale(2.0)).add(VECTOR_UP.scale(0.5)), + rotation.multPoint(far.scale(2.0)).add(VECTOR_UP.scale(0.5))); RayCastingResult result = vegetation->renderInstance(segment, instance, false, true); texture->setPixel(startx + x, starty + y, @@ -99,8 +109,40 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m texture_changed = true; } -void OpenGLVegetationImpostor::setVertex(int i, float x, float y, float z) { - vertices[i * 3] = x; - vertices[i * 3 + 1] = y; - vertices[i * 3 + 2] = z; +int OpenGLVegetationImpostor::getIndex(const Vector3 &camera, const Vector3 &instance) const { + int result; + + VectorSpherical diff = camera.sub(instance).toSpherical(); + if (diff.theta > 1.0) { + return 0; + } else { + double angle = diff.phi / M_2PI; + if (diff.theta > 0.4) { + angle = (angle >= 0.9) ? 0.0 : (angle + 0.1); + return 1 + (int)(5.0 * angle); + } else { + angle = (angle >= 0.95) ? 0.0 : (angle + 0.05); + return 6 + (int)(10.0 * angle); + } + } + + assert(result >= 0 and result <= 16); + return result; +} + +void OpenGLVegetationImpostor::setVertex(int i, float u, float v) { + int parts = 4; + for (int py = 0; py < parts; py++) { + for (int px = 0; px < parts; px++) { + int index = py * parts + px; + Matrix4 rotation = matrixForIndex(index); + + Vector3 vertex = rotation.multPoint(Vector3(1.0, u, -(v - 0.5))); + vertices[index * 4 * 3 + i * 3] = vertex.x; + vertices[index * 4 * 3 + i * 3 + 1] = vertex.y; + vertices[index * 4 * 3 + i * 3 + 2] = vertex.z; + } + } + uv[i * 2] = u; + uv[i * 2 + 1] = v; } diff --git a/src/render/opengl/OpenGLVegetationImpostor.h b/src/render/opengl/OpenGLVegetationImpostor.h index f37136d..4a9ff7b 100644 --- a/src/render/opengl/OpenGLVegetationImpostor.h +++ b/src/render/opengl/OpenGLVegetationImpostor.h @@ -21,18 +21,25 @@ class OPENGLSHARED_EXPORT OpenGLVegetationImpostor { /** * Render a single instance using this impostor. */ - void render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, int index); + void render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, int instance_index, + const Vector3 &camera_location); /** * Prepare the texture grid for a given model. */ void prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, bool *interrupt); + /** + * Get the impostor grid index for an instance, to face the camera. + */ + int getIndex(const Vector3 &camera, const Vector3 &instance) const; + private: - void setVertex(int i, float x, float y, float z); + void setVertex(int i, float u, float v); private: float *vertices; + float *uv; int texture_size; bool texture_changed; Texture2D *texture; diff --git a/src/render/opengl/OpenGLVegetationInstance.cpp b/src/render/opengl/OpenGLVegetationInstance.cpp index 4c72296..e32bf40 100644 --- a/src/render/opengl/OpenGLVegetationInstance.cpp +++ b/src/render/opengl/OpenGLVegetationInstance.cpp @@ -5,7 +5,6 @@ OpenGLVegetationInstance::OpenGLVegetationInstance(const VegetationInstance &wrapped) : wrapped(wrapped) { } -void OpenGLVegetationInstance::setDistance(double distance) -{ +void OpenGLVegetationInstance::setDistance(double distance) { this->distance = distance; } diff --git a/src/render/opengl/OpenGLVegetationInstance.h b/src/render/opengl/OpenGLVegetationInstance.h index a4262ac..35a265f 100644 --- a/src/render/opengl/OpenGLVegetationInstance.h +++ b/src/render/opengl/OpenGLVegetationInstance.h @@ -33,7 +33,7 @@ class OPENGLSHARED_EXPORT OpenGLVegetationInstance { */ void setDistance(double distance); -private: + private: VegetationInstance wrapped; double distance; }; diff --git a/src/render/opengl/OpenGLVegetationLayer.cpp b/src/render/opengl/OpenGLVegetationLayer.cpp index a0d9c43..c298a77 100644 --- a/src/render/opengl/OpenGLVegetationLayer.cpp +++ b/src/render/opengl/OpenGLVegetationLayer.cpp @@ -93,7 +93,7 @@ void OpenGLVegetationLayer::threadedUpdate() { zmax = newzmax; removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances); instances.insert(instances.end(), new_instances.begin(), new_instances.end()); - for (auto instance: instances) { + for (auto instance : instances) { instance->setDistance(instance->getBase().sub(*camera_location).getNorm()); } std::sort(instances.begin(), instances.end(), compareInstances); @@ -111,7 +111,7 @@ void OpenGLVegetationLayer::render() { // TODO Instanced rendering int index = 0; for (auto instance : instances) { - impostor->render(parent->getProgram(), instance, index++); + impostor->render(parent->getProgram(), instance, index++, *camera_location); } lock_instances->release(); diff --git a/src/render/opengl/shaders/vegetation.vert b/src/render/opengl/shaders/vegetation.vert index a7915d3..0f872d4 100644 --- a/src/render/opengl/shaders/vegetation.vert +++ b/src/render/opengl/shaders/vegetation.vert @@ -1,4 +1,5 @@ -attribute highp vec4 vertex; +attribute highp vec3 vertex; +attribute highp vec2 uv; uniform highp mat4 viewMatrix; uniform highp vec3 offset; uniform float size; @@ -9,8 +10,7 @@ uniform float waterOffset; void main(void) { - vec3 final = offset + size * (vertex.xyz - vec3(0.0, 0.0, 0.5)); // + vec3(0, waterOffset, 0) - unprojected = final.xyz; - texcoord = vec2(0.25 * (vertex.z + float(mod(index, 4))), 0.25 * (vertex.y + float(index / 4))); - gl_Position = viewMatrix * vec4(final.xyz, 1.0); + unprojected = offset + size * vertex; // + vec3(0, waterOffset, 0) + texcoord = vec2(0.25 * (uv.s + float(mod(index, 4))), 0.25 * (uv.t + float(index / 4))); + gl_Position = viewMatrix * vec4(unprojected, 1.0); } diff --git a/src/tests/OpenGLVegetationImpostor_Test.cpp b/src/tests/OpenGLVegetationImpostor_Test.cpp new file mode 100644 index 0000000..2f0b8a2 --- /dev/null +++ b/src/tests/OpenGLVegetationImpostor_Test.cpp @@ -0,0 +1,21 @@ +#include "BaseTestCase.h" +#include "OpenGLVegetationImpostor.h" + +#include "Vector3.h" + +TEST(OpenGLVegetationImpostor, getIndex) { + OpenGLVegetationImpostor impostor; + + EXPECT_EQ(0, impostor.getIndex(Vector3(0.0, 1.0, 0.0), VECTOR_ZERO)); + + EXPECT_EQ(1, impostor.getIndex(Vector3(1.0, 1.0, 0.0), VECTOR_ZERO)); + EXPECT_EQ(1, impostor.getIndex(Vector3(1.0, 1.0, 0.1), VECTOR_ZERO)); + EXPECT_EQ(1, impostor.getIndex(Vector3(1.0, 1.0, -0.1), VECTOR_ZERO)); + + EXPECT_EQ(2, impostor.getIndex(Vector3(1.0, 1.0, -1.0), VECTOR_ZERO)); + EXPECT_EQ(5, impostor.getIndex(Vector3(1.0, 1.0, 1.0), VECTOR_ZERO)); + + EXPECT_EQ(6, impostor.getIndex(Vector3(1.0, 0.0, 0.0), VECTOR_ZERO)); + EXPECT_EQ(6, impostor.getIndex(Vector3(1.0, 0.0, 0.1), VECTOR_ZERO)); + EXPECT_EQ(6, impostor.getIndex(Vector3(1.0, 0.0, -0.1), VECTOR_ZERO)); +} From 1d9d24b95862c94158e8938beaea10ce54183663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 13 Dec 2015 17:47:22 +0100 Subject: [PATCH 17/17] vegetation: Fixed segfault in unit test --- src/definition/VegetationDefinition.cpp | 4 +- src/render/opengl/OpenGLVegetationLayer.cpp | 84 +++++++++++---------- src/render/opengl/OpenGLVegetationLayer.h | 14 ++++ src/tests/OpenGLVegetationLayer_Test.cpp | 14 ++-- 4 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/definition/VegetationDefinition.cpp b/src/definition/VegetationDefinition.cpp index 027e42f..2ed1d6f 100644 --- a/src/definition/VegetationDefinition.cpp +++ b/src/definition/VegetationDefinition.cpp @@ -29,9 +29,9 @@ void VegetationDefinition::applyPreset(VegetationPreset preset, RandomGenerator clear(); - if (preset == VEGETATION_PRESET_TEMPERATE) { + /*if (preset == VEGETATION_PRESET_TEMPERATE) { layer.applyPreset(VegetationLayerDefinition::VEGETATION_BASIC_TREES, random); layer.setName("Basic tree"); addLayer(layer); - } + }*/ } diff --git a/src/render/opengl/OpenGLVegetationLayer.cpp b/src/render/opengl/OpenGLVegetationLayer.cpp index 9974fec..388d5b4 100644 --- a/src/render/opengl/OpenGLVegetationLayer.cpp +++ b/src/render/opengl/OpenGLVegetationLayer.cpp @@ -59,49 +59,55 @@ void OpenGLVegetationLayer::removeInstancesOutsideArea(double xmin, double xmax, instances->erase(remove_if(instances->begin(), instances->end(), isNull), instances->end()); } +void OpenGLVegetationLayer::updateInstances() { + // Compute new area around camera + double newxmin, newxmax, newzmin, newzmax; + newxmin = camera_location->x - range; + newxmax = camera_location->x + range; + newzmin = camera_location->z - range; + newzmax = camera_location->z + range; + + // Prepare instances where area grew + vector new_instances; + if (newxmin < xmin) { + produceInstancesInArea(newxmin, xmin, newzmin, newzmax, &new_instances); + } + if (newxmax > xmax) { + produceInstancesInArea(xmax, newxmax, newzmin, newzmax, &new_instances); + } + if (newzmin < zmin) { + produceInstancesInArea(xmin, xmax, newzmin, zmin, &new_instances); + } + if (newzmax > zmax) { + produceInstancesInArea(xmin, xmax, zmax, newzmax, &new_instances); + } + + // Apply the changes + lock_instances->acquire(); + xmin = newxmin; + xmax = newxmax; + zmin = newzmin; + zmax = newzmax; + removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances); + instances.insert(instances.end(), new_instances.begin(), new_instances.end()); + for (auto instance : instances) { + instance->setDistance(instance->getBase().sub(*camera_location).getNorm()); + } + sort(instances.begin(), instances.end(), compareInstances); + lock_instances->release(); +} + +void OpenGLVegetationLayer::updateImpostor() { + bool interrupted = false; + impostor->prepareTexture(*definition->getModel(), *parent->getScenery(), &interrupted); +} + void OpenGLVegetationLayer::threadedUpdate() { if (camera_changed) { camera_changed = false; - // Compute new area around camera - double newxmin, newxmax, newzmin, newzmax; - newxmin = camera_location->x - range; - newxmax = camera_location->x + range; - newzmin = camera_location->z - range; - newzmax = camera_location->z + range; - - // Prepare instances where area grew - vector new_instances; - if (newxmin < xmin) { - produceInstancesInArea(newxmin, xmin, newzmin, newzmax, &new_instances); - } - if (newxmax > xmax) { - produceInstancesInArea(xmax, newxmax, newzmin, newzmax, &new_instances); - } - if (newzmin < zmin) { - produceInstancesInArea(xmin, xmax, newzmin, zmin, &new_instances); - } - if (newzmax > zmax) { - produceInstancesInArea(xmin, xmax, zmax, newzmax, &new_instances); - } - - // Apply the changes - lock_instances->acquire(); - xmin = newxmin; - xmax = newxmax; - zmin = newzmin; - zmax = newzmax; - removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances); - instances.insert(instances.end(), new_instances.begin(), new_instances.end()); - for (auto instance : instances) { - instance->setDistance(instance->getBase().sub(*camera_location).getNorm()); - } - sort(instances.begin(), instances.end(), compareInstances); - lock_instances->release(); - - // Update impostor texture - bool interrupted = false; - impostor->prepareTexture(*definition->getModel(), *parent->getScenery(), &interrupted); + updateInstances(); + updateImpostor(); } } diff --git a/src/render/opengl/OpenGLVegetationLayer.h b/src/render/opengl/OpenGLVegetationLayer.h index fb16eef..76f70d2 100644 --- a/src/render/opengl/OpenGLVegetationLayer.h +++ b/src/render/opengl/OpenGLVegetationLayer.h @@ -34,6 +34,20 @@ class OPENGLSHARED_EXPORT OpenGLVegetationLayer { virtual void removeInstancesOutsideArea(double xmin, double xmax, double zmin, double zmax, vector *instances) const; + /** + * Update the instances list. + * + * This should be called when the camera has moved enough to make a change. + */ + void updateInstances(); + + /** + * Update the impostor textures. + * + * This should be called when the camera has moved enough to make a change. + */ + void updateImpostor(); + /** * Perform maintenance tasks that can be perform in a thread. * diff --git a/src/tests/OpenGLVegetationLayer_Test.cpp b/src/tests/OpenGLVegetationLayer_Test.cpp index 8f71a79..8bf161e 100644 --- a/src/tests/OpenGLVegetationLayer_Test.cpp +++ b/src/tests/OpenGLVegetationLayer_Test.cpp @@ -28,7 +28,7 @@ class FakeLayerRenderer : public OpenGLVegetationLayer { vector static_instances; }; -TEST(OpenGLVegetationLayer, threadedUpdate) { +TEST(OpenGLVegetationLayer, updateInstances) { CameraDefinition camera; VegetationLayerDefinition definition(NULL, "test"); FakeLayerRenderer rendering(&definition); @@ -36,32 +36,32 @@ TEST(OpenGLVegetationLayer, threadedUpdate) { EXPECT_EQ(0, rendering.getInstanceCount()); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(0, rendering.getInstanceCount()); rendering.static_instances.push_back( new OpenGLVegetationInstance(VegetationInstance(model, Vector3(0.0, 0.0, 0.0)))); rendering.reset(); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(1, rendering.getInstanceCount()); camera.setLocation(Vector3(-5.0, 0.0, 0.0)); rendering.setCamera(&camera); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(1, rendering.getInstanceCount()); camera.setLocation(Vector3(-11.0, 0.0, 0.0)); rendering.setCamera(&camera); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(0, rendering.getInstanceCount()); camera.setLocation(Vector3(0.0, 0.0, 5.0)); rendering.setCamera(&camera); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(1, rendering.getInstanceCount()); camera.setLocation(Vector3(0.0, 0.0, 15.0)); rendering.setCamera(&camera); - rendering.threadedUpdate(); + rendering.updateInstances(); EXPECT_EQ(0, rendering.getInstanceCount()); }