6

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;
        }
    }
}
Cinder Biscuits
  • 4,880
  • 31
  • 51

2 Answers2

4

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;
        }
    }
}
dragly
  • 1,445
  • 11
  • 14
2

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;
        }
    }
}
Cinder Biscuits
  • 4,880
  • 31
  • 51
  • Downcasting from QPickEvent* to QPickTriangleEvent* results in a nullptr for me. Is there something special you are doing that allows it to work? (Qt 5.9.1) – fat_flying_pigs Oct 12 '17 at 18:07
  • 1
    @fat_flying_pigs Yes, you need to set `QPickingSettings` PickMethod to `TrianglePicking` and then the pick signals will return `QPickTriangleEvent` IN QML: Entity { components: [ RenderSettings { pickingSettings { pickMethod: PickingSettings.TrianglePicking } } ] } https://doc.qt.io/qt-5/qml-qt3d-render-pickingsettings.html – Cinder Biscuits Oct 12 '17 at 18:27
  • 1
    Making note of what resolved my issue: I'm not using QML. I had to set the RenderSetting's activeFrameGraph to the QWindow's activeFrameGraph – fat_flying_pigs Oct 23 '17 at 16:19