OpenGL in Qt. Realization 3d or 2d model class. Help.



  • Hi everyone! Recently i've started work with Qt OpenGL.
    After i've created my first triangle, rectangle, cube e.t.c.,
    i decided to create separate class, which will be contain instances QGLBuffer: VBO, IBO, etc. Also this class should contain array vertex and index.
    But i stumbled on the problem. My class does not work.

    My Class:
    @
    class Obj2d
    {
    public:
    Obj2d(float x, float y,float vrt, unsigned int svrt, unsigned int ind, unsigned int sind);
    private:
    QGLBuffer vertexBuffer;
    QGLBuffer indexBuffer;
    bool prepareBufferObject(QGLBuffer
    buffer,
    QGLBuffer::UsagePattern usagePattern,
    const void
    data,
    int dataSize);
    unsigned int *indeces;
    unsigned int svrt; //Size vertices
    unsigned int sind;//indeces
    float *verteces;
    void bufferinit();
    }

    Obj2d::bufferinit()
    {
    if(prepareBufferObject(&vertexBuffer, QGLBuffer::StaticDraw, verteces, svrt))
    return;
    if(prepareBufferObject(&indexBuffer, QGLBuffer::StaticDraw, indeces, sind))
    return;
    vertexBuffer.bind();
    indexBuffer.bind();
    }

    bool Obj2d::prepareBufferObject( QGLBuffer* buffer,
    QGLBuffer::UsagePattern usagePattern,
    const void* data,
    int dataSize )
    {
    buffer->create();
    buffer->setUsagePattern( usagePattern );
    if ( !buffer->bind() )
    {
    qDebug() << "Could not bind buffer object to the context";
    return false;
    }
    buffer->allocate( data, dataSize );
    return true;
    }@

    @
    //In InitializeGL()
    float points[] = {-300.5f, -300.5f, 0.0f, 1.0f,
    -300.5f, 300.5f, 0.0f, 1.0f,
    300.5f, 300.5f, 0.0f, 1.0f,
    300.5f, -300.5f, 0.0f, 1.0f,

                        1.0f,  0.0f, 0.0f, 1.0f,
                        0.0f,  1.0f, 0.0f, 1.0f,
                        0.0f,  0.0f, 1.0f, 1.0f,
                        0.0f,  1.0f, 1.0f, 1.0f};
    unsigned int indeces[] = {0 , 1, 2, 2, 3 ,0};
    quad = new HObj2d(0.0f,0.0f, points, 8*4*sizeof(float), indeces, 3*2*sizeof(unsigned int));
    quad->bufferinit();
    

    @

    @
    //and PaintGL()
    glDrawRangeElements(GL_TRIANGLES, 0,4,6, GL_UNSIGNED_INT, 0);//Program crashed
    @

    Does anybody know, where is mistake in code?
    How can i bind QGLBuffer in Obj2d with current QGLContext using Obj2d method?
    I'll appreciate if you demostrate me your 3d or 2d model class realization.



  • In your constructor make sure that you pass in QGLBuffer::IndexBuffer in the initializer list for the index buffer.



  • [quote author="ZapB" date="1349546958"]In your constructor make sure that you pass in QGLBuffer::IndexBuffer in the initializer list for the index buffer.[/quote]
    Yes i did this in the initializer list.
    @HObj2d::HObj2d(float x, float y, float *vrt, unsigned int svrt,unsigned int *ind, unsigned int sind):
    vertexBuffer(QGLBuffer::VertexBuffer),
    indexBuffer(QGLBuffer::IndexBuffer)@
    Same result.



  • OK. The other thing that typically causes crashes in glDraw* functions is reading beyond the end of the index or vertex buffers. I can't tell from your code snippet but make sure that you really have the vertex and index buffers bound that you think you have.



  • [quote author="ZapB" date="1349594930"]OK. The other thing that typically causes crashes in glDraw* functions is reading beyond the end of the index or vertex buffers. I can't tell from your code snippet but make sure that you really have the vertex and index buffers bound that you think you have.[/quote]
    Thanks for reply.
    Ok. Could you give example of code which demonstrate realization separate class for 3d or 2d model, please?
    Also, i noticed that if I call the methods directly:
    @
    //InitializeGL
    quad->prepareBufferObject(&(quad->vertexBuffer),QGLBuffer::StaticDraw,points, 48sizeof(float));
    quad->prepareBufferObject(&(quad->indexBuffer),QGLBuffer::StaticDraw,indeces, 32sizeof(float));

    quad->vertexBuffer.bind();
    quad->indexBuffer.bind();
    @
    Program works fine.



  • Something like this works for me. This will not work out of the box though as it uses a couple of classes not in Qt (yet): Material and QOpenGLVertexArrayObject. You should be able to see the idea though :)

    plane.h
    @
    #ifndef PLANE_H
    #define PLANE_H

    #include <QObject>

    #include "material.h"

    #include <QOpenGLBuffer>
    #include <QOpenGLVertexArrayObject>

    class Plane : public QObject
    {
    Q_OBJECT
    Q_PROPERTY( float width READ width WRITE setWidth )
    Q_PROPERTY( float height READ height WRITE setHeight )
    Q_PROPERTY( int xDivisions READ xDivisions WRITE setXDivisions )
    Q_PROPERTY( int yDivisions READ yDivisions WRITE setYDivisions )

    public:
    explicit Plane( QObject* parent = 0 );
    Plane( float width, float height, int xDivisions, int yDivisions, QObject* parent = 0 );

    void setMaterial( const MaterialPtr& material );
    MaterialPtr material() const;
    
    float width() const
    {
        return m_width;
    }
    
    float height() const
    {
        return m_height;
    }
    
    int xDivisions() const
    {
        return m_xDivisions;
    }
    
    int yDivisions() const
    {
        return m_yDivisions;
    }
    
    int indexCount() const { return 6 * m_xDivisions * m_yDivisions; }
    

    public slots:
    void setWidth(float width)
    {
    m_width = width;
    }

    void setHeight(float height)
    {
        m_height = height;
    }
    
    void setXDivisions(int xDivisions)
    {
        m_xDivisions = xDivisions;
    }
    
    void setYDivisions(int yDivisions)
    {
        m_yDivisions = yDivisions;
    }
    
    void create();
    
    void render();
    

    private:
    void generateVertexData( QVector<float>& vertices, QVector<float>& normals,
    QVector<float>& texCoords, QVector<unsigned int>& indices );
    void updateVertexArrayObject();

    float m_width;
    float m_height;
    int m_xDivisions;
    int m_yDivisions;
    
    MaterialPtr m_material;
    
    // QVertexBuffers to hold the data
    QOpenGLBuffer m_positionBuffer;
    QOpenGLBuffer m_normalBuffer;
    QOpenGLBuffer m_textureCoordBuffer;
    QOpenGLBuffer m_indexBuffer;
    
    QOpenGLVertexArrayObject m_vao;
    

    };

    #endif // PLANE_H
    @



  • I had to split the post. Here's the implementation:

    plane.cpp
    @
    #include "plane.h"

    #include <QVector>

    Plane::Plane( QObject* parent )
    : QObject( parent ),
    m_width( 1.0f ),
    m_height( 1.0f ),
    m_xDivisions( 1 ),
    m_yDivisions( 1 ),
    m_positionBuffer( QOpenGLBuffer::VertexBuffer ),
    m_normalBuffer( QOpenGLBuffer::VertexBuffer ),
    m_textureCoordBuffer( QOpenGLBuffer::VertexBuffer ),
    m_indexBuffer( QOpenGLBuffer::IndexBuffer ),
    m_vao()
    {
    }

    Plane::Plane( float width, float height, int xDivisions, int yDivisions, QObject* parent )
    : QObject( parent ),
    m_width( width ),
    m_height( height ),
    m_xDivisions( xDivisions ),
    m_yDivisions( yDivisions ),
    m_positionBuffer( QOpenGLBuffer::VertexBuffer ),
    m_normalBuffer( QOpenGLBuffer::VertexBuffer ),
    m_textureCoordBuffer( QOpenGLBuffer::VertexBuffer ),
    m_indexBuffer( QOpenGLBuffer::IndexBuffer ),
    m_vao()
    {
    }

    void Plane::setMaterial( const MaterialPtr& material )
    {
    if ( material == m_material )
    return;
    m_material = material;
    updateVertexArrayObject();
    }

    MaterialPtr Plane::material() const
    {
    return m_material;
    }

    void Plane::create()
    {
    // Allocate some storage to hold per-vertex data
    QVector<float> v; // Vertices
    QVector<float> n; // Normals
    QVector<float> tex; // Tex coords
    QVector<unsigned int> el; // Element indices

    // Generate the vertex data
    generateVertexData( v, n, tex, el );
    
    // Create and populate the buffer objects
    m_positionBuffer.create();
    m_positionBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_positionBuffer.bind();
    m_positionBuffer.allocate( v.constData(), v.size() * sizeof( float ) );
    
    m_normalBuffer.create();
    m_normalBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_normalBuffer.bind();
    m_normalBuffer.allocate( n.constData(), n.size() * sizeof( float ) );
    
    m_textureCoordBuffer.create();
    m_textureCoordBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_textureCoordBuffer.bind();
    m_textureCoordBuffer.allocate( tex.constData(), tex.size() * sizeof( float ) );
    
    m_indexBuffer.create();
    m_indexBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_indexBuffer.bind();
    m_indexBuffer.allocate( el.constData(), el.size() * sizeof( unsigned int ) );
    
    updateVertexArrayObject();
    

    }

    void Plane::generateVertexData( QVector<float>& vertices, QVector<float>& normals,
    QVector<float>& texCoords, QVector<unsigned int>& indices )
    {
    vertices.resize( 3 * ( m_xDivisions + 1 ) * ( m_yDivisions + 1 ) );
    normals.resize( 3 * ( m_xDivisions + 1 ) * ( m_yDivisions + 1 ) );
    texCoords.resize( 2 * ( m_xDivisions + 1 ) * ( m_yDivisions + 1 ) );
    indices.resize( 6 * m_xDivisions * m_yDivisions );

    float x2 = m_width / 2.0f;
    float y2 = m_height / 2.0f;
    float iFactor = static_cast<float>( m_height ) / static_cast<float>( m_yDivisions );
    float jFactor = static_cast<float>( m_width ) / static_cast<float>( m_xDivisions );
    float texi = 1.0f / m_yDivisions;
    float texj = 1.0f / m_xDivisions;
    
    int vertexIndex = 0;
    int textureCoordIndex = 0;
    for ( int i = 0; i <= m_yDivisions; ++i )
    {
        float y = iFactor * i - y2;
    
        for ( int j = 0; j <= m_xDivisions; ++j )
        {
            float x = jFactor * j - x2;
    
            vertices[vertexIndex] = x;
            vertices[vertexIndex+1] = 0.0f;
            vertices[vertexIndex+2] = y;
    
            normals[vertexIndex] = 0.0f;
            normals[vertexIndex+1] = 1.0f;
            normals[vertexIndex+2] = 0.0f;
    
            vertexIndex += 3;
    
            texCoords[textureCoordIndex] = j * texi;
            texCoords[textureCoordIndex+1] = i * texj;
    
            textureCoordIndex += 2;
        }
    }
    
    int index = 0;
    for ( int i = 0; i < m_yDivisions; ++i )
    {
        unsigned int rowStart = i * ( m_xDivisions + 1 );
        unsigned int nextRowStart = ( i + 1 ) * ( m_xDivisions + 1 );
    
        for ( int j = 0; j < m_xDivisions; ++j )
        {
            indices[index]   = rowStart + j;
            indices[index+1] = nextRowStart + j;
            indices[index+2] = nextRowStart + j + 1;
            indices[index+3] = rowStart + j;
            indices[index+4] = nextRowStart + j + 1;
            indices[index+5] = rowStart + j + 1;
            index += 6;
        }
    }
    

    }

    void Plane::updateVertexArrayObject()
    {
    // Ensure that we have a valid material and geometry
    if ( !m_material || !m_positionBuffer.isCreated() )
    return;

    // Create a vertex array object
    if ( !m_vao.isCreated() )
        m_vao.create();
    m_vao.bind();
    
    QOpenGLShaderProgramPtr shader = m_material->shader();
    shader->bind();
    
    m_positionBuffer.bind();
    shader->enableAttributeArray( "vertexPosition" );
    shader->setAttributeBuffer( "vertexPosition", GL_FLOAT, 0, 3 );
    
    m_normalBuffer.bind();
    shader->enableAttributeArray( "vertexNormal" );
    shader->setAttributeBuffer( "vertexNormal", GL_FLOAT, 0, 3 );
    
    m_textureCoordBuffer.bind();
    shader->enableAttributeArray( "vertexTexCoord" );
    shader->setAttributeBuffer( "vertexTexCoord", GL_FLOAT, 0, 2 );
    
    m_indexBuffer.bind();
    
    // End VAO setup
    m_vao.release();
    

    }

    void Plane::render()
    {
    // Bind the vertex array object to set up our vertex buffers and index buffer
    m_vao.bind();

    // Draw it!
    glDrawElements( GL_TRIANGLES, indexCount(), GL_UNSIGNED_INT, 0 );
    

    }
    @



  • Note that above is written against Qt5 so it uses QOpenGL* classes rather than QGL* classes.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.