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 explained in detail. In general: to implement smooth shading, it's neces