Qt Quick 3D - カスタムジオメトリの例

C++ と QML からカスタム頂点データを提供する例を示します。

この例では、Model のQQuick3DGeometrythe geometry property を使用して、ベイク済みのアセットの代わりに、C++ と QML から指定された頂点、法線、テクスチャ座標を持つメッシュをレンダリングします。

さらに、GridGeometry のデモも行います。GridGeometry は組み込みのQQuick3DGeometry 実装で、グリッドの表示に適した線プリミティブを持つメッシュを提供します。

この例では、カスタムジオメトリを提供するコードに焦点を当てますので、まずExampleTriangleGeometry C++ ヘッダーファイルを見てみましょう:

class ExampleTriangleGeometry : public QQuick3DGeometry
{
    Q_OBJECT
    QML_NAMED_ELEMENT(ExampleTriangleGeometry)
    Q_PROPERTY(bool normals READ normals WRITE setNormals NOTIFY normalsChanged)
    Q_PROPERTY(float normalXY READ normalXY WRITE setNormalXY NOTIFY normalXYChanged)
    Q_PROPERTY(bool uv READ uv WRITE setUV NOTIFY uvChanged)
    Q_PROPERTY(float uvAdjust READ uvAdjust WRITE setUVAdjust NOTIFY uvAdjustChanged)

public:
    ExampleTriangleGeometry();

    bool normals() const { return m_hasNormals; }
    void setNormals(bool enable);

    float normalXY() const { return m_normalXY; }
    void setNormalXY(float xy);

    bool uv() const { return m_hasUV; }
    void setUV(bool enable);

    float uvAdjust() const { return m_uvAdjust; }
    void setUVAdjust(float f);

signals:
    void normalsChanged();
    void normalXYChanged();
    void uvChanged();
    void uvAdjustChanged();

private:
    void updateData();

    bool m_hasNormals = false;
    float m_normalXY = 0.0f;
    bool m_hasUV = false;
    float m_uvAdjust = 0.0f;
};

最も重要な点は、ExampleTriangleGeometry クラスがQQuick3DGeometry を継承していることと、QML_NAMED_ELEMENT(ExampleTriangleGeometry) マクロを呼び出し、QML からアクセスできるようにしていることです。また、Q_PROPERTY マクロで定義されたいくつかのプロパティは、QML オブジェクトの中で自動的に公開されます。では、QMLモデルを見てみましょう:

Model {
    id: triangleModel
    visible: false
    scale: Qt.vector3d(100, 100, 100)
    geometry: ExampleTriangleGeometry {
        normals: cbNorm.checked
        normalXY: sliderNorm.value
        uv: cbUV.checked
        uvAdjust: sliderUV.value
    }
    materials: [
        DefaultMaterial {
            Texture {
                id: baseColorMap
                source: "qt_logo_rect.png"
            }
            cullMode: DefaultMaterial.NoCulling
            diffuseMap: cbTexture.checked ? baseColorMap : null
            specularAmount: 0.5
        }
    ]
}

ExampleTriangleGeometry クラスを使用するために、geometry プロパティを指定し、関連するプロパティを指定していることに注意してください。QML 側でカスタムジオメトリを使用するために必要なことはこれだけです。

次に、C++ コードのもうひとつの重要な部分、すなわちupdateData() メソッドを見てみましょう。このメソッドは、ExampleTriangleGeometry クラスが作成されたり、その QML プロパティが更新されたりするたびに、カスタムジオメトリのデータを作成し、アップロードします。

void ExampleTriangleGeometry::updateData()
{
    clear();

    int stride = 3 * sizeof(float);
    if (m_hasNormals)
        stride += 3 * sizeof(float);
    if (m_hasUV)
        stride += 2 * sizeof(float);

    QByteArray vertexData(3 * stride, Qt::Initialization::Uninitialized);
    float *p = reinterpret_cast<float *>(vertexData.data());

    // a triangle, front face = counter-clockwise
    *p++ = -1.0f; *p++ = -1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 0.0f + m_uvAdjust; *p++ = 0.0f + m_uvAdjust;
    }
    *p++ = 1.0f; *p++ = -1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 1.0f - m_uvAdjust; *p++ = 0.0f + m_uvAdjust;
    }
    *p++ = 0.0f; *p++ = 1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 1.0f - m_uvAdjust; *p++ = 1.0f - m_uvAdjust;
    }

    setVertexData(vertexData);
    setStride(stride);
    setBounds(QVector3D(-1.0f, -1.0f, 0.0f), QVector3D(+1.0f, +1.0f, 0.0f));

    setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);

    addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
                 0,
                 QQuick3DGeometry::Attribute::F32Type);

    if (m_hasNormals) {
        addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
                     3 * sizeof(float),
                     QQuick3DGeometry::Attribute::F32Type);
    }

    if (m_hasUV) {
        addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic,
                     m_hasNormals ? 6 * sizeof(float) : 3 * sizeof(float),
                     QQuick3DGeometry::Attribute::F32Type);
    }
}

このメソッドはまずclear() を呼び出し、以前にアップロードされたデータをすべてクリアします。次に、法線と uv 座標の存在を考慮して頂点のストライドを計算します。次に、頂点バッファを保持するバイト配列が作成され、コーナーが(-1, -1, 0)、(1, -1, 0)、(0, 1, 0)の三角形の頂点で満たされる。

その後、頂点データがアップロードされ、setVertexData()setStride() を呼び出してストライドが設定される。ジオメトリの境界は、setBounds を呼び出して設定します。この例では使用しませんが、境界の設定は影が機能するために必要です。次に、setPrimitiveType() を呼び出してプリミティブタイプを設定します。最後に、位置、法線、および uv 座標のアトリビュートを、各アトリビュートに対してaddAttribute() を呼び出すことで、先にアップロードされたバッファのメモリ上に配置する方法を指定します。

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。