aboutsummaryrefslogtreecommitdiffstats
path: root/src/imports/wavefrontmesh/qwavefrontmesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/wavefrontmesh/qwavefrontmesh.cpp')
-rw-r--r--src/imports/wavefrontmesh/qwavefrontmesh.cpp681
1 files changed, 0 insertions, 681 deletions
diff --git a/src/imports/wavefrontmesh/qwavefrontmesh.cpp b/src/imports/wavefrontmesh/qwavefrontmesh.cpp
deleted file mode 100644
index 6be5c3d73f..0000000000
--- a/src/imports/wavefrontmesh/qwavefrontmesh.cpp
+++ /dev/null
@@ -1,681 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qwavefrontmesh.h"
-
-#include <QtCore/qfile.h>
-#include <QtCore/qtextstream.h>
-#include <QtCore/private/qobject_p.h>
-
-#include <QtGui/qvector2d.h>
-#include <QtGui/qvector3d.h>
-
-#include <QtQml/qqmlfile.h>
-#include <QtQml/qqmlcontext.h>
-
-#include <QtQuick/qsggeometry.h>
-
-QT_BEGIN_NAMESPACE
-
-class QWavefrontMeshPrivate : public QObjectPrivate
-{
-public:
- QWavefrontMeshPrivate()
- : lastError(QWavefrontMesh::NoError)
- {}
-
- Q_DECLARE_PUBLIC(QWavefrontMesh)
-
- static QWavefrontMeshPrivate *get(QWavefrontMesh *mesh)
- {
- return mesh->d_func();
- }
-
- static const QWavefrontMeshPrivate *get(const QWavefrontMesh *mesh)
- {
- return mesh->d_func();
- }
-
- QVector<QPair<ushort, ushort> > indexes;
- QVector<QVector3D> vertexes;
- QVector<QVector2D> textureCoordinates;
-
- QUrl source;
- QWavefrontMesh::Error lastError;
-
- QVector3D planeV;
- QVector3D planeW;
-};
-
-/*!
- \qmlmodule Qt.labs.wavefrontmesh 1.\QtMinorVersion
- \title Qt Labs WavefrontMesh QML Types
- \ingroup qmlmodules
- \brief The WavefrontMesh provides a mesh based on a Wavefront .obj file.
-
- To use this module, import the module with the following line:
-
- \qml
- import Qt.labs.wavefrontmesh
- \endqml
-*/
-
-/*!
- \qmltype WavefrontMesh
- \inqmlmodule Qt.labs.wavefrontmesh
- \instantiates QWavefrontMesh
- \ingroup qtquick-effects
- \brief The WavefrontMesh provides a mesh based on a Wavefront .obj file.
- \since 5.12
-
- WavefrontMesh reads the geometry from a Wavefront .obj file and generates
- a two-dimensional \l{QSGGeometry}{geometry} from this. If the .obj file
- contains a three-dimensional shape, it will be orthographically projected,
- onto a plane. If defined, this is given by \l projectionPlaneV
- and \l projectionPlaneW. Otherwise, the first face encountered in the data
- will be used to determine the projection plane.
-
- If the file contains texture coordinates, these will also be used. Otherwise,
- the vertexes of the object will be normalized and used.
-
- The mesh can be used in a ShaderEffect to define the shaded geometry. The
- geometry will be normalized before use, so the position and scale of the
- input objects have no impact on the result.
-
- \note Some Wavefront exporters will change the source scene's coordinate system
- before exporting it. This can cause unexpected results when Qt applies the
- projection. If the visual results are not as you expect, try checking the export
- parameters and the documentation of the editor tool to see if this is the case.
-
- For instance, the following example takes an .obj file containing a standard torus
- and visualizes the automatically generated texture coordinates.
-
- \table
- \row
- \li \image qtlabs-wavefrontmesh.png
- \li \qml
- import QtQuick 2.\1
- import Qt.labs.wavefrontmesh 1.\1
-
- ShaderEffect {
- width: 200
- height: 200
- mesh: WavefrontMesh {
- source: "torus.obj"
- projectionPlaneV: Qt.vector3d(0, 1, 0)
- projectionPlaneW: Qt.vector3d(1, 0, 0)
- }
- vertexShader: "
- uniform highp mat4 qt_Matrix;
- attribute highp vec4 qt_Vertex;
- attribute highp vec2 qt_MultiTexCoord0;
- varying highp vec2 coord;
- void main() {
- coord = qt_MultiTexCoord0;
- gl_Position = qt_Matrix * qt_Vertex;
- }"
- fragmentShader: "
- varying highp vec2 coord;
- uniform lowp float qt_Opacity;
- void main() {
- gl_FragColor = vec4(coord.x, coord.y, 0.0, 1.0);
- }"
-
- }
- \endqml
- \endtable
-
- \note Since the input is a 3D torus, we need to define the projection plane. This would not be necessary when
- using a 2D shape as input. We use the XY plane in this case, because of the orientation of the input.
-*/
-
-QWavefrontMesh::QWavefrontMesh(QObject *parent)
- : QQuickShaderEffectMesh(*(new QWavefrontMeshPrivate), parent)
-{
- connect(this, &QWavefrontMesh::sourceChanged, this, &QWavefrontMesh::readData);
- connect(this, &QWavefrontMesh::projectionPlaneVChanged, this, &QQuickShaderEffectMesh::geometryChanged);
- connect(this, &QWavefrontMesh::projectionPlaneWChanged, this, &QQuickShaderEffectMesh::geometryChanged);
-}
-
-QWavefrontMesh::~QWavefrontMesh()
-{
-}
-
-/*!
- \qmlproperty enumeration WavefrontMesh::lastError
-
- This property holds the last error, if any, that occurred when parsing the
- source or building the mesh.
-
- \list
- \li WavefrontMesh.NoError No error has occurred.
- \li WavefrontMesh.InvalidSourceError The source was not recognized as a valid .obj file.
- \li WavefrontMesh.UnsupportedFaceShapeError The faces in the source is of an unsupported type.
- WavefrontMesh only supports triangles and convex quads.
- \li WavefrontMesh.UnsupportedIndexSizeError The source shape is too large. Only 16 bit indexes are supported.
- \li WavefrontMesh.FileNotFoundError The source file was not found.
- \li WavefrontMesh.MissingPositionAttributeError The 'qt_Vertex' attribute is missing from the shaders.
- \li WavefrontMesh.MissingTextureCoordinateAttributeError The texture coordinate attribute in the shaders is wrongly named. Use 'qt_MultiTexCoord0'.
- \li WavefrontMesh.MissingPositionAndTextureCoordinateAttributesError Both the 'qt_Vertex' and 'qt_MultiTexCoord0' attributes are missing from the shaders.
- \li WavefrontMesh.TooManyAttributesError The shaders expect too many attributes (maximum is two: Position, 'qt_Vertex', and texture coordinate, 'qt_MultiTexCoord0').
- \li WavefrontMesh.InvalidPlaneDefinitionError The V and W vectors in the plane cannot be null, nor parallel to each other.
- \endlist
-*/
-
-QWavefrontMesh::Error QWavefrontMesh::lastError() const
-{
- Q_D(const QWavefrontMesh);
- return d->lastError;
-}
-
-void QWavefrontMesh::setLastError(Error lastError)
-{
- Q_D(QWavefrontMesh);
- if (d->lastError == lastError)
- return;
-
- d->lastError = lastError;
- emit lastErrorChanged();
-}
-
-/*!
- \qmlproperty url WavefrontMesh::source
-
- This property holds the URL of the source. This must be either a local file or in qrc. The source will
- be read as a Wavefront .obj file and the geometry will be updated.
-*/
-QUrl QWavefrontMesh::source() const
-{
- Q_D(const QWavefrontMesh);
- return d->source;
-}
-
-void QWavefrontMesh::setSource(const QUrl &source)
-{
- Q_D(QWavefrontMesh);
- if (d->source == source)
- return;
-
- d->source = source;
- emit sourceChanged();
-}
-
-void QWavefrontMesh::readData()
-{
- Q_D(QWavefrontMesh);
- d->vertexes.clear();
- d->textureCoordinates.clear();
- d->indexes.clear();
-
- QString localFile = QQmlFile::urlToLocalFileOrQrc(d->source);
- if (!localFile.isEmpty()) {
- QFile file(localFile);
- if (file.open(QIODevice::ReadOnly)) {
- QTextStream stream(&file);
-
- QString buffer;
- buffer.reserve(256);
-
- static QChar space(QLatin1Char(' '));
- static QChar slash(QLatin1Char('/'));
-
- while (!stream.atEnd()) {
- stream.readLineInto(&buffer);
- auto tokens = QStringView{buffer}.split(space, Qt::SkipEmptyParts);
- if (tokens.size() < 2)
- continue;
-
- QByteArray command = tokens.at(0).toLatin1();
-
- if (command == "vt") {
- bool ok;
- float u = tokens.at(1).toFloat(&ok);
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- float v = tokens.size() > 2 ? tokens.at(2).toFloat(&ok) : 0.0;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- d->textureCoordinates.append(QVector2D(u, v));
- } else if (command == "v") {
- // Format: v <x> <y> <z> [w]
- if (tokens.length() < 4 || tokens.length() > 5) {
- setLastError(InvalidSourceError);
- return;
- }
-
- bool ok;
-
- float x = tokens.at(1).toFloat(&ok);
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- float y = tokens.at(2).toFloat(&ok);
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- float z = tokens.at(3).toFloat(&ok);
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- d->vertexes.append(QVector3D(x, y, z));
- } else if (command == "f") {
- // The scenegraph only supports triangles, so we
- // support triangles and quads (which we split up)
- int p1, p2, p3;
- int t1 = 0;
- int t2 = 0;
- int t3 = 0;
- if (tokens.size() >= 4 && tokens.size() <= 5) {
- {
- bool ok;
- auto faceTokens = tokens.at(1).split(slash, Qt::SkipEmptyParts);
- Q_ASSERT(!faceTokens.isEmpty());
-
- p1 = faceTokens.at(0).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- if (faceTokens.size() > 1) {
- t1 = faceTokens.at(1).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
- }
- }
-
- {
- bool ok;
- auto faceTokens = tokens.at(2).split(slash, Qt::SkipEmptyParts);
- Q_ASSERT(!faceTokens.isEmpty());
-
- p2 = faceTokens.at(0).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- if (faceTokens.size() > 1) {
- t2 = faceTokens.at(1).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
- }
- }
-
- {
- bool ok;
- auto faceTokens = tokens.at(3).split(slash, Qt::SkipEmptyParts);
- Q_ASSERT(!faceTokens.isEmpty());
-
- p3 = faceTokens.at(0).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- if (faceTokens.size() > 1) {
- t3 = faceTokens.at(1).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
- }
- }
-
- if (Q_UNLIKELY(p1 < 0 || p1 > UINT16_MAX
- || p2 < 0 || p2 > UINT16_MAX
- || p3 < 0 || p3 > UINT16_MAX
- || t1 < 0 || t1 > UINT16_MAX
- || t2 < 0 || t2 > UINT16_MAX
- || t3 < 0 || t3 > UINT16_MAX)) {
- setLastError(UnsupportedIndexSizeError);
- return;
- }
-
- d->indexes.append(qMakePair(ushort(p1), ushort(t1)));
- d->indexes.append(qMakePair(ushort(p2), ushort(t2)));
- d->indexes.append(qMakePair(ushort(p3), ushort(t3)));
- } else {
- setLastError(UnsupportedFaceShapeError);
- return;
- }
-
- if (tokens.size() == 5) {
- bool ok;
- auto faceTokens = tokens.at(4).split(slash, Qt::SkipEmptyParts);
- Q_ASSERT(!faceTokens.isEmpty());
-
- int p4 = faceTokens.at(0).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
-
- int t4 = 0;
- if (faceTokens.size() > 1) {
- t4 = faceTokens.at(1).toInt(&ok) - 1;
- if (!ok) {
- setLastError(InvalidSourceError);
- return;
- }
- }
-
- if (Q_UNLIKELY(p4 < 0 || p4 > UINT16_MAX || t4 < 0 || t4 > UINT16_MAX)) {
- setLastError(UnsupportedIndexSizeError);
- return;
- }
-
- // ### Assumes convex quad, correct algorithm is to find the concave corner,
- // and if there is one, do the split on the line between this and the corner it is
- // not connected to. Also assumes order of vertices is counter clockwise.
- d->indexes.append(qMakePair(ushort(p3), ushort(t3)));
- d->indexes.append(qMakePair(ushort(p4), ushort(t4)));
- d->indexes.append(qMakePair(ushort(p1), ushort(t1)));
- }
- }
- }
- } else {
- setLastError(FileNotFoundError);
- }
- } else {
- setLastError(InvalidSourceError);
- }
-
- emit geometryChanged();
-}
-
-QString QWavefrontMesh::log() const
-{
- Q_D(const QWavefrontMesh);
- switch (d->lastError) {
- case NoError: return QStringLiteral("No error");
- case InvalidSourceError: return QStringLiteral("Error: Invalid source");
- case UnsupportedFaceShapeError: return QStringLiteral("Error: Unsupported face shape in source");
- case UnsupportedIndexSizeError: return QStringLiteral("Error: Unsupported index size in source");
- case FileNotFoundError: return QStringLiteral("Error: File not found");
- case MissingPositionAttributeError: return QStringLiteral("Error: Missing '%1' attribute").arg(qtPositionAttributeName());
- case MissingTextureCoordinateAttributeError: return QStringLiteral("Error: Missing '%1' attribute").arg(qtTexCoordAttributeName());
- case MissingPositionAndTextureCoordinateAttributesError: return QStringLiteral("Error: Missing '%1' and '%2' attributes").arg(qtPositionAttributeName()).arg(qtTexCoordAttributeName());
- case TooManyAttributesError: return QStringLiteral("Error: Too many attributes");
- case InvalidPlaneDefinitionError: return QStringLiteral("Error: Invalid plane. V and W must be non-null and cannot be parallel");
- default: return QStringLiteral("Unknown error");
- };
-}
-
-bool QWavefrontMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
-{
- Q_D(QWavefrontMesh);
- const int attrCount = attributes.count();
- int positionIndex = attributes.indexOf(qtPositionAttributeName());
- int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
-
- switch (attrCount) {
- case 0:
- d->lastError = NoAttributesError;
- return false;
- case 1:
- if (positionIndex < 0) {
- d->lastError = MissingPositionAttributeError;
- return false;
- }
- break;
- case 2:
- if (positionIndex < 0 || texCoordIndex < 0) {
- if (positionIndex < 0 && texCoordIndex < 0)
- d->lastError = MissingPositionAndTextureCoordinateAttributesError;
- else if (positionIndex < 0)
- d->lastError = MissingPositionAttributeError;
- else if (texCoordIndex < 0)
- d->lastError = MissingTextureCoordinateAttributeError;
- return false;
- }
- break;
- default:
- d->lastError = TooManyAttributesError;
- return false;
- }
-
- if (posIndex)
- *posIndex = positionIndex;
-
- return true;
-
-}
-
-QSGGeometry *QWavefrontMesh::updateGeometry(QSGGeometry *geometry, int attributeCount, int positionIndex,
- const QRectF &sourceRect, const QRectF &destinationRect)
-{
- Q_D(QWavefrontMesh);
-
- if (geometry == nullptr) {
- Q_ASSERT(attributeCount == 1 || attributeCount == 2);
- geometry = new QSGGeometry(attributeCount == 1
- ? QSGGeometry::defaultAttributes_Point2D()
- : QSGGeometry::defaultAttributes_TexturedPoint2D(),
- d->indexes.size(),
- d->indexes.size(),
- QSGGeometry::UnsignedShortType);
- geometry->setDrawingMode(QSGGeometry::DrawTriangles);
-
- } else {
- geometry->allocate(d->indexes.size(), d->indexes.size());
- }
-
- // If there is not at least a full triangle in the data set, skip out
- if (d->indexes.size() < 3) {
- geometry->allocate(0, 0);
- return geometry;
- }
-
- QVector3D planeV = d->planeV;
- QVector3D planeW = d->planeW;
-
- // Automatically detect plane based on first face if none is set
- if (planeV.isNull() || planeW.isNull()) {
- QVector3D p = d->vertexes.at(d->indexes.at(0).first);
- planeV = (d->vertexes.at(d->indexes.at(1).first) - p);
- planeW = (p - d->vertexes.at(d->indexes.at(2).first)).normalized();
- }
-
- planeV.normalize();
- planeW.normalize();
-
- QVector3D planeNormal = QVector3D::crossProduct(planeV, planeW).normalized();
- if (planeNormal.isNull()) { // V and W are either parallel or null
- setLastError(InvalidPlaneDefinitionError);
- geometry->allocate(0, 0);
- return geometry;
- }
-
- QVector3D planeAxes1 = planeV;
- QVector3D planeAxes2 = QVector3D::crossProduct(planeAxes1, planeNormal).normalized();
-
- ushort *indexData = static_cast<ushort *>(geometry->indexData());
- QSGGeometry::Point2D *vertexData = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
-
- float minX = 0.0f;
- float maxX = 0.0f;
- float minY = 0.0f;
- float maxY = 0.0f;
- for (ushort i = 0; i < ushort(d->indexes.size()); ++i) {
- *(indexData + i) = i;
-
- QVector3D v = d->vertexes.at(d->indexes.at(i).first);
-
- // Project onto plane
- QVector2D w;
- v -= QVector3D::dotProduct(planeNormal, v) * planeNormal;
- w.setX(QVector3D::dotProduct(v, planeAxes1));
- w.setY(QVector3D::dotProduct(v, planeAxes2));
-
- QSGGeometry::Point2D *positionData = vertexData + (i * attributeCount + positionIndex);
- positionData->x = w.x();
- positionData->y = w.y();
-
- if (i == 0 || minX > w.x())
- minX = w.x();
- if (i == 0 || maxX < w.x())
- maxX = w.x();
- if (i == 0 || minY > w.y())
- minY = w.y();
- if (i == 0 || maxY < w.y())
- maxY = w.y();
-
- if (attributeCount > 1 && !d->textureCoordinates.isEmpty()) {
- Q_ASSERT(positionIndex == 0 || positionIndex == 1);
-
- QVector2D uv = d->textureCoordinates.at(d->indexes.at(i).second);
- QSGGeometry::Point2D *textureCoordinateData = vertexData + (i * attributeCount + (1 - positionIndex));
- textureCoordinateData->x = uv.x();
- textureCoordinateData->y = uv.y();
- }
- }
-
- float width = maxX - minX;
- float height = maxY - minY;
-
- QVector2D center(minX + width / 2.0f, minY + height / 2.0f);
- QVector2D scale(1.0f / width, 1.0f / height);
-
- for (int i = 0; i < geometry->vertexCount(); ++i) {
- float x = ((vertexData + positionIndex)->x - center.x()) * scale.x();
- float y = ((vertexData + positionIndex)->y - center.y()) * scale.y();
-
- for (int attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) {
- if (attributeIndex == positionIndex) {
- vertexData->x = float(destinationRect.left()) + x * float(destinationRect.width()) + float(destinationRect.width()) / 2.0f;
- vertexData->y = float(destinationRect.top()) + y * float(destinationRect.height()) + float(destinationRect.height()) / 2.0f;
- } else {
- // If there are no texture coordinates, use the normalized vertex
- float tx = d->textureCoordinates.isEmpty() ? x : vertexData->x;
- float ty = d->textureCoordinates.isEmpty() ? y : vertexData->y;
-
- vertexData->x = float(sourceRect.left()) + tx * float(sourceRect.width());
- vertexData->y = float(sourceRect.top()) + ty * float(sourceRect.height());
- }
-
- ++vertexData;
- }
- }
-
- return geometry;
-}
-
-/*!
- \qmlproperty vector3d WavefrontMesh::projectionPlaneV
-
- Since the Wavefront .obj format describes an object in 3D space, the coordinates
- have to be projected into 2D before they can be displayed in Qt Quick.
-
- This will be done in WavefrontMesh by an orthographic projection onto an
- appropriate plane.
-
- The projectionPlaneV is one of two vectors in the plane in 3D space. If
- either this, or \l projectionPlaneW is set to (0, 0, 0) (the default),
- then the plane will be detected based on the first encountered face in the
- data set.
-
- \note projectionPlaneV and \l projectionPlaneW cannot be parallel vectors.
-*/
-void QWavefrontMesh::setProjectionPlaneV(const QVector3D &v)
-{
- Q_D(QWavefrontMesh);
- if (d->planeV == v)
- return;
-
- d->planeV = v;
- emit projectionPlaneVChanged();
-}
-
-QVector3D QWavefrontMesh::projectionPlaneV() const
-{
- Q_D(const QWavefrontMesh);
- return d->planeV;
-}
-
-/*!
- \qmlproperty vector3d WavefrontMesh::projectionPlaneW
-
- Since the Wavefront .obj format describes an object in 3D space, the coordinates
- have to be projected into 2D before they can be displayed in Qt Quick.
-
- This will be done in WavefrontMesh by an orthographic projection onto an
- appropriate plane.
-
- The projectionPlaneW is one of two vectors in the plane in 3D space. If
- either this, or \l projectionPlaneV is set to (0, 0, 0) (the default),
- then the plane will be detected based on the first encountered face in the
- data set.
-
- \note \l projectionPlaneV and projectionPlaneW cannot be parallel vectors.
-*/
-void QWavefrontMesh::setProjectionPlaneW(const QVector3D &w)
-{
- Q_D(QWavefrontMesh);
- if (d->planeW == w)
- return;
-
- d->planeW = w;
- emit projectionPlaneWChanged();
-}
-
-QVector3D QWavefrontMesh::projectionPlaneW() const
-{
- Q_D(const QWavefrontMesh);
- return d->planeW;
-}
-
-
-QT_END_NAMESPACE