Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwavefrontmesh.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwavefrontmesh_p.h"
5
6#include <QtCore/qfile.h>
7#include <QtCore/qtextstream.h>
8#include <QtCore/private/qobject_p.h>
9
10#include <QtGui/qvector2d.h>
11#include <QtGui/qvector3d.h>
12
13#include <QtQml/qqmlfile.h>
14#include <QtQml/qqmlcontext.h>
15
16#include <QtQuick/qsggeometry.h>
17
19
21{
22public:
26
27 Q_DECLARE_PUBLIC(QWavefrontMesh)
28
30 {
31 return mesh->d_func();
32 }
33
34 static const QWavefrontMeshPrivate *get(const QWavefrontMesh *mesh)
35 {
36 return mesh->d_func();
37 }
38
39 QVector<QPair<ushort, ushort> > indexes;
40 QVector<QVector3D> vertexes;
41 QVector<QVector2D> textureCoordinates;
42
45
48};
49
67
140
144
167{
168 Q_D(const QWavefrontMesh);
169 return d->lastError;
170}
171
173{
174 Q_D(QWavefrontMesh);
175 if (d->lastError == lastError)
176 return;
177
178 d->lastError = lastError;
180}
181
189{
190 Q_D(const QWavefrontMesh);
191 return d->source;
192}
193
195{
196 Q_D(QWavefrontMesh);
197 if (d->source == source)
198 return;
199
200 d->source = source;
202}
203
205{
206 Q_D(QWavefrontMesh);
207 d->vertexes.clear();
208 d->textureCoordinates.clear();
209 d->indexes.clear();
210
211 QString localFile = QQmlFile::urlToLocalFileOrQrc(d->source);
212 if (!localFile.isEmpty()) {
213 QFile file(localFile);
216
218 buffer.reserve(256);
219
220 static QChar space(QLatin1Char(' '));
221 static QChar slash(QLatin1Char('/'));
222
223 while (!stream.atEnd()) {
224 stream.readLineInto(&buffer);
225 auto tokens = QStringView{buffer}.split(space, Qt::SkipEmptyParts);
226 if (tokens.size() < 2)
227 continue;
228
229 QByteArray command = tokens.at(0).toLatin1();
230
231 if (command == "vt") {
232 bool ok;
233 float u = tokens.at(1).toFloat(&ok);
234 if (!ok) {
236 return;
237 }
238
239 float v = tokens.size() > 2 ? tokens.at(2).toFloat(&ok) : 0.0;
240 if (!ok) {
242 return;
243 }
244
245 d->textureCoordinates.append(QVector2D(u, v));
246 } else if (command == "v") {
247 // Format: v <x> <y> <z> [w]
248 if (tokens.size() < 4 || tokens.size() > 5) {
250 return;
251 }
252
253 bool ok;
254
255 float x = tokens.at(1).toFloat(&ok);
256 if (!ok) {
258 return;
259 }
260
261 float y = tokens.at(2).toFloat(&ok);
262 if (!ok) {
264 return;
265 }
266
267 float z = tokens.at(3).toFloat(&ok);
268 if (!ok) {
270 return;
271 }
272
273 d->vertexes.append(QVector3D(x, y, z));
274 } else if (command == "f") {
275 // The scenegraph only supports triangles, so we
276 // support triangles and quads (which we split up)
277 int p1, p2, p3;
278 int t1 = 0;
279 int t2 = 0;
280 int t3 = 0;
281 if (tokens.size() >= 4 && tokens.size() <= 5) {
282 {
283 bool ok;
284 auto faceTokens = tokens.at(1).split(slash, Qt::SkipEmptyParts);
285 Q_ASSERT(!faceTokens.isEmpty());
286
287 p1 = faceTokens.at(0).toInt(&ok) - 1;
288 if (!ok) {
290 return;
291 }
292
293 if (faceTokens.size() > 1) {
294 t1 = faceTokens.at(1).toInt(&ok) - 1;
295 if (!ok) {
297 return;
298 }
299 }
300 }
301
302 {
303 bool ok;
304 auto faceTokens = tokens.at(2).split(slash, Qt::SkipEmptyParts);
305 Q_ASSERT(!faceTokens.isEmpty());
306
307 p2 = faceTokens.at(0).toInt(&ok) - 1;
308 if (!ok) {
310 return;
311 }
312
313 if (faceTokens.size() > 1) {
314 t2 = faceTokens.at(1).toInt(&ok) - 1;
315 if (!ok) {
317 return;
318 }
319 }
320 }
321
322 {
323 bool ok;
324 auto faceTokens = tokens.at(3).split(slash, Qt::SkipEmptyParts);
325 Q_ASSERT(!faceTokens.isEmpty());
326
327 p3 = faceTokens.at(0).toInt(&ok) - 1;
328 if (!ok) {
330 return;
331 }
332
333 if (faceTokens.size() > 1) {
334 t3 = faceTokens.at(1).toInt(&ok) - 1;
335 if (!ok) {
337 return;
338 }
339 }
340 }
341
342 if (Q_UNLIKELY(p1 < 0 || p1 > UINT16_MAX
343 || p2 < 0 || p2 > UINT16_MAX
344 || p3 < 0 || p3 > UINT16_MAX
345 || t1 < 0 || t1 > UINT16_MAX
346 || t2 < 0 || t2 > UINT16_MAX
347 || t3 < 0 || t3 > UINT16_MAX)) {
349 return;
350 }
351
352 d->indexes.append(qMakePair(ushort(p1), ushort(t1)));
353 d->indexes.append(qMakePair(ushort(p2), ushort(t2)));
354 d->indexes.append(qMakePair(ushort(p3), ushort(t3)));
355 } else {
357 return;
358 }
359
360 if (tokens.size() == 5) {
361 bool ok;
362 auto faceTokens = tokens.at(4).split(slash, Qt::SkipEmptyParts);
363 Q_ASSERT(!faceTokens.isEmpty());
364
365 int p4 = faceTokens.at(0).toInt(&ok) - 1;
366 if (!ok) {
368 return;
369 }
370
371 int t4 = 0;
372 if (faceTokens.size() > 1) {
373 t4 = faceTokens.at(1).toInt(&ok) - 1;
374 if (!ok) {
376 return;
377 }
378 }
379
380 if (Q_UNLIKELY(p4 < 0 || p4 > UINT16_MAX || t4 < 0 || t4 > UINT16_MAX)) {
382 return;
383 }
384
385 // ### Assumes convex quad, correct algorithm is to find the concave corner,
386 // and if there is one, do the split on the line between this and the corner it is
387 // not connected to. Also assumes order of vertices is counter clockwise.
388 d->indexes.append(qMakePair(ushort(p3), ushort(t3)));
389 d->indexes.append(qMakePair(ushort(p4), ushort(t4)));
390 d->indexes.append(qMakePair(ushort(p1), ushort(t1)));
391 }
392 }
393 }
394 } else {
396 }
397 } else {
399 }
400
402}
403
405{
406 Q_D(const QWavefrontMesh);
407 switch (d->lastError) {
408 case NoError:
409 return QStringLiteral("No error");
411 return QStringLiteral("Error: Invalid source");
413 return QStringLiteral("Error: Unsupported face shape in source");
415 return QStringLiteral("Error: Unsupported index size in source");
417 return QStringLiteral("Error: File not found");
419 return QStringLiteral("Error: Missing '%1' attribute").arg(
422 return QStringLiteral("Error: Missing '%1' attribute").arg(
425 return QStringLiteral("Error: Missing '%1' and '%2' attributes").arg(
429 return QStringLiteral("Error: Too many attributes");
431 return QStringLiteral("Error: Invalid plane. "
432 "V and W must be non-null and cannot be parallel");
433 default:
434 return QStringLiteral("Unknown error");
435 };
436}
437
438bool QWavefrontMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
439{
440 Q_D(QWavefrontMesh);
441 const int attrCount = attributes.size();
442 int positionIndex = attributes.indexOf(qtPositionAttributeName());
443 int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
444
445 switch (attrCount) {
446 case 0:
447 d->lastError = NoAttributesError;
448 return false;
449 case 1:
450 if (positionIndex < 0) {
451 d->lastError = MissingPositionAttributeError;
452 return false;
453 }
454 break;
455 case 2:
456 if (positionIndex < 0 || texCoordIndex < 0) {
457 if (positionIndex < 0 && texCoordIndex < 0)
459 else if (positionIndex < 0)
460 d->lastError = MissingPositionAttributeError;
461 else if (texCoordIndex < 0)
463 return false;
464 }
465 break;
466 default:
467 d->lastError = TooManyAttributesError;
468 return false;
469 }
470
471 if (posIndex)
472 *posIndex = positionIndex;
473
474 return true;
475
476}
477
478QSGGeometry *QWavefrontMesh::updateGeometry(QSGGeometry *geometry, int attributeCount, int positionIndex,
479 const QRectF &sourceRect, const QRectF &destinationRect)
480{
481 Q_D(QWavefrontMesh);
482
483 if (geometry == nullptr) {
484 Q_ASSERT(attributeCount == 1 || attributeCount == 2);
485 geometry = new QSGGeometry(attributeCount == 1
488 d->indexes.size(),
489 d->indexes.size(),
492
493 } else {
494 geometry->allocate(d->indexes.size(), d->indexes.size());
495 }
496
497 // If there is not at least a full triangle in the data set, skip out
498 if (d->indexes.size() < 3) {
499 geometry->allocate(0, 0);
500 return geometry;
501 }
502
503 QVector3D planeV = d->planeV;
504 QVector3D planeW = d->planeW;
505
506 // Automatically detect plane based on first face if none is set
507 if (planeV.isNull() || planeW.isNull()) {
508 QVector3D p = d->vertexes.at(d->indexes.at(0).first);
509 planeV = (d->vertexes.at(d->indexes.at(1).first) - p);
510 planeW = (p - d->vertexes.at(d->indexes.at(2).first)).normalized();
511 }
512
513 planeV.normalize();
514 planeW.normalize();
515
516 QVector3D planeNormal = QVector3D::crossProduct(planeV, planeW).normalized();
517 if (planeNormal.isNull()) { // V and W are either parallel or null
519 geometry->allocate(0, 0);
520 return geometry;
521 }
522
523 QVector3D planeAxes1 = planeV;
524 QVector3D planeAxes2 = QVector3D::crossProduct(planeAxes1, planeNormal).normalized();
525
526 ushort *indexData = static_cast<ushort *>(geometry->indexData());
527 QSGGeometry::Point2D *vertexData = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
528
529 float minX = 0.0f;
530 float maxX = 0.0f;
531 float minY = 0.0f;
532 float maxY = 0.0f;
533 for (ushort i = 0; i < ushort(d->indexes.size()); ++i) {
534 *(indexData + i) = i;
535
536 QVector3D v = d->vertexes.at(d->indexes.at(i).first);
537
538 // Project onto plane
539 QVector2D w;
540 v -= QVector3D::dotProduct(planeNormal, v) * planeNormal;
541 w.setX(QVector3D::dotProduct(v, planeAxes1));
542 w.setY(QVector3D::dotProduct(v, planeAxes2));
543
544 QSGGeometry::Point2D *positionData = vertexData + (i * attributeCount + positionIndex);
545 positionData->x = w.x();
546 positionData->y = w.y();
547
548 if (i == 0 || minX > w.x())
549 minX = w.x();
550 if (i == 0 || maxX < w.x())
551 maxX = w.x();
552 if (i == 0 || minY > w.y())
553 minY = w.y();
554 if (i == 0 || maxY < w.y())
555 maxY = w.y();
556
557 if (attributeCount > 1 && !d->textureCoordinates.isEmpty()) {
558 Q_ASSERT(positionIndex == 0 || positionIndex == 1);
559
560 QVector2D uv = d->textureCoordinates.at(d->indexes.at(i).second);
561 QSGGeometry::Point2D *textureCoordinateData = vertexData + (i * attributeCount + (1 - positionIndex));
562 textureCoordinateData->x = uv.x();
563 textureCoordinateData->y = uv.y();
564 }
565 }
566
567 float width = maxX - minX;
568 float height = maxY - minY;
569
570 QVector2D center(minX + width / 2.0f, minY + height / 2.0f);
571 QVector2D scale(1.0f / width, 1.0f / height);
572
573 for (int i = 0; i < geometry->vertexCount(); ++i) {
574 float x = ((vertexData + positionIndex)->x - center.x()) * scale.x();
575 float y = ((vertexData + positionIndex)->y - center.y()) * scale.y();
576
577 for (int attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) {
578 if (attributeIndex == positionIndex) {
579 vertexData->x = float(destinationRect.left()) + x * float(destinationRect.width()) + float(destinationRect.width()) / 2.0f;
580 vertexData->y = float(destinationRect.top()) + y * float(destinationRect.height()) + float(destinationRect.height()) / 2.0f;
581 } else {
582 // If there are no texture coordinates, use the normalized vertex
583 float tx = d->textureCoordinates.isEmpty() ? x : vertexData->x;
584 float ty = d->textureCoordinates.isEmpty() ? y : vertexData->y;
585
586 vertexData->x = float(sourceRect.left()) + tx * float(sourceRect.width());
587 vertexData->y = float(sourceRect.top()) + ty * float(sourceRect.height());
588 }
589
590 ++vertexData;
591 }
592 }
593
594 return geometry;
595}
596
614{
615 Q_D(QWavefrontMesh);
616 if (d->planeV == v)
617 return;
618
619 d->planeV = v;
621}
622
624{
625 Q_D(const QWavefrontMesh);
626 return d->planeV;
627}
628
646{
647 Q_D(QWavefrontMesh);
648 if (d->planeW == w)
649 return;
650
651 d->planeW = w;
653}
654
656{
657 Q_D(const QWavefrontMesh);
658 return d->planeW;
659}
660
661
663
664#include "moc_qwavefrontmesh_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:600
\inmodule QtCore
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:497
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
Definition qsggeometry.h:15
static const AttributeSet & defaultAttributes_Point2D()
Convenience function which returns attributes to be used for 2D solid color drawing.
void setDrawingMode(unsigned int mode)
Sets the mode to be used for drawing this geometry.
static const AttributeSet & defaultAttributes_TexturedPoint2D()
Convenience function which returns attributes to be used for textured 2D drawing.
void * indexData()
Returns a pointer to the raw index data of this geometry object.
void allocate(int vertexCount, int indexCount=0)
Resizes the vertex and index data of this geometry object to fit vertexCount vertices and indexCount ...
void * vertexData()
Returns a pointer to the raw vertex data of this geometry object.
int vertexCount() const
Returns the number of vertices in this geometry object.
\inmodule QtCore
Definition qstringview.h:78
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:8249
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
\inmodule QtCore
\inmodule QtCore
Definition qurl.h:94
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr bool isNull() const noexcept
Returns true if the x, y, and z coordinates are set to 0.0, otherwise returns false.
Definition qvectornd.h:665
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
void normalize() noexcept
Normalizes the current vector in place.
Definition qvectornd.h:702
QVector< QPair< ushort, ushort > > indexes
QWavefrontMesh::Error lastError
static QWavefrontMeshPrivate * get(QWavefrontMesh *mesh)
QVector< QVector2D > textureCoordinates
static const QWavefrontMeshPrivate * get(const QWavefrontMesh *mesh)
QVector< QVector3D > vertexes
void projectionPlaneVChanged()
QVector3D projectionPlaneV
QVector3D projectionPlaneW
void lastErrorChanged()
void setLastError(Error lastError)
void sourceChanged()
QWavefrontMesh(QObject *parent=nullptr)
\qmlmodule Qt.labs.wavefrontmesh 1.
QString log() const override
void projectionPlaneWChanged()
~QWavefrontMesh() override
void setProjectionPlaneW(const QVector3D &projectionPlaneW)
\qmlproperty vector3d WavefrontMesh::projectionPlaneW
void setProjectionPlaneV(const QVector3D &projectionPlaneV)
\qmlproperty vector3d WavefrontMesh::projectionPlaneV
bool validateAttributes(const QList< QByteArray > &attributes, int *posIndex) override
@ MissingPositionAndTextureCoordinateAttributesError
@ MissingTextureCoordinateAttributeError
QSGGeometry * updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex, const QRectF &srcRect, const QRectF &rect) override
void setSource(const QUrl &url)
QPixmap p2
QPixmap p1
[0]
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_UNLIKELY(x)
EGLStreamKHR stream
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLfloat GLfloat GLfloat GLfloat GLfloat maxY
GLfloat minY
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint buffer
GLint GLsizei width
GLint y
GLfloat GLfloat GLfloat GLfloat maxX
GLsizei GLsizei GLchar * source
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
GLbyte ty
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
const char * qtTexCoordAttributeName()
const char * qtPositionAttributeName()
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define t4
#define t3
#define t2
@ NoError
Definition main.cpp:34
#define emit
unsigned short ushort
Definition qtypes.h:33
float vertexData[]
QFile file
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
The QSGGeometry::Point2D struct is a convenience struct for accessing 2D Points.
Definition qsggeometry.h:79