Qt Quick 3D - Custom Morphing Animation
#include "morphgeometry.h"
#include <qmath.h>
struct Vertex {
QVector3D position;
QVector3D normal;
QVector3D targetPosition;
QVector3D targetNormal;
};
Q_STATIC_ASSERT((sizeof(Vertex)/16)*16 == sizeof(Vertex));
MorphGeometry::MorphGeometry(QQuick3DObject *parent)
: QQuick3DGeometry(parent)
{
updateData();
}
void MorphGeometry::setGridSize(int gridSize)
{
if (m_gridSize == gridSize)
return;
m_gridSize = gridSize;
emit gridSizeChanged();
updateData();
update();
}
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<Vertex *>(m_vertexBuffer.data());
for (int i = 0; i < numVertexes; ++i) {
Vertex &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<char *>(m_indexes.data()), m_indexes.size() * sizeof(quint32));
setIndexData(m_indexBuffer);
}
void MorphGeometry::calculateGeometry()
{
float dw = 10.0;
float dh = 10.0;
int iw = m_gridSize;
int ih = m_gridSize;
float wf = dw * 2 / iw;
float hf = dh * 2 / ih;
m_positions.clear();
m_indexes.clear();
m_targetPositions.clear();
m_targetNormals.clear();
constexpr float maxFloat = std::numeric_limits<float>::max();
boundsMin = QVector3D(maxFloat, maxFloat, maxFloat);
boundsMax = QVector3D(-maxFloat, -maxFloat, -maxFloat);
for (int iy = 0; iy < ih; ++iy) {
for (int ix = 0; ix < iw; ++ix) {
float x = ix * wf - dw;
float z = iy * hf - dh;
float y = 2 * qCos(x) + 3.0;
QVector3D normal{2 * qSin(x), 2, 0};
float tx = x * 1.2;
float tz = z * 1.2;
constexpr float R = 16;
QVector3D targetPosition;
QVector3D targetNormal;
if (tx*tx + tz*tz < R*R) {
float ty = 0.4f * qSqrt(R*R - tx*tx - tz*tz);
targetPosition = {tx, ty, tz};
targetNormal = targetPosition;
} else {
targetPosition = {tx, -3, tz};
targetNormal = {0, 1, 0};
}
if (ix == 0 || iy == 0 || ix == iw-1 || iy == ih-1) {
int iix = qMin(iw-2, qMax(1, ix));
int iiy = qMin(ih-2, qMax(1, iy));
x = iix * wf - dw;
z = iiy * hf - dh;
y = -3.0;
targetPosition.setY(-3);
}
if (iy >= ih-2)
normal = {0, 0, 1};
else if (iy <= 1)
normal = {0, 0, -1};
m_positions.append({x, y, z});
m_normals.append(normal.normalized());
m_targetPositions.append(targetPosition);
m_targetNormals.append(targetNormal.normalized());
boundsMin.setX(std::min(boundsMin.x(), targetPosition.x()));
boundsMin.setY(std::min(boundsMin.y(), targetPosition.y()));
boundsMin.setZ(std::min(boundsMin.z(), targetPosition.z()));
boundsMax.setX(std::max(boundsMax.x(), targetPosition.x()));
boundsMax.setY(std::max(boundsMax.y(), targetPosition.y()));
boundsMax.setZ(std::max(boundsMax.z(), targetPosition.z()));
}
}
for (int ix = 0; ix < iw - 1; ++ix) {
for (int iy = 0; iy < ih - 1; ++iy) {
int idx = ix + iy * ih;
m_indexes << idx << idx + iw << idx + iw + 1
<< idx << idx + iw + 1 << idx + 1;