135QWavefrontMesh::QWavefrontMesh(QObject *parent)
136 : QQuickShaderEffectMesh(*(
new QWavefrontMeshPrivate), parent)
138 connect(
this, &QWavefrontMesh::sourceChanged,
this, &QWavefrontMesh::readData);
139 connect(
this, &QWavefrontMesh::projectionPlaneVChanged,
this, &QQuickShaderEffectMesh::geometryChanged);
140 connect(
this, &QWavefrontMesh::projectionPlaneWChanged,
this, &QQuickShaderEffectMesh::geometryChanged);
206void QWavefrontMesh::readData()
210 d->textureCoordinates.clear();
213 QString localFile = QQmlFile::urlToLocalFileOrQrc(d->source);
214 if (!localFile.isEmpty()) {
215 QFile file(localFile);
216 if (file.open(QIODevice::ReadOnly)) {
217 QTextStream stream(&file);
222 static QChar space(QLatin1Char(
' '));
223 static QChar slash(QLatin1Char(
'/'));
225 while (!stream.atEnd()) {
226 stream.readLineInto(&buffer);
227 auto tokens = QStringView{buffer}.split(space, Qt::SkipEmptyParts);
228 if (tokens.size() < 2)
231 QByteArray command = tokens.at(0).toLatin1();
233 if (command ==
"vt") {
235 float u = tokens.at(1).toFloat(&ok);
237 setLastError(InvalidSourceError);
241 float v = tokens.size() > 2 ? tokens.at(2).toFloat(&ok) : 0.0;
243 setLastError(InvalidSourceError);
247 d->textureCoordinates.append(QVector2D(u, v));
248 }
else if (command ==
"v") {
250 if (tokens.size() < 4 || tokens.size() > 5) {
251 setLastError(InvalidSourceError);
257 float x = tokens.at(1).toFloat(&ok);
259 setLastError(InvalidSourceError);
263 float y = tokens.at(2).toFloat(&ok);
265 setLastError(InvalidSourceError);
269 float z = tokens.at(3).toFloat(&ok);
271 setLastError(InvalidSourceError);
275 d->vertexes.append(QVector3D(x, y, z));
276 }
else if (command ==
"f") {
283 if (tokens.size() >= 4 && tokens.size() <= 5) {
286 auto faceTokens = tokens.at(1).split(slash, Qt::SkipEmptyParts);
287 Q_ASSERT(!faceTokens.isEmpty());
289 p1 = faceTokens.at(0).toInt(&ok) - 1;
291 setLastError(InvalidSourceError);
295 if (faceTokens.size() > 1) {
296 t1 = faceTokens.at(1).toInt(&ok) - 1;
298 setLastError(InvalidSourceError);
306 auto faceTokens = tokens.at(2).split(slash, Qt::SkipEmptyParts);
307 Q_ASSERT(!faceTokens.isEmpty());
309 p2 = faceTokens.at(0).toInt(&ok) - 1;
311 setLastError(InvalidSourceError);
315 if (faceTokens.size() > 1) {
316 t2 = faceTokens.at(1).toInt(&ok) - 1;
318 setLastError(InvalidSourceError);
326 auto faceTokens = tokens.at(3).split(slash, Qt::SkipEmptyParts);
327 Q_ASSERT(!faceTokens.isEmpty());
329 p3 = faceTokens.at(0).toInt(&ok) - 1;
331 setLastError(InvalidSourceError);
335 if (faceTokens.size() > 1) {
336 t3 = faceTokens.at(1).toInt(&ok) - 1;
338 setLastError(InvalidSourceError);
344 if (Q_UNLIKELY(p1 < 0 || p1 > UINT16_MAX
345 || p2 < 0 || p2 > UINT16_MAX
346 || p3 < 0 || p3 > UINT16_MAX
347 || t1 < 0 || t1 > UINT16_MAX
348 || t2 < 0 || t2 > UINT16_MAX
349 || t3 < 0 || t3 > UINT16_MAX)) {
350 setLastError(UnsupportedIndexSizeError);
354 d->indexes.append(std::make_pair(ushort(p1), ushort(t1)));
355 d->indexes.append(std::make_pair(ushort(p2), ushort(t2)));
356 d->indexes.append(std::make_pair(ushort(p3), ushort(t3)));
358 setLastError(UnsupportedFaceShapeError);
362 if (tokens.size() == 5) {
364 auto faceTokens = tokens.at(4).split(slash, Qt::SkipEmptyParts);
365 Q_ASSERT(!faceTokens.isEmpty());
367 int p4 = faceTokens.at(0).toInt(&ok) - 1;
369 setLastError(InvalidSourceError);
374 if (faceTokens.size() > 1) {
375 t4 = faceTokens.at(1).toInt(&ok) - 1;
377 setLastError(InvalidSourceError);
382 if (Q_UNLIKELY(p4 < 0 || p4 > UINT16_MAX || t4 < 0 || t4 > UINT16_MAX)) {
383 setLastError(UnsupportedIndexSizeError);
390 d->indexes.append(std::make_pair(ushort(p3), ushort(t3)));
391 d->indexes.append(std::make_pair(ushort(p4), ushort(t4)));
392 d->indexes.append(std::make_pair(ushort(p1), ushort(t1)));
397 setLastError(FileNotFoundError);
400 setLastError(InvalidSourceError);
403 emit geometryChanged();
440bool QWavefrontMesh::validateAttributes(
const QList<QByteArray> &attributes,
int *posIndex)
443 const int attrCount = attributes.size();
444 int positionIndex = attributes.indexOf(qtPositionAttributeName());
445 int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
449 d->lastError = NoAttributesError;
452 if (positionIndex < 0) {
453 d->lastError = MissingPositionAttributeError;
458 if (positionIndex < 0 || texCoordIndex < 0) {
459 if (positionIndex < 0 && texCoordIndex < 0)
460 d->lastError = MissingPositionAndTextureCoordinateAttributesError;
461 else if (positionIndex < 0)
462 d->lastError = MissingPositionAttributeError;
463 else if (texCoordIndex < 0)
464 d->lastError = MissingTextureCoordinateAttributeError;
469 d->lastError = TooManyAttributesError;
474 *posIndex = positionIndex;
480QSGGeometry *QWavefrontMesh::updateGeometry(QSGGeometry *geometry,
int attributeCount,
int positionIndex,
481 const QRectF &sourceRect,
const QRectF &destinationRect)
485 if (geometry ==
nullptr) {
486 Q_ASSERT(attributeCount == 1 || attributeCount == 2);
487 geometry =
new QSGGeometry(attributeCount == 1
488 ? QSGGeometry::defaultAttributes_Point2D()
489 : QSGGeometry::defaultAttributes_TexturedPoint2D(),
492 QSGGeometry::UnsignedShortType);
493 geometry->setDrawingMode(QSGGeometry::DrawTriangles);
496 geometry->allocate(d->indexes.size(), d->indexes.size());
500 if (d->indexes.size() < 3) {
501 geometry->allocate(0, 0);
505 QVector3D planeV = d->planeV;
506 QVector3D planeW = d->planeW;
509 if (planeV.isNull() || planeW.isNull()) {
510 QVector3D p = d->vertexes.at(d->indexes.at(0).first);
511 planeV = (d->vertexes.at(d->indexes.at(1).first) - p);
512 planeW = (p - d->vertexes.at(d->indexes.at(2).first)).normalized();
518 QVector3D planeNormal = QVector3D::crossProduct(planeV, planeW).normalized();
519 if (planeNormal.isNull()) {
520 setLastError(InvalidPlaneDefinitionError);
521 geometry->allocate(0, 0);
525 QVector3D planeAxes1 = planeV;
526 QVector3D planeAxes2 = QVector3D::crossProduct(planeAxes1, planeNormal).normalized();
528 ushort *indexData =
static_cast<ushort *>(geometry->indexData());
529 QSGGeometry::Point2D *vertexData =
static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
535 for (ushort i = 0; i < ushort(d->indexes.size()); ++i) {
536 *(indexData + i) = i;
538 QVector3D v = d->vertexes.at(d->indexes.at(i).first);
542 v -= QVector3D::dotProduct(planeNormal, v) * planeNormal;
543 w.setX(QVector3D::dotProduct(v, planeAxes1));
544 w.setY(QVector3D::dotProduct(v, planeAxes2));
546 QSGGeometry::Point2D *positionData = vertexData + (i * attributeCount + positionIndex);
547 positionData->x = w.x();
548 positionData->y = w.y();
550 if (i == 0 || minX > w.x())
552 if (i == 0 || maxX < w.x())
554 if (i == 0 || minY > w.y())
556 if (i == 0 || maxY < w.y())
559 if (attributeCount > 1 && !d->textureCoordinates.isEmpty()) {
560 Q_ASSERT(positionIndex == 0 || positionIndex == 1);
562 QVector2D uv = d->textureCoordinates.at(d->indexes.at(i).second);
563 QSGGeometry::Point2D *textureCoordinateData = vertexData + (i * attributeCount + (1 - positionIndex));
564 textureCoordinateData->x = uv.x();
565 textureCoordinateData->y = uv.y();
569 float width = maxX - minX;
570 float height = maxY - minY;
572 QVector2D center(minX + width / 2.0f, minY + height / 2.0f);
573 QVector2D scale(1.0f / width, 1.0f / height);
575 for (
int i = 0; i < geometry->vertexCount(); ++i) {
576 float x = ((vertexData + positionIndex)->x - center.x()) * scale.x();
577 float y = ((vertexData + positionIndex)->y - center.y()) * scale.y();
579 for (
int attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) {
580 if (attributeIndex == positionIndex) {
581 vertexData->x =
float(destinationRect.left()) + x *
float(destinationRect.width()) +
float(destinationRect.width()) / 2.0f;
582 vertexData->y =
float(destinationRect.top()) + y *
float(destinationRect.height()) +
float(destinationRect.height()) / 2.0f;
585 float tx = d->textureCoordinates.isEmpty() ? x : vertexData->x;
586 float ty = d->textureCoordinates.isEmpty() ? y : vertexData->y;
588 vertexData->x =
float(sourceRect.left()) + tx *
float(sourceRect.width());
589 vertexData->y =
float(sourceRect.top()) + ty *
float(sourceRect.height());