#ifndef GLTRIANGLES_H #define GLTRIANGLES_H #include #include "GL.h" #include "GLHelper.h" #include #include template class GLTriangles : protected QOpenGLFunctions { private: QOpenGLBuffer arrayBuf; QOpenGLBuffer indexBuf; QOpenGLTexture* textures[4]; std::vector vertices; std::vector indices; public: /** ctor */ GLTriangles() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer), textures() { ; } /** dtor */ ~GLTriangles() { arrayBuf.destroy(); indexBuf.destroy(); for (int i = 0; i < 4; ++i) {delete textures[i];} } /** set the to-be-used texture */ void setTexture(const int slot, const QString& textureFile) { const QImage img(textureFile); if (img.width() <= 0) {throw "error";} QOpenGLTexture* texture = new QOpenGLTexture(img); texture->setMinificationFilter(QOpenGLTexture::Linear); texture->setMagnificationFilter(QOpenGLTexture::Linear); texture->setWrapMode(QOpenGLTexture::Repeat); texture->generateMipMaps(); textures[slot] = texture; } void setDiffuse(const QString& textureFile) { setTexture(0, textureFile); } void setNormalMap(const QString& textureFile) { setTexture(1, textureFile); } /** add a new face to this element */ void addFace(const T& vnt1, const T& vnt2, const T& vnt3) { addFace(vnt1, vnt2, vnt3, 0); } /** add a new face to this element */ void addFaceCCW(const T& vnt1, const T& vnt2, const T& vnt3) { addFace(vnt1, vnt2, vnt3, 1); } /** add a new face to this element */ void addFaceCW(const T& vnt1, const T& vnt2, const T& vnt3) { addFace(vnt1, vnt2, vnt3, 2); } /** add a new quad to this element */ void addQuadCCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { addFace(vnt1, vnt2, vnt3, 1); addFace(vnt3, vnt4, vnt1, 1); } /** add a new quad to this element */ void addQuadCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { addFace(vnt1, vnt2, vnt3, 2); addFace(vnt3, vnt4, vnt1, 2); } /** add a new quad to this element */ void addQuad(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { addFace(vnt1, vnt2, vnt3, 0); addFace(vnt3, vnt4, vnt1, 0); } /** build the underlying buffers */ void build() { initializeOpenGLFunctions(); // 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 rebuild() { if (indexBuf.isCreated()) {indexBuf.destroy();} if (arrayBuf.isCreated()) {arrayBuf.destroy();} build(); } void clear() { indices.clear(); vertices.clear(); } /** render the element */ void render(QOpenGLShaderProgram* program) { // nothing to-do? if (indices.empty()) {return;} // Tell OpenGL which VBOs to use arrayBuf.bind(); indexBuf.bind(); for (int i = 0; i < 4; ++i) { if (textures[i]) { textures[i]->bind(i); } } // 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 if (T::hasNormal()) { int normLoc = program->attributeLocation("a_normal"); program->enableAttributeArray(normLoc); program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0])); } if (T::hasTexCoord()) { int texcoordLocation = program->attributeLocation("a_texcoord"); program->enableAttributeArray(texcoordLocation); program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0])); } // bind tangent data? if (T::hasTangent()) { int tanLocation = program->attributeLocation("a_tangent"); program->enableAttributeArray(tanLocation); program->setAttributeBuffer(tanLocation, GL_FLOAT, vertices[0].getTanOffset(), 3, sizeof(vertices[0])); } // bind color data? if (T::hasColor()) { int colorLoc = program->attributeLocation("a_color"); program->enableAttributeArray(colorLoc); program->setAttributeBuffer(colorLoc, GL_FLOAT, vertices[0].getColorOffset(), 3, sizeof(vertices[0])); } // texture program->setUniformValue("texture", 0); // Draw cube geometry using indices from VBO 1 glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); } private: void addFace(const T& vnt1, const T& vnt2, const T& vnt3, const int mode) { // add vertices (remove duplicates!) const int i1 = addOnce(vnt1); const int i2 = addOnce(vnt2); const int i3 = addOnce(vnt3); // get current orientation const bool ccw = GLHelper::isCCW(vnt1.vert, vnt2.vert, vnt3.vert); // create indices if (mode == 0 || (mode == 1 && ccw) || (mode == 2 && !ccw) ) { indices.push_back(i1); indices.push_back(i2); indices.push_back(i3); } else { indices.push_back(i3); indices.push_back(i2); indices.push_back(i1); } } /** to conserve memory, avoid duplicate VNTs! */ int addOnce(const T& vnt) { const auto it = std::find(vertices.begin(), vertices.end(), vnt); if (it == vertices.end()) { const int idx = vertices.size(); vertices.push_back(vnt); return idx; } else { const int idx = it - vertices.begin(); return idx; } } }; #endif // GLTRIANGLES_H