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