Added vegetation impostors to OpenGL renderer

This is currently an unoptimized and broken version, to be improved
This commit is contained in:
Michaël Lemaire 2015-11-25 23:15:58 +01:00
parent 9e3c43f510
commit 9d7a7a0ff7
35 changed files with 935 additions and 15 deletions

View file

@ -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);

View file

@ -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;

View file

@ -42,11 +42,14 @@ bool VegetationPresenceDefinition::collectInstances(std::vector<VegetationInstan
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++;
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++;
}
}
}
}

View file

@ -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

View file

@ -23,12 +23,15 @@
#include "VegetationInstance.h"
#include "VegetationRenderer.h"
#include "RayCastingResult.h"
#include "OpenGLVegetationImpostor.h"
#include "Texture2D.h"
#include <sstream>
#include <iostream>
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();
}

View file

@ -40,3 +40,13 @@ void OpenGLPart::updateScenery(bool onlyCommon) {
update();
}
}
Scenery *OpenGLPart::getScenery() const
{
return renderer->getScenery();
}
OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const
{
return renderer->getOpenGlFunctions();
}

View file

@ -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);

View file

@ -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 &) {

View file

@ -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;
};
}
}

View file

@ -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() {

View file

@ -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;
};

View file

@ -3,7 +3,7 @@
OpenGLSharedState::OpenGLSharedState() {
}
paysages::opengl::OpenGLSharedState::~OpenGLSharedState()
OpenGLSharedState::~OpenGLSharedState()
{
for (const auto &pair : variables) {
delete pair.second;

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -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<OpenGLVegetationLayer *> 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<OpenGLVegetationLayer *> 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<OpenGLVegetationLayer *> layers;
acquireLayers(layers);
for (auto layer: layers) {
layer->setCamera(camera);
}
releaseLayers(layers);
}
void OpenGLVegetation::acquireLayers(std::vector<OpenGLVegetationLayer *> &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<OpenGLVegetationLayer *> &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();
}

View file

@ -0,0 +1,89 @@
#ifndef OPENGLVEGETATION_H
#define OPENGLVEGETATION_H
#include "opengl_global.h"
#include "OpenGLPart.h"
#include "DefinitionWatcher.h"
#include <map>
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<OpenGLVegetationLayer *> &layers);
/**
* Release the layers acquired by acquireLayers.
*/
void releaseLayers(const std::vector<OpenGLVegetationLayer *> &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<OpenGLVegetationLayer *> layers;
Mutex *layers_lock;
VegetationUpdater *updater;
};
}
}
#endif // OPENGLVEGETATION_H

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -0,0 +1,141 @@
#include "OpenGLVegetationLayer.h"
#include OPENGL_FUNCTIONS_INCLUDE
#include <algorithm>
#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<OpenGLVegetationInstance *> *instances) const {
std::vector<VegetationInstance> 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<OpenGLVegetationInstance *> *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<OpenGLVegetationInstance *> 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;
}
}

View file

@ -0,0 +1,82 @@
#ifndef OPENGLVEGETATIONLAYER_H
#define OPENGLVEGETATIONLAYER_H
#include "opengl_global.h"
#include <vector>
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<OpenGLVegetationInstance *> *instances) const;
/**
* Remove instances outside of a given area.
*/
virtual void removeInstancesOutsideArea(double xmin, double xmax, double zmin, double zmax,
std::vector<OpenGLVegetationInstance *> *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<OpenGLVegetationInstance *> instances;
Mutex *lock_instances;
OpenGLVegetationImpostor *impostor;
Vector3 *camera_location;
bool camera_changed;
};
}
}
#endif // OPENGLVEGETATIONLAYER_H

View file

@ -55,3 +55,7 @@ RESOURCES += \
OTHER_FILES += \
shaders/*.frag \
shaders/*.vert
DISTFILES += \
shaders/vegetation.frag \
shaders/vegetation.vert

View file

@ -19,6 +19,10 @@ class OpenGLVariable;
class OpenGLSkybox;
class OpenGLWater;
class OpenGLTerrain;
class OpenGLVegetation;
class OpenGLVegetationLayer;
class OpenGLVegetationInstance;
class OpenGLVegetationImpostor;
class ExplorerChunkTerrain;
template <typename Vertex> class VertexArray;
}

View file

@ -11,5 +11,7 @@
<file>fadeout.frag</file>
<file>noise.frag</file>
<file>ui.frag</file>
<file>vegetation.vert</file>
<file>vegetation.frag</file>
</qresource>
</RCC>

View file

@ -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;
}

View file

@ -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);
}

View file

@ -64,6 +64,7 @@ SoftwareRenderer::~SoftwareRenderer() {
delete clouds_renderer;
delete terrain_renderer;
delete textures_renderer;
delete vegetation_renderer;
delete water_renderer;
}

View file

@ -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
}
}

View file

@ -44,6 +44,9 @@ VegetationRenderer::VegetationRenderer(SoftwareRenderer *parent) : parent(parent
enabled = true;
}
VegetationRenderer::~VegetationRenderer() {
}
void VegetationRenderer::setEnabled(bool enabled) {
this->enabled = enabled;
}

View file

@ -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.

View file

@ -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<OpenGLVegetationInstance *> *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<OpenGLVegetationInstance *> 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());
}

View file

@ -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());
}

View file

@ -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());