Made the vegetation impostors face the camera

This commit is contained in:
Michaël Lemaire 2015-11-29 19:18:36 +01:00
parent 9d7a7a0ff7
commit d2efb599d9
17 changed files with 192 additions and 79 deletions

View file

@ -62,6 +62,11 @@ profile_cli:build
LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS) LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS)
perf report -g 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 package:build
rm -rf paysages3d-linux rm -rf paysages3d-linux
rm -f paysages3d-linux.tar.bz2 rm -f paysages3d-linux.tar.bz2

View file

@ -44,8 +44,7 @@ bool VegetationPresenceDefinition::collectInstances(std::vector<VegetationInstan
double angle = 3.0 * generator->get2DTotal(-x * 20.0, z * 20.0); // TODO balanced distribution double angle = 3.0 * generator->get2DTotal(-x * 20.0, z * 20.0); // TODO balanced distribution
double xo = x + fabs(generator->get2DTotal(x * 12.0, -z * 12.0)); double xo = x + fabs(generator->get2DTotal(x * 12.0, -z * 12.0));
double zo = z + fabs(generator->get2DTotal(-x * 27.0, -z * 27.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); double y = getScenery()->getTerrain()->getInterpolatedHeight(xo, zo, true, true);
result->push_back(VegetationInstance(model, Vector3(xo, y, zo), size, angle)); result->push_back(VegetationInstance(model, Vector3(xo, y, zo), size, angle));
added++; added++;

View file

@ -296,16 +296,21 @@ static void testVegetationModels() {
} }
static void testOpenGLVegetationImpostor() { static void testOpenGLVegetationImpostor() {
std::string filename = getFileName("opengl_vegetation_impostor"); for (int i = 0; i < 4; i++) {
std::cout << "Rendering " << filename << "..." << std::endl; std::string filename = getFileName("opengl_vegetation_impostor", i);
std::cout << "Rendering " << filename << "..." << std::endl;
Scenery scenery; Scenery scenery;
scenery.autoPreset(1); scenery.autoPreset(i);
OpenGLVegetationImpostor impostor(200);
VegetationModelDefinition model(NULL); OpenGLVegetationImpostor impostor(128);
bool interrupted = false; VegetationModelDefinition model(NULL);
impostor.prepareTexture(model, scenery, &interrupted);
impostor.getTexture()->saveToFile(filename); bool interrupted = false;
impostor.prepareTexture(model, scenery, &interrupted);
impostor.getTexture()->saveToFile(filename);
}
} }
void runTestSuite() { void runTestSuite() {

View file

@ -41,12 +41,10 @@ void OpenGLPart::updateScenery(bool onlyCommon) {
} }
} }
Scenery *OpenGLPart::getScenery() const Scenery *OpenGLPart::getScenery() const {
{
return renderer->getScenery(); return renderer->getScenery();
} }
OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const OpenGLFunctions *OpenGLPart::getOpenGlFunctions() const {
{
return renderer->getOpenGlFunctions(); return renderer->getOpenGlFunctions();
} }

View file

@ -75,13 +75,13 @@ void OpenGLShaderProgram::release() {
void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) { void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) {
bind(); bind();
GLuint vertex = program->attributeLocation("vertex"); GLuint array_vertex = program->attributeLocation("vertex");
program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(vertex); program->enableAttributeArray(array_vertex);
functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3); functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
program->disableAttributeArray(vertex); program->disableAttributeArray(array_vertex);
release(); release();
} }
@ -89,13 +89,51 @@ void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) {
void OpenGLShaderProgram::drawTriangleStrip(float *vertices, int vertex_count) { void OpenGLShaderProgram::drawTriangleStrip(float *vertices, int vertex_count) {
bind(); bind();
GLuint vertex = program->attributeLocation("vertex"); GLuint array_vertex = program->attributeLocation("vertex");
program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(vertex); program->enableAttributeArray(array_vertex);
functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); 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(); release();
} }

View file

