Qt Quick 3D - Custom Morphing Animation | Qt Quick 3D 6.1.0

- Qt 6.1
- Qt Quick 3D
- Qt Quick 3D Examples and Tutorials
- Qt Quick 3D - Custom Morphing Animation
- Qt 6.1.0 Reference Documentation

# Qt Quick 3D - Custom Morphing Animation

     /****************************************************************************
     **
     ** Copyright (C) 2021 The Qt Company Ltd.
     ** Contact: https://www.qt.io/licensing/
     **
     ** This file is part of the examples of the Qt Toolkit.
     **
     ** $QT_BEGIN_LICENSE:BSD$
     ** Commercial License Usage
     ** Licensees holding valid commercial Qt licenses may use this file in
     ** accordance with the commercial license agreement provided with the
     ** Software or, alternatively, in accordance with the terms contained in
     ** a written agreement between you and The Qt Company. For licensing terms
     ** and conditions see https://www.qt.io/terms-conditions. For further
     ** information use the contact form at https://www.qt.io/contact-us.
     **
     ** BSD License Usage
     ** Alternatively, you may use this file under the terms of the BSD license
     ** as follows:
     **
     ** "Redistribution and use in source and binary forms, with or without
     ** modification, are permitted provided that the following conditions are
     ** met:
     **   * Redistributions of source code must retain the above copyright
     **     notice, this list of conditions and the following disclaimer.
     **   * Redistributions in binary form must reproduce the above copyright
     **     notice, this list of conditions and the following disclaimer in
     **     the documentation and/or other materials provided with the
     **     distribution.
     **   * Neither the name of The Qt Company Ltd nor the names of its
     **     contributors may be used to endorse or promote products derived
     **     from this software without specific prior written permission.
     **
     **
     ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
     **
     ** $QT_END_LICENSE$
     **
     ****************************************************************************/

     #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)); // must be  4-float aligned

     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);

         m_indexBuffer = QByteArray(reinterpret_cast<char *>(m_indexes.data()), m_indexes.size() * sizeof(quint32));
         setIndexData(m_indexBuffer);
     }

     void MorphGeometry::calculateGeometry()
     {
         float dw = 10.0; // width/2
         float dh = 10.0;
         int iw = m_gridSize;
         int ih = m_gridSize;
         float wf = dw * 2 / iw; //factor grid coord => position
         float hf = dh * 2 / ih;

         m_positions.clear();
         m_indexes.clear();
         m_targetPositions.clear();
         m_targetNormals.clear();

         // We construct a rectangular grid of iw times ih vertices;
         // ix and iy are indices into the grid. x, y, and z are the spatial
         // coordinates we calculate for each vertex. tx, ty, and tz are the
         // coordinates for the morph target.

         for (int iy = 0; iy < ih; ++iy) {
             for (int ix = 0; ix < iw; ++ix) {
                 float x = ix * wf - dw;
                 float z = iy * hf - dh;

                 // The base shape is a cosine wave, and the corresponding normal
                 // vectors are calculated from the partial derivatives.
                 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;

                 // The morph target shape is a hemisphere. Therefore we don't have
                 // to do complex math to calculate the normal vector, since all vectors
                 // from the center are normal to the surface of a sphere.
                 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};
                 }

                 // Finally, we make the outside edges of the shapes vertical, so they look nicer.
                 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());
             }
         }

         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;
             }
         }
     }