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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,7 @@ namespace opengl {
class OPENGLSHARED_EXPORT OpenGLSharedState {
public:
OpenGLSharedState();
~OpenGLSharedState();
~OpenGLSharedState();
/*!
* \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;
}
void OpenGLVariable::set(int value)
{
void OpenGLVariable::set(int value) {
assert(type == TYPE_NONE or type == TYPE_INTEGER);
type = TYPE_INTEGER;

View file

@ -27,7 +27,7 @@ class paysages::opengl::VegetationUpdater : public Thread {
while (not interrupted) {
std::vector<OpenGLVegetationLayer *> 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<OpenGLVegetationLayer *> 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<OpenGLVegetationLayer *> layers;
acquireLayers(layers);
for (auto layer: layers) {
for (auto layer : layers) {
layer->setCamera(camera);
}
releaseLayers(layers);

View file

@ -1,5 +1,6 @@
#include "OpenGLVegetationImpostor.h"
#include <cassert>
#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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

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