Qt3D 从 QGeometry 读取原始顶点数据

Qt3D reading raw vertex data from QGeometry

我正在使用 Qt3D 开发应用程序,需要通过 C++ 访问原始顶点数据。我正在使用 QObjectPicker 进行光线指向,但是由于数据是专门的(我开发了一个导入器,它为每个包含温度读数的顶点添加了一个额外的属性)我不能使用 QObjectPicker 从中读取数据方便点。

3D 对象正在通过 QMesh 加载,因此我认为访问原始数据的最佳方式是通过 QMeshQGeometry 成员。如果我错了纠正我。 QGeometry 有一个包含顶点属性的 QAttribute 向量。 (同样,如果我错了,请纠正我。)从这一点来看,我不确定如何从特定的顶点索引读取数据。我的猜测是我需要通过知道每个顶点数据有多大并从其偏移量读取数据来在特定位置从 QAttribute::buffer 读取数据,但我将如何在此处执行此操作?

这是我到目前为止的想法:

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;
        }
    }
}

我认为您必须访问您感兴趣的属性的 QBuffer。这可能不是带有 defaultPositionAttributeName() 的属性,而是您在导入程序中为其指定的名称。获取实际数据需要您将其从 QByteArray 转换为正确的数据类型,并使用 QAttributebyteStridebyteOffset 中包含的信息获取数据中的正确位置.您可能还想使用 vertexSizevertexBaseType,具体取决于您编写导入程序的方式。

按照这些思路应该可以工作(未测试):

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;
        }
    }
}

在深入了解 Qt3D 源代码后,我想到了这个:

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;
}

然后这样调用:

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;
        }
    }
}