134QWavefrontMesh::QWavefrontMesh(QObject *parent)
135 : QQuickShaderEffectMesh(*(
new QWavefrontMeshPrivate), parent)
137 connect(
this, &QWavefrontMesh::sourceChanged,
this, &QWavefrontMesh::readData);
138 connect(
this, &QWavefrontMesh::projectionPlaneVChanged,
this, &QQuickShaderEffectMesh::geometryChanged);
139 connect(
this, &QWavefrontMesh::projectionPlaneWChanged,
this, &QQuickShaderEffectMesh::geometryChanged);
205void QWavefrontMesh::readData()
209 d->textureCoordinates.clear();
212 QString localFile = QQmlFile::urlToLocalFileOrQrc(d->source);
213 if (!localFile.isEmpty()) {
214 QFile file(localFile);
215 if (file.open(QIODevice::ReadOnly)) {
216 QTextStream stream(&file);
221 static QChar space(QLatin1Char(
' '));
222 static QChar slash(QLatin1Char(
'/'));
224 while (!stream.atEnd()) {
225 stream.readLineInto(&buffer);
226 auto tokens = QStringView{buffer}.split(space, Qt::SkipEmptyParts);
227 if (tokens.size() < 2)
230 QByteArray command = tokens.at(0).toLatin1();
232 if (command ==
"vt") {
234 float u = tokens.at(1).toFloat(&ok);
236 setLastError(InvalidSourceError);
240 float v = tokens.size() > 2 ? tokens.at(2).toFloat(&ok) : 0.0;
242 setLastError(InvalidSourceError);
246 d->textureCoordinates.append(QVector2D(u, v));
247 }
else if (command ==
"v") {
249 if (tokens.size() < 4 || tokens.size() > 5) {
250 setLastError(InvalidSourceError);
256 float x = tokens.at(1).toFloat(&ok);
258 setLastError(InvalidSourceError);
262 float y = tokens.at(2).toFloat(&ok);
264 setLastError(InvalidSourceError);
268 float z = tokens.at(3).toFloat(&ok);
270 setLastError(InvalidSourceError);
274 d->vertexes.append(QVector3D(x, y, z));
275 }
else if (command ==
"f") {
282 if (tokens.size() >= 4 && tokens.size() <= 5) {
285 auto faceTokens = tokens.at(1).split(slash, Qt::SkipEmptyParts);
286 Q_ASSERT(!faceTokens.isEmpty());
288 p1 = faceTokens.at(0).toInt(&ok) - 1;
290 setLastError(InvalidSourceError);
294 if (faceTokens.size() > 1) {
295 t1 = faceTokens.at(1).toInt(&ok) - 1;
297 setLastError(InvalidSourceError);
305 auto faceTokens = tokens.at(2).split(slash, Qt::SkipEmptyParts);
306 Q_ASSERT(!faceTokens.isEmpty());
308 p2 = faceTokens.at(0).toInt(&ok) - 1;
310 setLastError(InvalidSourceError);
314 if (faceTokens.size() > 1) {
315 t2 = faceTokens.at(1).toInt(&ok) - 1;
317 setLastError(InvalidSourceError);
325 auto faceTokens = tokens.at(3).split(slash, Qt::SkipEmptyParts);
326 Q_ASSERT(!faceTokens.isEmpty());
328 p3 = faceTokens.at(0).toInt(&ok) - 1;
330 setLastError(InvalidSourceError);
334 if (faceTokens.size() > 1) {
335 t3 = faceTokens.at(1).toInt(&ok) - 1;
337 setLastError(InvalidSourceError);
343 if (Q_UNLIKELY(p1 < 0 || p1 > UINT16_MAX
344 || p2 < 0 || p2 > UINT16_MAX
345 || p3 < 0 || p3 > UINT16_MAX
346 || t1 < 0 || t1 > UINT16_MAX
347 || t2 < 0 || t2 > UINT16_MAX
348 || t3 < 0 || t3 > UINT16_MAX)) {
349 setLastError(UnsupportedIndexSizeError);
353 d->indexes.append(std::make_pair(ushort(p1), ushort(t1)));
354 d->indexes.append(std::make_pair(ushort(p2), ushort(t2)));
355 d->indexes.append(std::make_pair(ushort(p3), ushort(t3)));
357 setLastError(UnsupportedFaceShapeError);
361 if (tokens.size() == 5) {
363 auto faceTokens = tokens.at(4).split(slash, Qt::SkipEmptyParts);
364 Q_ASSERT(!faceTokens.isEmpty());
366 int p4 = faceTokens.at(0).toInt(&ok) - 1;
368 setLastError(InvalidSourceError);
373 if (faceTokens.size() > 1) {
374 t4 = faceTokens.at(1).toInt(&ok) - 1;
376 setLastError(InvalidSourceError);
381 if (Q_UNLIKELY(p4 < 0 || p4 > UINT16_MAX || t4 < 0 || t4 > UINT16_MAX)) {
382 setLastError(UnsupportedIndexSizeError);
389 d->indexes.append(std::make_pair(ushort(p3), ushort(t3)));
390 d->indexes.append(std::make_pair(ushort(p4), ushort(t4)));
391 d->indexes.append(std::make_pair(ushort(p1), ushort(t1)));
396 setLastError(FileNotFoundError);
399 setLastError(InvalidSourceError);
402 emit geometryChanged();
439bool QWavefrontMesh::validateAttributes(
const QList<QByteArray> &attributes,
int *posIndex)
442 const int attrCount = attributes.size();
443 int positionIndex = attributes.indexOf(qtPositionAttributeName());
444 int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
448 d->lastError = NoAttributesError;
451 if (positionIndex < 0) {
452 d->lastError = MissingPositionAttributeError;
457 if (positionIndex < 0 || texCoordIndex < 0) {
458 if (positionIndex < 0 && texCoordIndex < 0)
459 d->lastError = MissingPositionAndTextureCoordinateAttributesError;
460 else if (positionIndex < 0)
461 d->lastError = MissingPositionAttributeError;
462 else if (texCoordIndex < 0)
463 d->lastError = MissingTextureCoordinateAttributeError;
468 d->lastError = TooManyAttributesError;
473 *posIndex = positionIndex;
479QSGGeometry *QWavefrontMesh::updateGeometry(QSGGeometry *geometry,
int attributeCount,
int positionIndex,
480 const QRectF &sourceRect,
const QRectF &destinationRect)
484 if (geometry ==
nullptr) {
485 Q_ASSERT(attributeCount == 1 || attributeCount == 2);
486 geometry =
new QSGGeometry(attributeCount == 1
487 ? QSGGeometry::defaultAttributes_Point2D()
488 : QSGGeometry::defaultAttributes_TexturedPoint2D(),
491 QSGGeometry::UnsignedShortType);
492 geometry->setDrawingMode(QSGGeometry::DrawTriangles);
495 geometry->allocate(d->indexes.size(), d->indexes.size());
499 if (d->indexes.size() < 3) {
500 geometry->allocate(0, 0);
504 QVector3D planeV = d->planeV;
505 QVector3D planeW = d->planeW;
508 if (planeV.isNull() || planeW.isNull()) {
509 QVector3D p = d->vertexes.at(d->indexes.at(0).first);
510 planeV = (d->vertexes.at(d->indexes.at(1).first) - p);
511 planeW = (p - d->vertexes.at(d->indexes.at(2).first)).normalized();
517 QVector3D planeNormal = QVector3D::crossProduct(planeV, planeW).normalized();
518 if (planeNormal.isNull()) {
519 setLastError(InvalidPlaneDefinitionError);
520 geometry->allocate(0, 0);
524 QVector3D planeAxes1 = planeV;
525 QVector3D planeAxes2 = QVector3D::crossProduct(planeAxes1, planeNormal).normalized();
527 ushort *indexData =
static_cast<ushort *>(geometry->indexData());
528 QSGGeometry::Point2D *vertexData =
static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
534 for (ushort i = 0; i < ushort(d->indexes.size()); ++i) {
535 *(indexData + i) = i;
537 QVector3D v = d->vertexes.at(d->indexes.at(i).first);
541 v -= QVector3D::dotProduct(planeNormal, v) * planeNormal;
542 w.setX(QVector3D::dotProduct(v, planeAxes1));
543 w.setY(QVector3D::dotProduct(v, planeAxes2));
545 QSGGeometry::Point2D *positionData = vertexData + (i * attributeCount + positionIndex);
546 positionData->x = w.x();
547 positionData->y = w.y();
549 if (i == 0 || minX > w.x())
551 if (i == 0 || maxX < w.x())
553 if (i == 0 || minY > w.y())
555 if (i == 0 || maxY < w.y())
558 if (attributeCount > 1 && !d->textureCoordinates.isEmpty()) {
559 Q_ASSERT(positionIndex == 0 || positionIndex == 1);
561 QVector2D uv = d->textureCoordinates.at(d->indexes.at(i).second);
562 QSGGeometry::Point2D *textureCoordinateData = vertexData + (i * attributeCount + (1 - positionIndex));
563 textureCoordinateData->x = uv.x();
564 textureCoordinateData->y = uv.y();
568 float width = maxX - minX;
569 float height = maxY - minY;
571 QVector2D center(minX + width / 2.0f, minY + height / 2.0f);
572 QVector2D scale(1.0f / width, 1.0f / height);
574 for (
int i = 0; i < geometry->vertexCount(); ++i) {
575 float x = ((vertexData + positionIndex)->x - center.x()) * scale.x();
576 float y = ((vertexData + positionIndex)->y - center.y()) * scale.y();
578 for (
int attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) {
579 if (attributeIndex == positionIndex) {
580 vertexData->x =
float(destinationRect.left()) + x *
float(destinationRect.width()) +
float(destinationRect.width()) / 2.0f;
581 vertexData->y =
float(destinationRect.top()) + y *
float(destinationRect.height()) +
float(destinationRect.height()) / 2.0f;
584 float tx = d->textureCoordinates.isEmpty() ? x : vertexData->x;
585 float ty = d->textureCoordinates.isEmpty() ? y : vertexData->y;
587 vertexData->x =
float(sourceRect.left()) + tx *
float(sourceRect.width());
588 vertexData->y =
float(sourceRect.top()) + ty *
float(sourceRect.height());