• GammaRay User Manual
  • GammaRay User Manual
  • Qt3D Geometry
  • mycylinder.cpp Example File
  • GammaRay User Manual 2.10.0
  • mycylinder.cpp Example File

    qt3d-geometry/mycylinder.cpp
    /*
      mycylinder.cpp
    
      This file is part of GammaRay, the Qt application inspection and
      manipulation tool.
    
      Copyright (C) 2016-2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
      Author: Volker Krause <volker.krause@kdab.com>
    
      Licensees holding valid commercial KDAB GammaRay licenses may use this file in
      accordance with GammaRay Commercial License Agreement provided with the Software.
    
      Contact info@kdab.com if any conditions of this licensing are not clear to you.
    
      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 2 of the License, or
      (at your option) any later version.
    
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
    
      You should have received a copy of the GNU General Public License
      along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */
    
    #include "mycylinder.h"
    
    #include <Qt3DRender/QAttribute>
    #include <Qt3DRender/QBuffer>
    #include <Qt3DRender/QGeometry>
    
    #include <QVector3D>
    #include <qmath.h>
    
    class MyCylinderGeometry : public Qt3DRender::QGeometry
    {
        Q_OBJECT
    public:
        explicit MyCylinderGeometry(QNode *parent = nullptr);
        ~MyCylinderGeometry();
    
    private:
        void createVertexData();
        void createSidesVertices(float *&verticesPtr);
        void createDiscVertices(float *&verticesPtr, float yPosition);
        void createIndexData();
        void createSidesIndices(quint16 *&indicesPtr);
        void createDiscIndices(quint16 *&indicesPtr, int discCenterIndex, float yPosition);
    
        Qt3DRender::QAttribute *m_positionAttribute;
        Qt3DRender::QAttribute *m_normalAttribute;
        Qt3DRender::QAttribute *m_indexAttribute;
    
        Qt3DRender::QBuffer *m_vertexBuffer;
        Qt3DRender::QBuffer *m_indexBuffer;
    
        int m_rings = 7;
        int m_slices = 16;
        float m_radius = 1.0f;
        float m_length = 4.0f;
    };
    
    MyCylinderGeometry::MyCylinderGeometry(QNode* parent)
        : QGeometry(parent)
    {
        m_positionAttribute = new Qt3DRender::QAttribute(this);
        m_normalAttribute = new Qt3DRender::QAttribute(this);
        m_indexAttribute = new Qt3DRender::QAttribute(this);
    
        m_vertexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, this);
        m_indexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, this);
    
        // vec3 pos, vec3 normal
        const quint32 elementSize = 3 + 3;
        const quint32 stride = elementSize * sizeof(float);
        const int nVerts = (m_slices + 1) * m_rings + 2 * (m_slices + 1) + 2;
        const int faces = (m_slices * 2) * (m_rings - 1) + (m_slices * 2);
    
        m_positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
        m_positionAttribute->setDataType(Qt3DRender::QAttribute::Float);
        m_positionAttribute->setDataSize(3);
        m_positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
        m_positionAttribute->setBuffer(m_vertexBuffer);
        m_positionAttribute->setByteStride(stride);
        m_positionAttribute->setCount(nVerts);
    
        m_normalAttribute->setName(Qt3DRender::QAttribute::defaultNormalAttributeName());
        m_normalAttribute->setDataType(Qt3DRender::QAttribute::Float);
        m_normalAttribute->setDataSize(3);
        m_normalAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
        m_normalAttribute->setBuffer(m_vertexBuffer);
        m_normalAttribute->setByteStride(stride);
        m_normalAttribute->setByteOffset(3 * sizeof(float));
        m_normalAttribute->setCount(nVerts);
    
        m_indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
        m_indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedShort);
        m_indexAttribute->setBuffer(m_indexBuffer);
    
        m_indexAttribute->setCount(faces * 3);
    
        createVertexData();
        createIndexData();
    
        addAttribute(m_positionAttribute);
        addAttribute(m_normalAttribute);
        addAttribute(m_indexAttribute);
    }
    
    MyCylinderGeometry::~MyCylinderGeometry()
    {
    }
    
    void MyCylinderGeometry::createVertexData()
    {
        const int verticesCount  = (m_slices + 1) * m_rings + 2 * (m_slices + 1) + 2;
        // vec3 pos, vec3 normal
        const quint32 vertexSize = (3 + 3) * sizeof(float);
    
        QByteArray verticesData;
        verticesData.resize(vertexSize * verticesCount);
        float *verticesPtr = reinterpret_cast<float*>(verticesData.data());
    
        createSidesVertices(verticesPtr);
        createDiscVertices(verticesPtr, -m_length * 0.5f);
        createDiscVertices(verticesPtr, m_length * 0.5f);
    
        m_vertexBuffer->setData(verticesData);
    }
    
    void MyCylinderGeometry::createSidesVertices(float*& verticesPtr)
    {
        const float dY = m_length / static_cast<float>(m_rings - 1);
        const float dTheta = (M_PI * 2) / static_cast<float>(m_slices);
    
        for (int ring = 0; ring < m_rings; ++ring) {
            const float y = -m_length / 2.0f + static_cast<float>(ring) * dY;
            for (int slice = 0; slice <= m_slices; ++slice) {
                const float theta = static_cast<float>(slice) * dTheta;
                *verticesPtr++ = m_radius * qCos(theta);;
                *verticesPtr++ = y;
                *verticesPtr++ = m_radius * qSin(theta);
    
                QVector3D n(qCos(theta), -y *0.5f, qSin(theta));
                n.normalize();
                *verticesPtr++ = n.x();
                *verticesPtr++ = n.y();
                *verticesPtr++ = n.z();
            }
        }
    }
    
    void MyCylinderGeometry::createDiscVertices(float *&verticesPtr, float yPosition)
    {
        const float dTheta = (M_PI * 2) / static_cast<float>(m_slices);
        const float yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f;
    
        *verticesPtr++ = 0.0f;
        *verticesPtr++ = yPosition;
        *verticesPtr++ = 0.0f;
    
        *verticesPtr++ = 0.0f;
        *verticesPtr++ = yNormal;
        *verticesPtr++ = 0.0f;
    
        for (int slice = 0; slice <= m_slices; ++slice) {
            const float theta = static_cast<float>(slice) * dTheta;
            *verticesPtr++ = m_radius * qCos(theta);
            *verticesPtr++ = yPosition;
            *verticesPtr++ = m_radius * qSin(theta);
    
            *verticesPtr++ = 0.0f;
            *verticesPtr++ = yNormal;
            *verticesPtr++ = 0.0f;
        }
    }
    
    void MyCylinderGeometry::createIndexData()
    {
        const int facesCount = (m_slices * 2) * (m_rings - 1) // two tris per side, for each pair of adjacent rings
                + m_slices * 2; // two caps
        const int indicesCount = facesCount * 3;
        const int indexSize = sizeof(quint16);
        Q_ASSERT(indicesCount < 65536);
    
        QByteArray indicesBytes;
        indicesBytes.resize(indicesCount * indexSize);
        quint16 *indicesPtr = reinterpret_cast<quint16*>(indicesBytes.data());
    
        createSidesIndices(indicesPtr);
        createDiscIndices(indicesPtr, m_rings * (m_slices + 1), -m_length * 0.5);
        createDiscIndices(indicesPtr, m_rings * (m_slices + 1) + m_slices + 2, m_length * 0.5);
    
        m_indexBuffer->setData(indicesBytes);
    }
    
    void MyCylinderGeometry::createSidesIndices(quint16 *&indicesPtr)
    {
        for (auto ring = 0; ring < m_rings - 1; ++ring) {
            const auto ringIndexStart = ring * (m_slices + 1);
            const auto nextRingIndexStart = (ring + 1) * (m_slices + 1);
    
            for (auto slice = 0; slice < m_slices; ++slice) {
                const auto nextSlice = slice + 1;
                *indicesPtr++ = (ringIndexStart + slice);
                *indicesPtr++ = (nextRingIndexStart + slice);
                *indicesPtr++ = (ringIndexStart + nextSlice);
                *indicesPtr++ = (ringIndexStart + nextSlice);
                *indicesPtr++ = (nextRingIndexStart + slice);
                *indicesPtr++ = (nextRingIndexStart + nextSlice);
            }
        }
    }
    
    void MyCylinderGeometry::createDiscIndices(quint16 *&indicesPtr, int discCenterIndex, float /*yPosition*/)
    {
        //const auto yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f;
        for (auto slice = 0; slice < m_slices; ++slice) {
            const auto nextSlice = slice + 1;
            *indicesPtr++ = discCenterIndex;
            *indicesPtr++ = (discCenterIndex + 1 + nextSlice);
            *indicesPtr++ = (discCenterIndex + 1 + slice);
    
            //if (yNormal < 0.0f)
            //    qSwap(*(indicesPtr -1), *(indicesPtr - 2));
        }
    }
    
    MyCylinder::MyCylinder(Qt3DCore::QNode* parent)
        : QGeometryRenderer(parent)
    {
        setGeometry(new MyCylinderGeometry(this));
    }
    
    MyCylinder::~MyCylinder()
    {
    }
    
    #include "mycylinder.moc"