Qt Quick 3D - Custom Morphing Animation▲
This example shows how to define a complex custom geometry in C++ that contains a base shape and a morph target, with normal vectors for both.
Custom geometry▲
The main part of this example is creating a custom geometry with a morph target. We do this by subclassing QQuick3DGeometry:
class
MorphGeometry : public
QQuick3DGeometry
{
Q_OBJECT
QML_NAMED_ELEMENT(MorphGeometry)
Q_PROPERTY(int
gridSize READ gridSize WRITE setGridSize NOTIFY gridSizeChanged)
public
:
MorphGeometry(QQuick3DObject *
parent =
nullptr
);
int
gridSize() {
return
m_gridSize; }
void
setGridSize(int
gridSize);
signals
:
void
gridSizeChanged();
private
:
void
calculateGeometry();
void
updateData();
QList&
lt;QVector3D&
gt; m_positions;
QList&
lt;QVector3D&
gt; m_normals;
QList&
lt;QVector3D&
gt; m_targetPositions;
QList&
lt;QVector3D&
gt; m_targetNormals;
QList&
lt;quint32&
gt; m_indexes;
QByteArray m_vertexBuffer;
QByteArray m_indexBuffer;
int
m_gridSize =
50
;
QVector3D boundsMin;
QVector3D boundsMax;
}
;
The constructor defines the layout of the mesh data:
MorphGeometry::
MorphGeometry(QQuick3DObject *
parent)
:
QQuick3DGeometry(parent)
{
updateData();
}
The function updateData performs the actual uploading of the mesh geometry:
void
MorphGeometry::
updateData()
{
clear();
calculateGeometry();
addAttribute(QQuick3DGeometry::Attribute::
PositionSemantic, 0
,
QQuick3DGeometry::Attribute::ComponentType::
F32Type);
addAttribute(QQuick3DGeometry::Attribute::
NormalSemantic, 3
*
sizeof
(float
),
QQuick3DGeometry::Attribute::ComponentType::
F32Type);
addAttribute(QQuick3DGeometry::Attribute::
TargetPositionSemantic, 6
*
sizeof
(float
),
QQuick3DGeometry::Attribute::ComponentType::
F32Type);
addAttribute(QQuick3DGeometry::Attribute::
TargetNormalSemantic, 9
*
sizeof
(float
),
QQuick3DGeometry::Attribute::ComponentType::
F32Type);
addAttribute(QQuick3DGeometry::Attribute::
IndexSemantic, 0
,
QQuick3DGeometry::Attribute::ComponentType::
U32Type);
const
int
numVertexes =
m_positions.size();
m_vertexBuffer.resize(numVertexes *
sizeof
(Vertex));
Vertex *
vert =
reinterpret_cast
&
lt;Vertex *&
gt;(m_vertexBuffer.data());
for
(int
i =
0
; i &
lt; numVertexes; ++
i) {
Vertex &
amp;v =
vert[i];
v.position =
m_positions[i];
v.normal =
m_normals[i];
v.targetPosition =
m_targetPositions[i];
v.targetNormal =
m_targetNormals[i];
}
setStride(sizeof
(Vertex));
setVertexData(m_vertexBuffer);
setPrimitiveType(QQuick3DGeometry::PrimitiveType::
Triangles);
setBounds(boundsMin, boundsMax);
m_indexBuffer =
QByteArray(reinterpret_cast
&
lt;char
*&
gt;(m_indexes.data()), m_indexes.size() *
sizeof
(quint32));
setIndexData(m_indexBuffer);
}
We call updateData from the constructor, and when a property has changed.
The function calculateGeometry contains all the tedious mathematics to calculate the shapes and normal vectors. It is specific to this example, and the code will not be