了解Qt3D创建的网格 [英] Understanding the mesh created by Qt3D

查看:264
本文介绍了了解Qt3D创建的网格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我这样创建一个Qt3D网格:

I create a Qt3D mesh like this:

Qt3DCore::QEntity *newEntity = new Qt3DCore::QEntity();
Qt3DExtras::QConeMesh *mesh =new Qt3DExtras::QConeMesh();
mesh->setTopRadius(0.2);
mesh->setBottomRadius(1.0);
mesh->setLength(2.0);
for(int i = 0; i < mesh->geometry()->attributes().size(); ++i) {
    mesh->geometry()->attributes().at(i)->buffer()->setSyncData(true); // To have access to data
}
newEntity->addComponent(mesh);

创建的网格看起来像这样:

The created mesh looks like this:

稍后在代码中,我尝试以 STL二进制格式导出上述网格.为此,我提取了实体的 geometry transformation 分量:

Later in the code, I try to export the above mesh in STL binary format. To do so, I extract the geometry and transformation components of the entity:

Qt3DCore::QComponent *compoMesh = nullptr; // place holder for mesh geometry of entity
Qt3DCore::QComponent *compoTran = nullptr; // place holder for mesh transformation of entity
QVector<Qt3DCore::QComponent *> compos = newEntity->components();
for(int i = 0; i < compos.size(); ++i) {
    if (qobject_cast<Qt3DRender::QGeometryRenderer *>(compos.at(i))) {
        compoMesh = compos.at(i); // mesh geometry component
    } else if (qobject_cast<Qt3DCore::QTransform *>(compos.at(i))) {
        compoTran = compos.at(i); // mesh transformation component
    }
}

然后我得到包含顶点位置和法线的缓冲区数据:

Then I get the buffer data containing the vertex positions and normals:

Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>(compoMesh);
Qt3DRender::QGeometry *geometry = mesh->geometry();
QVector<Qt3DRender::QAttribute *> atts = geometry->attributes();

现在,我们专注于顶点 positions 属性和顶点 normals 属性.我们分别获得字节偏移字节跨度,并且还要检查两者是否使用相同的数据缓冲区:

Now, we focus on vertex positions attribute and vertex normals attribute. We get the byte offset and byte stride for each, also we check if both are using the same data buffer:

for(int i = 0; i < atts.size(); ++i) {
        if(atts.at(i)->name() == Qt3DRender::QAttribute::defaultPositionAttributeName()) {
            byteOffsetPos = atts.at(i)->byteOffset();
            byteStridePos = atts.at(i)->byteStride();
            bufferPtrPos = atts.at(i)->buffer();
        } else if(atts.at(i)->name() == Qt3DRender::QAttribute::defaultNormalAttributeName()) {
            byteOffsetNorm = atts.at(i)->byteOffset();
            byteStrideNorm = atts.at(i)->byteStride();
            bufferPtrNorm = atts.at(i)->buffer();
        }
    }
if(bufferPtrPos != bufferPtrNorm) {
    qDebug() << __func__ << "!!! Buffer pointer for position and normal are NOT the same";
    // Throw error here
    }

然后,我使用字节偏移字节跨度提取三角形并将其写入STL文件.但是,导出的STL不好:

Then I use the byte offset and byte stride to extract triangles and write them to STL file. However the exported STL is NOT good:

我使用相同的代码导出自定义网格的STL,效果很好.但是,当我使用相同的代码来导出Qt3D现成的网格(如QConeMesh)时,导出的STL是不可接受的.有人可以给我提示吗?

I use the same code for exporting STL of custom meshes which works fine. However when I use the same code to export the Qt3D ready-made meshes like QConeMesh, the exported STL is NOT acceptable. Can anybody give me a hint.

如@vre所述,我将发布其余代码,以将三角形写入STL文件.这是一个很大的代码,我会尽力保持其简洁明了:

