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;
}
}
}
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;
}
}
}
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With