#ifndef FLOORRENDERER_H #define FLOORRENDERER_H #include #include #include #include #include "GL.h" class FloorRenderer : protected QOpenGLFunctions { private: Floorplan::Floor* floor; QOpenGLBuffer arrayBuf; QOpenGLBuffer indexBuf; QOpenGLTexture* texture = nullptr; std::vector vertices; std::vector indices; public: /** ctor */ FloorRenderer(Floorplan::Floor* floor) : floor(floor), arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) { ; } /** dctor */ ~FloorRenderer() { arrayBuf.destroy(); indexBuf.destroy(); delete texture; } /** render the floor */ void render(QOpenGLShaderProgram *program) { // Tell OpenGL which VBOs to use arrayBuf.bind(); indexBuf.bind(); texture->bind(); // vertices int vertLoc = program->attributeLocation("a_position"); program->enableAttributeArray(vertLoc); program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0])); // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data int normLoc = program->attributeLocation("a_normal"); program->enableAttributeArray(normLoc); program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0])); int texcoordLocation = program->attributeLocation("a_texcoord"); program->enableAttributeArray(texcoordLocation); program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0])); // texture program->setUniformValue("texture", 0); // Draw cube geometry using indices from VBO 1 glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, 0); } void initGL() { initializeOpenGLFunctions(); build(); } private: void build() { QImage img(":/res/gl/tex/wall1.jpg"); texture = new QOpenGLTexture(img); texture->setMinificationFilter(QOpenGLTexture::Nearest); texture->setMagnificationFilter(QOpenGLTexture::Linear); texture->setWrapMode(QOpenGLTexture::Repeat); // build array of vertices and indices for (Floorplan::FloorObstacle* obstacle : floor->obstacles) { add(floor, obstacle, vertices, indices); } // Transfer vertex data to VBO 0 arrayBuf.create(); arrayBuf.bind(); arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0])); // Transfer index data to VBO 1 indexBuf.create(); indexBuf.bind(); indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0])); } void add(Floorplan::Floor* floor, Floorplan::FloorObstacle* obstacle, std::vector& vertices, std::vector& indices) { if (dynamic_cast(obstacle)) { Floorplan::FloorObstacleLine* line = dynamic_cast(obstacle); if (line->type != Floorplan::ObstacleType::WALL) {return;} // QVector3D p1(line->from.x, floor->getStartingZ(), line->from.y); // QVector3D p2(line->to.x, floor->getStartingZ(), line->to.y); // QVector3D p3(line->to.x, floor->getEndingZ(), line->to.y); // QVector3D p4(line->from.x, floor->getEndingZ(), line->from.y); // addFace(p1,p2,p3,p4, vertices, indices); addFace(line->from, line->to, floor->getStartingZ(), floor->getEndingZ(), vertices, indices); addFace(line->to, line->from, floor->getStartingZ(), floor->getEndingZ(), vertices, indices); } } void addFace(const Point2 from, const Point2 to, const float h1, const float h2, std::vector& vertices, std::vector& indices) { // unit-face with unit normal (facing the camera) QVector3D p1(-0.5, -0.0, 0); QVector3D p2(+0.5, -0.0, 0); QVector3D p3(+0.5, +1.0, 0); QVector3D p4(-0.5, +1.0, 0); QVector3D norm(0, 0, 1); // how to scale the unit-face to match the wall const float h = h2-h1; const float s = from.getDistance(to); QMatrix4x4 scale; scale.scale(s, h, 1); // how to rotate the unit-face to match the wall const float angle = std::atan2(to.y - from.y, to.x - from.x); const float deg = angle * 180 / M_PI; QMatrix4x4 rot; rot.rotate(deg, QVector3D(0,1,0)); // how to translate the unit-face to match the wall const Point2 center = (from + to) / 2; QMatrix4x4 move; move.translate(center.x, h1, center.y); // final matrix QMatrix4x4 mat = move * rot * scale; // texture coordinates (scale only) const QVector2D tex1 = (scale * p1).toVector2D() / 5; const QVector2D tex2 = (scale * p2).toVector2D() / 5; const QVector2D tex3 = (scale * p3).toVector2D() / 5; const QVector2D tex4 = (scale * p4).toVector2D() / 5; // modify vertices p1 = mat * p1; p2 = mat * p2; p3 = mat * p3; p4 = mat * p4; norm = (rot * norm).normalized(); const int start = vertices.size(); vertices.push_back(VertNormTex(p1, norm, tex1)); vertices.push_back(VertNormTex(p2, norm, tex2)); vertices.push_back(VertNormTex(p3, norm, tex3)); vertices.push_back(VertNormTex(p4, norm, tex4)); indices.push_back(start+0); indices.push_back(start+1); indices.push_back(start+2); indices.push_back(start+3); } // void addFace(QVector3D p1, QVector3D p2, QVector3D p3, QVector3D p4, std::vector& vertices, std::vector& indices) { // const float s = 50; // // ensure camera facing (for culling) // if (p1.x() != p2.x()) { // if (p1.x() > p2.x()) {std::swap(p1, p2), std::swap(p3, p4);} // } else { // if (p1.z() > p2.z()) {std::swap(p1, p2), std::swap(p3, p4);} // } // // corresponding normal vector // QVector3D norm = QVector3D::crossProduct((p2-p1), (p3-p1)).normalized(); // // orient towards the viewport // const QVector3D view(-99,-99,-99); // if ((view-norm).length() > (view+norm).length()) {norm = -norm;} // const int start = vertices.size(); // indices.push_back(start+0); // indices.push_back(start+1); // indices.push_back(start+2); // indices.push_back(start+3); // vertices.push_back(VertNorm(p1/s, norm)); // vertices.push_back(VertNorm(p2/s, norm)); // vertices.push_back(VertNorm(p3/s, norm)); // vertices.push_back(VertNorm(p4/s, norm)); // // and the other side (clockwise, negated normal) // indices.push_back(start+0); // indices.push_back(start+3); // indices.push_back(start+2); // indices.push_back(start+1); // vertices.push_back(VertNorm(p1/s, -norm)); // vertices.push_back(VertNorm(p2/s, -norm)); // vertices.push_back(VertNorm(p3/s, -norm)); // vertices.push_back(VertNorm(p4/s, -norm)); // } }; #endif // FLOORRENDERER_H