As noted by @vre I'm going to post the rest of the code for writing triangles to STL file. It's a large code, I try my best to keep it clear and concise:

要获取三角形的位置和法线,我遍历属性并获得VertexBuffer缓冲区,该缓冲区存储所有位置和法线:

For getting triangle positions and normals, I loop over attributes and get the VertexBuffer buffer which stores all the positions and normals:

// I loop over attributes to get access to VertexBuffer buffer
for(int i = 0; i < atts.size(); ++i) {
    Qt3DRender::QBuffer *buffer = atts.at(i)->buffer();
    QByteArray data = buffer->data();
    // We focus on VertexBuffer, NOT IndexBuffer!
    if( buffer->type() == Qt3DRender::QBuffer::VertexBuffer ) {
        // Number of triangles is number of vertices divided by 3:
        quint32 trianglesCount = atts.at(i)->count() / 3;

        // For each triangle, extract vertex positions and normals
        for(int j = 0; j < trianglesCount; ++j) {
            // Index for each triangle positions data
            // Each triangle has 3 vertices, hence 3 factor:
            // We already know byte-offset and byte-stride for positions
            int idxPos  = byteOffsetPos  + j * 3 * byteStridePos ;
            // Index for each triangle normals data
            // Each tirangle has 3 normals (right?), hence 3 factor:
            // We already know byte-offset and byte-stride for normals
            int idxNorm = byteOffsetNorm + j * 3 * byteStrideNorm;

            // Get x, y, z positions for 1st vertex
            // I have already checked that attribute base type is float by: `atts.at(i)->vertexBaseType();`
            QByteArray pos0x = data.mid(idxPos + 0 * sizeof(float), sizeof(float));
            QByteArray pos0y = data.mid(idxPos + 1 * sizeof(float), sizeof(float));
            QByteArray pos0z = data.mid(idxPos + 2 * sizeof(float), sizeof(float));

            // Get x, y z for 1st normal
            QByteArray norm0x= data.mid(idxNorm + 0 * sizeof(float), sizeof(float));
            QByteArray norm0y= data.mid(idxNorm + 1 * sizeof(float), sizeof(float));
            QByteArray norm0z= data.mid(idxNorm + 2 * sizeof(float), sizeof(float));

            // Get x, y, z positions for 2nd vertex
            QByteArray pos1x = data.mid(idxPos  + 1 * byteStridePos + 0 * sizeof(float), sizeof(float));
            QByteArray pos1y = data.mid(idxPos  + 1 * byteStridePos + 1 * sizeof(float), sizeof(float));
            QByteArray pos1z = data.mid(idxPos  + 1 * byteStridePos + 2 * sizeof(float), sizeof(float));

            // Get x, y, z for 2nd normal
            QByteArray norm1x= data.mid(idxNorm + 1 * byteStrideNorm + 0 * sizeof(float), sizeof(float));
            QByteArray norm1y= data.mid(idxNorm + 1 * byteStrideNorm + 1 * sizeof(float), sizeof(float));
            QByteArray norm1z= data.mid(idxNorm + 1 * byteStrideNorm + 2 * sizeof(float), sizeof(float));

            // Get x, y, z positions for 3rd vertex
            QByteArray pos2x = data.mid(idxPos  + 2 * byteStridePos + 0 * sizeof(float), sizeof(float));
            QByteArray pos2y = data.mid(idxPos  + 2 * byteStridePos + 1 * sizeof(float), sizeof(float));
            QByteArray pos2z = data.mid(idxPos  + 2 * byteStridePos + 2 * sizeof(float), sizeof(float));

            // Get x, y, z for 3rd normal
            QByteArray norm2x= data.mid(idxNorm + 2 * byteStrideNorm+ 0 * sizeof(float), sizeof(float));
            QByteArray norm2y= data.mid(idxNorm + 2 * byteStrideNorm+ 1 * sizeof(float), sizeof(float));
            QByteArray norm2z= data.mid(idxNorm + 2 * byteStrideNorm+ 2 * sizeof(float), sizeof(float));

            // Convert x, y, z byte arrays into floats
            float floatPos0x;
            if ( pos0x.size() >= sizeof(floatPos0x) ) {
                floatPos0x = *reinterpret_cast<const float *>( pos0x.data() );
            }
            float floatPos0y;
            if ( pos0y.size() >= sizeof(floatPos0y) ) {
                floatPos0y = *reinterpret_cast<const float *>( pos0y.data() );
            }
            float floatPos0z;
            if ( pos0z.size() >= sizeof(floatPos0z) ) {
                floatPos0z = *reinterpret_cast<const float *>( pos0z.data() );
            }

            // Do the rest of byte-array to float conversions:
            // norm0x=>floatNorm0x, norm0y=>floatNorm0y, norm0z=>floatNorm0z
            // pos1x=>floatPos1x, pos1y=>floatPos1y, pos1z=>floatPos1z
            // norm1x=>floatNorm1x, norm1y=>floatNorm1y, norm1z=>floatNorm1z
            // pos2x=>floatPos2x, pos2y=>floatPos2y, pos2z=>floatPos2z
            // norm2x=>floatNorm2x, norm2y=>floatNorm2y, norm2z=>floatNorm2z

            // Compose positions matrix before applying transformations
            // I'm going to use `QMatrix4x4` but I have 3 vertices of 3x1
            // Therefore I have to fill out `QMatrix4x4` with zeros and ones
            // Please see this question and its answer: https://stackoverflow.com/q/51979168/3405291
            QMatrix4x4 floatPos4x4 = QMatrix4x4(
                floatPos0x, floatPos1x, floatPos2x, 0,
                floatPos0y, floatPos1y, floatPos2y, 0,
                floatPos0z, floatPos1z, floatPos2z, 0,
                1         , 1         , 1         , 0
            );

            // Apply transformations to positions:
            // We already have transformations component `compoTran` from previous code:
            Qt3DCore::QTransform *tran = qobject_cast<Qt3DCore::QTransform *>(compoTran);
            QMatrix4x4 newFloatPos4x4 = tran->matrix() * floatPos4x4;

            // Get new positions after applying transformations:
            float newFloatPos0x = newFloatPos4x4(0,0);
            float newFloatPos0y = newFloatPos4x4(1,0);
            float newFloatPos0z = newFloatPos4x4(2,0);

            float newFloatPos1x = newFloatPos4x4(0,1);
            float newFloatPos1y = newFloatPos4x4(1,1);
            float newFloatPos1z = newFloatPos4x4(2,1);

            float newFloatPos2x = newFloatPos4x4(0,2);
            float newFloatPos2y = newFloatPos4x4(1,2);
            float newFloatPos2z = newFloatPos4x4(2,2);

            // Convert all the floats (after applying transformations) back to byte array:
            QByteArray newPos0x( reinterpret_cast<const char *>( &newFloatPos0x ), sizeof( newFloatPos0x ) );
            QByteArray newPos0y( reinterpret_cast<const char *>( &newFloatPos0y ), sizeof( newFloatPos0y ) );
            QByteArray newPos0z( reinterpret_cast<const char *>( &newFloatPos0z ), sizeof( newFloatPos0z ) );

            QByteArray newPos1x( reinterpret_cast<const char *>( &newFloatPos1x ), sizeof( newFloatPos1x ) );
            QByteArray newPos1y( reinterpret_cast<const char *>( &newFloatPos1y ), sizeof( newFloatPos1y ) );
            QByteArray newPos1z( reinterpret_cast<const char *>( &newFloatPos1z ), sizeof( newFloatPos1z ) );

            QByteArray newPos2x( reinterpret_cast<const char *>( &newFloatPos2x ), sizeof( newFloatPos2x ) );
            QByteArray newPos2y( reinterpret_cast<const char *>( &newFloatPos2y ), sizeof( newFloatPos2y ) );
            QByteArray newPos2z( reinterpret_cast<const char *>( &newFloatPos2z ), sizeof( newFloatPos2z ) );

            // Log triangle vertex positions and normals (float numbers)
            // A sample log is posted on this question on StackOverflow
            qDebug() << __func__ << " pos 0: x " << newFloatPos0x << " y " << newFloatPos0y << " z " << newFloatPos0z;
            qDebug() << __func__ << " pos 1: x " << newFloatPos1x << " y " << newFloatPos1y << " z " << newFloatPos1z;
            qDebug() << __func__ << " pos 2: x " << newFloatPos2x << " y " << newFloatPos2y << " z " << newFloatPos2z;

            qDebug() << __func__ << " norm 0: x " << floatNorm0x << " y " << floatNorm0y << " z " << floatNorm0z;
            qDebug() << __func__ << " norm 1: x " << floatNorm1x << " y " << floatNorm1y << " z " << floatNorm1z;
            qDebug() << __func__ << " norm 2: x " << floatNorm2x << " y " << floatNorm2y << " z " << floatNorm2z;

            // Write the triangle to STL file
            // Note that STL file needs a header which is written in another section of code
            // Note that STL file needs total number of triangles which is written in another section of code
            // Note that STL file needs only one normal vector for each triangle, but here we have 3 normals (for 3 vertices), therefore I'm writing only the 1st normal to STL (is it OK?!)
            // `baStl` is a byte-array containing all the STL data
            // `baStl` byte-array is written to a file in another section of the code
            QBuffer tempBuffer(&baStl);
            tempBuffer.open(QIODevice::Append);
            tempBuffer.write( norm0x   ); // vertex 0 Normal vector
            tempBuffer.write( norm0y   );
            tempBuffer.write( norm0z   );
            tempBuffer.write( newPos0x ); // New vertex 0 position
            tempBuffer.write( newPos0y );
            tempBuffer.write( newPos0z );
            tempBuffer.write( newPos1x ); // New vertex 1 position
            tempBuffer.write( newPos1y );
            tempBuffer.write( newPos1z );
            tempBuffer.write( newPos2x ); // New vertex 2 position
            tempBuffer.write( newPos2y );
            tempBuffer.write( newPos2z );
            tempBuffer.write("aa"); // Attribute byte count: UINT16: 2 bytes: content doesn't matter, just write 2 bytes
            tempBuffer.close();
        }
    }
}

