Qt Quick 3D - Custom Geometry Example▲
This example makes use of QQuick3DGeometry and the geometry property of Model to render a mesh with vertex, normal, and texture coordinates specified from C++ instead of a pre-baked asset.
In addition, the GridGeometry is also demonstrated. GridGeometry is a built-in QQuick3DGeometry implementation that provides a mesh with line primitives suitable for displaying a grid.
The focus on this example will be on the code that provides the custom geometry, so let's first have a look at the ExampleTriangleGeometry C++ header file:
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.0
f;
bool
m_hasUV =
false
;
float
m_uvAdjust =
0.0
f;
}
;
The most important thing to notice is that our ExampleTriangleGeometry class inherits from QQuick3DGeometry and that we call the QML_NAMED_ELEMENT(ExampleTriangleGeometry) macro, making our class accessible in QML. There are also a few properties defined through the Q_PROPERTY macro which are automatically exposed in our QML object. Now, let's look at the QML Model:
Model {
visible
:
radioCustGeom.checked
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
}
]
}
Note that we specify the geometry property to use our ExampleTriangleGeometry class, with the relevant properties specified. This is all that is needed on the QML side to use a custom geometry.
Now, lets look at the other important part of the C++ code, namely the updateData() method. This method creates and uploads the data for our custom geometry whenever a ExampleTriangleGeometry class is created or any of its QML properties are updated.
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
&
lt;float
*&
gt;(vertexData.data());
// a triangle, front face = counter-clockwise
*
p++
=
-
1.0
f; *
p++
=
-
1.0
f; *
p++
=
0.0
f;
if
(m_hasNormals) {
*
p++
=
m_normalXY; *
p++
=
m_normalXY; *
p++
=
1.0
f;
}
if
(m_hasUV) {
*
p++
=
0.0
f +
m_uvAdjust; *
p++
=
0.0
f +
m_uvAdjust;
}
*
p++
=
1.0
f; *
p++
=
-
1.0
f; *
p++
=
0.0
f;
if
(m_hasNormals) {
*
p++
=
m_normalXY; *
p++
=
m_normalXY; *
p++
=
1.0
f;
}
if
(m_hasUV) {
*
p++
=
1.0
f -
m_uvAdjust; *
p++
=
0.0
f +
m_uvAdjust;
}
*
p++
=
0.0
f; *
p++
=
1.0
f; *
p++
=
0.0
f;
if
(m_hasNormals) {
*
p++
=
m_normalXY; *
p++
=
m_normalXY; *
p++
=
1.0
f;
}
if
(m_hasUV) {
*
p++
=
1.0
f -
m_uvAdjust; *
p++
=
1.0
f -
m_uvAdjust;
}
setVertexData(vertexData);
setStride(stride);
setBounds(QVector3D(-
1.0
f, -
1.0
f, 0.0
f), QVector3D(+
1.0
f, +
1.0
f, 0.0
f));
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);
}
}
The method starts by calling clear() to clear all previously uploaded data. It then computes the stride for the vertices, taking into account the pre