@ -21,6 +21,9 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram {
void drawTriangles(float *vertices, int triangle_count); void drawTriangles(float *vertices, int triangle_count);
void drawTriangleStrip(float *vertices, int vertex_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 bind();
void release(); void release();

View file

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

View file

@ -15,7 +15,7 @@ namespace opengl {
class OPENGLSHARED_EXPORT OpenGLSharedState { class OPENGLSHARED_EXPORT OpenGLSharedState {
public: public:
OpenGLSharedState(); OpenGLSharedState();
~OpenGLSharedState(); ~OpenGLSharedState();
/*! /*!
* \brief Apply the stored variables to the bound program. * \brief Apply the stored variables to the bound program.

View file

@ -91,8 +91,7 @@ void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) {
texture_color = color; texture_color = color;
} }
void OpenGLVariable::set(int value) void OpenGLVariable::set(int value) {
{
assert(type == TYPE_NONE or type == TYPE_INTEGER); assert(type == TYPE_NONE or type == TYPE_INTEGER);
type = TYPE_INTEGER; type = TYPE_INTEGER;

View file

@ -27,7 +27,7 @@ class paysages::opengl::VegetationUpdater : public Thread {
while (not interrupted) { while (not interrupted) {
std::vector<OpenGLVegetationLayer *> layers; std::vector<OpenGLVegetationLayer *> layers;
vegetation->acquireLayers(layers); vegetation->acquireLayers(layers);
for (auto layer: layers) { for (auto layer : layers) {
layer->threadedUpdate(); layer->threadedUpdate();
} }
vegetation->releaseLayers(layers); vegetation->releaseLayers(layers);
@ -50,7 +50,7 @@ OpenGLVegetation::OpenGLVegetation(OpenGLRenderer *renderer) : OpenGLPart(render
} }
OpenGLVegetation::~OpenGLVegetation() { OpenGLVegetation::~OpenGLVegetation() {
for (auto layer: layers) { for (auto layer : layers) {
delete layer; delete layer;
} }
layers.clear(); layers.clear();
@ -81,7 +81,7 @@ void OpenGLVegetation::render() {
if (enabled) { if (enabled) {
std::vector<OpenGLVegetationLayer *> layers; std::vector<OpenGLVegetationLayer *> layers;
acquireLayers(layers); acquireLayers(layers);
for (auto layer: layers) { for (auto layer : layers) {
layer->render(); layer->render();
} }
releaseLayers(layers); 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(); return renderer->getScenery();
} }
void OpenGLVegetation::cameraChanged(const CameraDefinition *camera) void OpenGLVegetation::cameraChanged(const CameraDefinition *camera) {
{
std::vector<OpenGLVegetationLayer *> layers; std::vector<OpenGLVegetationLayer *> layers;
acquireLayers(layers); acquireLayers(layers);
for (auto layer: layers) { for (auto layer : layers) {
layer->setCamera(camera); layer->setCamera(camera);
} }
releaseLayers(layers); releaseLayers(layers);

View file

@ -1,5 +1,6 @@
#include "OpenGLVegetationImpostor.h" #include "OpenGLVegetationImpostor.h"
#include <cassert>
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "OpenGLSharedState.h" #include "OpenGLSharedState.h"
#include "OpenGLVegetationInstance.h" #include "OpenGLVegetationInstance.h"
@ -16,16 +17,30 @@
#include "LightingManager.h" #include "LightingManager.h"
#include "CameraDefinition.h" #include "CameraDefinition.h"
OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) { // Get the rotation matrix for an impostor grid index
vertices = new float[4 * 3]; static inline Matrix4 matrixForIndex(int index) {
texture_size = partsize * 4; if (index == 0) {
texture = new Texture2D(texture_size, texture_size); return Matrix4::newRotateZ(M_PI_2);
texture_changed = true; } 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); OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) {
setVertex(1, 0.0f, 0.0f, 1.0f); int parts = 4;
setVertex(2, 0.0f, 1.0f, 0.0f);
setVertex(3, 0.0f, 1.0f, 1.0f); 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() { OpenGLVegetationImpostor::~OpenGLVegetationImpostor() {
@ -34,15 +49,17 @@ OpenGLVegetationImpostor::~OpenGLVegetationImpostor() {
} }
void OpenGLVegetationImpostor::render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance, void OpenGLVegetationImpostor::render(OpenGLShaderProgram *program, const OpenGLVegetationInstance *instance,
int index) { int instance_index, const Vector3 &camera_location) {
if (index == 0 or texture_changed) { if (instance_index == 0 or texture_changed) {
texture_changed = false; texture_changed = false;
program->getState()->set("impostorTexture", texture); 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("offset", instance->getBase());
program->getState()->set("size", instance->getSize()); program->getState()->set("size", 2.0 * instance->getSize());
program->drawTriangleStrip(vertices, 4); program->drawTriangleStripUV(vertices + index * 4 * 3, uv, 4);
} }
void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment,
@ -58,19 +75,12 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m
int parts = 4; int parts = 4;
int partsize = texture_size / parts; int partsize = texture_size / parts;
Matrix4 rotation;
for (int py = 0; py < parts; py++) { for (int py = 0; py < parts; py++) {
for (int px = 0; px < parts; px++) { for (int px = 0; px < parts; px++) {
int index = py * parts + px; int index = py * parts + px;
if (index == 0) { Matrix4 rotation = matrixForIndex(index);
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); Vector3 cam(5.0, 0.0, 0.0);
scenery.getCamera()->setLocation(rotation.multPoint(cam)); scenery.getCamera()->setLocation(rotation.multPoint(cam));
scenery.getCamera()->setTarget(VECTOR_ZERO); scenery.getCamera()->setTarget(VECTOR_ZERO);
renderer.prepare(); renderer.prepare();
@ -83,10 +93,10 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m
for (int y = 0; y < partsize; y++) { for (int y = 0; y < partsize; y++) {
double dy = (double)y / (double)partsize; double dy = (double)y / (double)partsize;
Vector3 near(dx - 0.5, dy - 0.5, 5.0); Vector3 near(5.0, dy - 0.5, -(dx - 0.5));
Vector3 far(dx - 0.5, dy - 0.5, -5.0); Vector3 far(-5.0, dy - 0.5, -(dx - 0.5));
SpaceSegment segment(rotation.multPoint(near.scale(1.3)).add(VECTOR_UP.scale(0.5)), SpaceSegment segment(rotation.multPoint(near.scale(2.0)).add(VECTOR_UP.scale(0.5)),
rotation.multPoint(far.scale(1.3)).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); RayCastingResult result = vegetation->renderInstance(segment, instance, false, true);
texture->setPixel(startx + x, starty + y, texture->setPixel(startx + x, starty + y,
@ -99,8 +109,40 @@ void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &m
texture_changed = true; texture_changed = true;
} }
void OpenGLVegetationImpostor::setVertex(int i, float x, float y, float z) { int OpenGLVegetationImpostor::getIndex(const Vector3 &camera, const Vector3 &instance) const {
vertices[i * 3] = x; int result;
vertices[i * 3 + 1] = y;
vertices[i * 3 + 2] = z; 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;
} }

View file

@ -21,18 +21,25 @@ class OPENGLSHARED_EXPORT OpenGLVegetationImpostor {
/** /**
* Render a single instance using this impostor. * 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. * Prepare the texture grid for a given model.
*/ */
void prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, bool *interrupt); 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: private:
void setVertex(int i, float x, float y, float z); void setVertex(int i, float u, float v);
private: private:
float *vertices; float *vertices;
float *uv;
int texture_size; int texture_size;
bool texture_changed; bool texture_changed;
Texture2D *texture; Texture2D *texture;

View file

@ -5,7 +5,6 @@
OpenGLVegetationInstance::OpenGLVegetationInstance(const VegetationInstance &wrapped) : wrapped(wrapped) { OpenGLVegetationInstance::OpenGLVegetationInstance(const VegetationInstance &wrapped) : wrapped(wrapped) {
} }
void OpenGLVegetationInstance::setDistance(double distance) void OpenGLVegetationInstance::setDistance(double distance) {
{
this->distance = distance; this->distance = distance;
} }

View file

@ -33,7 +33,7 @@ class OPENGLSHARED_EXPORT OpenGLVegetationInstance {
*/ */
void setDistance(double distance); void setDistance(double distance);
private: private:
VegetationInstance wrapped; VegetationInstance wrapped;
double distance; double distance;
}; };

View file

@ -93,7 +93,7 @@ void OpenGLVegetationLayer::threadedUpdate() {
zmax = newzmax; zmax = newzmax;
removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances); removeInstancesOutsideArea(xmin, xmax, zmin, zmax, &instances);
instances.insert(instances.end(), new_instances.begin(), new_instances.end()); 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()); instance->setDistance(instance->getBase().sub(*camera_location).getNorm());
} }
std::sort(instances.begin(), instances.end(), compareInstances); std::sort(instances.begin(), instances.end(), compareInstances);
@ -111,7 +111,7 @@ void OpenGLVegetationLayer::render() {
// TODO Instanced rendering // TODO Instanced rendering
int index = 0; int index = 0;
for (auto instance : instances) { for (auto instance : instances) {
impostor->render(parent->getProgram(), instance, index++); impostor->render(parent->getProgram(), instance, index++, *camera_location);
} }
lock_instances->release(); lock_instances->release();

View file

@ -1,4 +1,5 @@
attribute highp vec4 vertex; attribute highp vec3 vertex;
attribute highp vec2 uv;
uniform highp mat4 viewMatrix; uniform highp mat4 viewMatrix;
uniform highp vec3 offset; uniform highp vec3 offset;
uniform float size; uniform float size;
@ -9,8 +10,7 @@ uniform float waterOffset;
void main(void) void main(void)
{ {
vec3 final = offset + size * (vertex.xyz - vec3(0.0, 0.0, 0.5)); // + vec3(0, waterOffset, 0) unprojected = offset + size * vertex; // + vec3(0, waterOffset, 0)
unprojected = final.xyz; texcoord = vec2(0.25 * (uv.s + float(mod(index, 4))), 0.25 * (uv.t + float(index / 4)));
texcoord = vec2(0.25 * (vertex.z + float(mod(index, 4))), 0.25 * (vertex.y + float(index / 4))); gl_Position = viewMatrix * vec4(unprojected, 1.0);
gl_Position = viewMatrix * vec4(final.xyz, 1.0);
} }

View file

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