上面的代码非常适合自定义网格.我的意思是,当我将STL文件导入到Qt3D应用程序中,然后再次将其导出为STL时,导出的STL 很好.问题是:当创建像QConeMesh这样的Qt3D现成的网格时,导出的STL 被拧紧了,我的意思是总体几何形状还可以,但是三角形却被弄乱了,如上图所示

The above code works perfect for custom meshes. I mean when I import a STL file into my Qt3D application and then export it again as STL, the exported STL is good. The problem is: when creating Qt3D ready-made meshes like QConeMesh, the exported STL is screwed up, I mean the overall geometry is OK, but the triangles are messed up as shown in the above image.

我的代码在尝试导出QConeMesh时记录以下值.可以看出,法线向量具有单位大小,这表明它们实际上是法线:

My code logs following values when trying to export a QConeMesh. As can be seen, normal vectors have unit size which shows they are actually normals:


...
exportStlUtil  pos 0: x  -10.6902  y  -7.55854  z  4.76837e-07
exportStlUtil  pos 1: x  -12.8579  y  -4.31431  z  2.98023e-07
exportStlUtil  pos 2: x  -13.6191  y  -0.487476  z  5.96046e-08
exportStlUtil  norm 0: x  -0.707107  y  0  z  0.707107
exportStlUtil  norm 1: x  -0.92388  y  0  z  0.382683
exportStlUtil  norm 2: x  -1  y  0  z  -8.74228e-08
exportStlUtil  pos 0: x  -12.8579  y  3.33936  z  -1.19209e-07
exportStlUtil  pos 1: x  -10.6902  y  6.58359  z  -3.57628e-07
exportStlUtil  pos 2: x  -7.44594  y  8.75132  z  -4.76837e-07
exportStlUtil  norm 0: x  -0.92388  y  0  z  -0.382683
exportStlUtil  norm 1: x  -0.707107  y  0  z  -0.707107
exportStlUtil  norm 2: x  -0.382683  y  0  z  -0.92388
exportStlUtil  pos 0: x  -3.61911  y  9.51252  z  -4.76837e-07
exportStlUtil  pos 1: x  0.207723  y  8.75132  z  -4.76837e-07
exportStlUtil  pos 2: x  3.45196  y  6.58359  z  -3.57628e-07
exportStlUtil  norm 0: x  1.19249e-08  y  0  z  -1
exportStlUtil  norm 1: x  0.382684  y  0  z  -0.923879
exportStlUtil  norm 2: x  0.707107  y  0  z  -0.707107
exportStlUtil  pos 0: x  5.61968  y  3.33936  z  -1.19209e-07
exportStlUtil  pos 1: x  6.38089  y  -0.487479  z  5.96046e-08
exportStlUtil  pos 2: x  6.38089  y  -0.487477  z  0.133333
exportStlUtil  norm 0: x  0.92388  y  0  z  -0.382683
exportStlUtil  norm 1: x  1  y  0  z  1.74846e-07
exportStlUtil  norm 2: x  1  y  0  z  0
exportStlUtil  pos 0: x  5.61968  y  -4.31431  z  0.133334
exportStlUtil  pos 1: x  3.45195  y  -7.55854  z  0.133334
exportStlUtil  pos 2: x  0.207721  y  -9.72627  z  0.133334
exportStlUtil  norm 0: x  0.92388  y  0  z  0.382683
exportStlUtil  norm 1: x  0.707107  y  0  z  0.707107
exportStlUtil  norm 2: x  0.382683  y  0  z  0.92388
...

