Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt3D reading raw vertex data from QGeometry

Tags:

c++

opengl

qt5

qt3d

I am developing an application using Qt3D and need to access raw vertex data via C++. I am using the QObjectPicker for raypointing, but since the data is specialized (I have developed an importer that adds an extra attribute to each vertex containing a temperature reading) I cannot use QObjectPicker to read the data from the point conveniently.

The 3D object is being loaded via QMesh so I believe the best way to access the raw data is through QMesh's QGeometry member. Correct me if I'm wrong. QGeometry has a vector of QAttribute that hold the vertex attributes. (Again, correct me if I'm wrong.) From this point, I'm not sure how to read the data from a specific vertex index. My guess is I need to read the data from QAttribute::buffer at a certain position by knowing how big each piece of vertex data is and reading from the offset of that, but how would I do that here?

This is what I've come up with so far:

void ES3DScene::handlePickerClicked(QPickEvent *pick)
{
    QPickTriangleEvent *trianglePick = qobject_cast<QPickTriangleEvent*>(pick);
    // I'd like to get the vertex data from vertex1Index's position.
    qDebug() << "Clicked " << trianglePick->vertex1Index();
    QGeometry *geometry = m_mesh->geometry();
    auto attributes = geometry->attributes();
    for (auto i = 0; i < attributes.count(); ++i)
    {
        if (attributes.at(i)->name() == QAttribute::defaultPositionAttributeName())
        {
            QAttribute *attribute = attributes.at(i);
            qDebug() << "Attrib " << attribute;

            //This is where I'm stuck. I need to read the vertex attributes for the
            //vertex at trianglePick->vertex1Index();

            break;
        }
    }
}
like image 860
Cinder Biscuits Avatar asked Oct 10 '17 13:10

Cinder Biscuits


2 Answers

I think you have to access the QBuffer of the attribute you are interested in. This is likely not the attribute with defaultPositionAttributeName(), but a name that you gave it in your importer. Getting the actual data will require you to convert it from the QByteArray to the right data type and fetch the correct position in the data by using the information contained in QAttribute's byteStride and byteOffset. You might also want to use vertexSize and vertexBaseType, depending on how your wrote your importer.

Something along these lines should work (not tested):

void ES3DScene::handlePickerClicked(QPickEvent *pick)
{
    QPickTriangleEvent *trianglePick = qobject_cast<QPickTriangleEvent*>(pick);
    QGeometry *geometry = m_mesh->geometry();
    auto attributes = geometry->attributes();
    int vertexIndex = trianglePick->vertex1Index();
    for (auto i = 0; i < attributes.count(); ++i)
    {
        if (attributes.at(i)->name() == "yourattributename")
        {
            QAttribute *attribute = attributes.at(i);
            QBuffer *buffer = attribute->buffer();
            const QByteArray &data = buffer->data();

            int vertexOffset = vertexIndex * attribute->byteStride();
            int offset = vertexOffset + attribute.byteOffset();

            const char *rawData = &(data.constData()[offset]);

            // replace float with your data type
            float *value = reinterpret_cast<float*>(rawData);

            break;
        }
    }
}
like image 72
dragly Avatar answered Sep 22 '22 05:09

dragly


After digging through the Qt3D sources, I came up with this:

template<typename T>
T extractIndexData(QAttribute *attribute, int index)
{
    const T *typedData = reinterpret_cast<const T*>(attribute->buffer()->data().constData());
    const T indexValue = *(typedData + index);
    return indexValue;
}

template<typename VT, typename IT>
VT extractVertexData(QAttribute *attribute, IT index)
{
    const char *buffer = attribute->buffer()->data().constData();
    const char *vertexData = buffer + (index * attribute->byteStride() + attribute->byteOffset());
    // Construct vertex from from the typed data
    VT vertex;
    const QAttribute::VertexBaseType type = attribute->vertexBaseType();
    switch (type)
    {
    case QAttribute::Float: {
        const float *typedVertexData = reinterpret_cast<const float*>(vertexData);
        const int components = attribute->vertexSize();
        for (int i = 0; i < components; ++i)
            vertex[i] = typedVertexData[i];
        break;
    }
    // TODO: Handle other types as needed.
    default: {
        qWarning() << "Unhandled type";
        Q_UNREACHABLE();
    }
    }
    return vertex;
}

And called like so:

void ES3DScene::handlePickerClicked(QPickEvent *pick)
{
    float *temperature = nullptr;
    QPickTriangleEvent *trianglePick = qobject_cast<QPickTriangleEvent*>(pick);
    auto idx = trianglePick->vertex1Index();

    QGeometry *geometry = m_mesh->geometry();
    for (QAttribute* attribute : geometry->attributes())
    {
        if (attribute->name() == "vertexTemperature")
        {
            temperature = extractVertexData<float*>(attribute, idx);

            break;
        }
    }
}
like image 32
Cinder Biscuits Avatar answered Sep 20 '22 05:09

Cinder Biscuits