推荐答案

将我的评论重新形成答案:

Reformulating my comments as an answer:

Qt3D默认几何主要由至少两个缓冲区组成,其中的vertexBuffer包含顶点,纹理坐标以及法线,而indexBuffer包含形成三角形或triangleStrips的索引.要访问vertexBuffer中的顶点或法线,您首先要从indexBuffer中查找三个连续索引的序列,并在考虑vertexSize,byteStride和byteOffset的情况下计算vertexBuffer中的偏移量.

Qt3D default geometry consists mostly of at least two buffers, the vertexBuffer containing vertices, texture coordinates, as well as normals and the indexBuffer containing the indices that form triangles or triangleStrips. To access a vertex or a normal in the vertexBuffer you first look up a sequence of three consecutive indices from the indexBuffer and calculate the offset in the vertexBuffer taking vertexSize, byteStride and byteOffset into consideration.

要访问布局为[vertexCoords, textureCoords, normalCoords]的vertexBuffer(GeometryRenderer的基本类型为Triangles)中的vertexCoord posx,则vertexBufferIndex的计算应为:

To access the vertexCoord posx in a vertexBuffer of layout [vertexCoords, textureCoords, normalCoords] (the GeometryRenderer is of primitiveType Triangles) the calculation of the vertexBufferIndex would then be:

vertexBufPtr + indexBuffer(i) * byteStride + byteOffsetPos

以及第一个法线坐标

vertexBufPtr + indexBuffer(i) * byteStride + byteOffsetNormal.

byteStride等于8 * sizeof(float),byteOffsetPos等于0,byteOffsetNormal等于5 * sizeof(float).

byteStride equals to 8 * sizeof(float), byteOffsetPos equals to 0, and byteOffsetNormal to 5 * sizeof(float).

这篇关于了解Qt3D创建的网格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