Added GeometryProvider
This commit is contained in:
340
plugin/GeometryProvider.cpp
Normal file
340
plugin/GeometryProvider.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
#include "GeometryProvider.h"
|
||||
GeometryProvider::GeometryProvider(QQuick3DObject *parent) : QQuick3DGeometry{parent}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GeometryProvider::setHasNormals(bool hasNormals)
|
||||
{
|
||||
if(m_hasNormals != hasNormals)
|
||||
{
|
||||
m_hasNormals = hasNormals;
|
||||
Q_EMIT hasNormalsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryProvider::setHasUv(bool hasUv)
|
||||
{
|
||||
if(m_hasUV != hasUv)
|
||||
{
|
||||
m_hasUV = hasUv;
|
||||
Q_EMIT hasUvChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryProvider::setUVAdjust(qreal adjust)
|
||||
{
|
||||
if(m_uvAdjust != adjust)
|
||||
{
|
||||
m_uvAdjust = adjust;
|
||||
Q_EMIT uvAdjustChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryProvider::setSource(QString &source)
|
||||
{
|
||||
QFile file(source);
|
||||
QFileInfo fileInfo(file);
|
||||
|
||||
if(!fileInfo.exists())
|
||||
{
|
||||
qWarning() << QLatin1String("File %1 does not exist").arg(fileInfo.absoluteFilePath());
|
||||
return;
|
||||
}
|
||||
|
||||
if(fileInfo.suffix() == QLatin1String("obj"))
|
||||
loadObj(file);
|
||||
|
||||
else if(fileInfo.suffix() == QLatin1String("stl"))
|
||||
loadStl(file);
|
||||
}
|
||||
|
||||
void GeometryProvider::loadObj(QFile &file)
|
||||
{
|
||||
// 1e9 = 1*10^9 = 1,000,000,000
|
||||
QVector3D boundsMin( 1e9, 1e9, 1e9);
|
||||
QVector3D boundsMax(-1e9,-1e9,-1e9);
|
||||
setHasNormals(false);
|
||||
setHasUv(false);
|
||||
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
QString input = in.readLine();
|
||||
// # means comment
|
||||
if (input.isEmpty() || input[0] == QLatin1Char('#'))
|
||||
continue;
|
||||
|
||||
QTextStream ts(&input);
|
||||
QString id;
|
||||
ts >> id;
|
||||
//--------------- v = List of vertices with (x,y,z[,w]) corrdinates. -----------------
|
||||
if (id == QLatin1String("v")) {
|
||||
QVector3D p;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
ts >> p[i];
|
||||
boundsMin[i] = qMin(boundsMin[i], p[i]);
|
||||
boundsMax[i] = qMax(boundsMax[i], p[i]);
|
||||
}
|
||||
m_vertices << p;
|
||||
|
||||
//--------------- f = Face definitions -----------------
|
||||
} else if (id == QLatin1String("f") || id == QLatin1String("fo")) {
|
||||
QVarLengthArray<int, 4> p;
|
||||
|
||||
while (!ts.atEnd()) {
|
||||
QString vertex;
|
||||
ts >> vertex;
|
||||
//e.g. vertex / texture
|
||||
// vertex index in correspondence with vertex list.
|
||||
const int vertexIndex = vertex.split(QLatin1Char('/')).value(0).toInt();
|
||||
// qDebug() << "> vertexIndex : " << vertexIndex;
|
||||
if (vertexIndex) {
|
||||
p.append((vertexIndex > 0) ? (vertexIndex - 1) : (m_vertices.size() + vertexIndex));
|
||||
// int d = (vertexIndex > 0) ? (vertexIndex - 1) : (m_vertices.size() + vertexIndex);
|
||||
// qDebug() << "selected index : " << d;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << QLatin1String("p.size(%1) vs. vertexIndices.size(%2)").arg(QString::number(p.size())).arg(QString::number(m_vertexIndices.size()));
|
||||
|
||||
for (int i = 0; i < p.size(); ++i) {
|
||||
const int edgeA = p[i];
|
||||
const int edgeB = p[(i + 1) % p.size()];
|
||||
// qDebug() << QString("edgeA(%1/%2), edgeB(%3/%4)").arg(QString::number(edgeA), QString::number(i), QString::number(edgeB), QString::number((i + 1) % p.size())) ;
|
||||
|
||||
if (edgeA < edgeB) {
|
||||
m_edgeIndices << edgeA << edgeB;
|
||||
// qDebug() << "Added : edgeA : " << edgeA << " / edgeB : " << edgeB ;
|
||||
}
|
||||
}
|
||||
|
||||
// append vertex / texture-coordinate / normal
|
||||
for (int i = 0; i < 3; ++i)
|
||||
m_vertexIndices << p[i];
|
||||
|
||||
if (p.size() == 4)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
m_vertexIndices << p[(i + 2) % 4];
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << QLatin1String("size(%1), max-x(%2), min-x(%3), max-y(%4), min-y(%5), max-z(%6), min-z(%7)")
|
||||
.arg(QString::number(m_vertices.size()),
|
||||
QString::number(boundsMax.x()),
|
||||
QString::number(boundsMin.x()),
|
||||
QString::number(boundsMax.y()),
|
||||
QString::number(boundsMin.y()),
|
||||
QString::number(boundsMax.z()),
|
||||
QString::number(boundsMin.z()));
|
||||
const QVector3D bounds = boundsMax - boundsMin;
|
||||
const qreal scale = 1 / qMax(bounds.x(), qMax(bounds.y(), bounds.z()));
|
||||
qDebug() << QLatin1String("scale(%1) = 1 / %2")
|
||||
.arg(QString::number(scale), QString::number(qMax(bounds.x(), qMax(bounds.y(), bounds.z()))));
|
||||
// for (int i = 0; i < m_vertices.size(); ++i) {
|
||||
// //the way to place the model by mutiplying the ratio.
|
||||
// float ratio = 0.f;
|
||||
// m_vertices[i] = (m_vertices[i] - (boundsMin + bounds * ratio)) * scale;
|
||||
// }
|
||||
|
||||
m_verticesNew = m_vertices;
|
||||
recomputeAll();
|
||||
}
|
||||
|
||||
void GeometryProvider::loadStl(QFile &file)
|
||||
{
|
||||
QTextStream stream(&file);
|
||||
const QString &head = stream.readLine();
|
||||
setHasUv(false);
|
||||
if (head.left(6) == QLatin1String("solid ") && head.size() < 80) // ASCII format
|
||||
{
|
||||
// name = head.right(head.size() - 6).toStdString();
|
||||
QString word;
|
||||
stream >> word;
|
||||
for(; word == QLatin1String("facet") ; stream >> word)
|
||||
{
|
||||
stream >> word; // normal x y z
|
||||
QVector3D n;
|
||||
stream >> n[0] >> n[1] >> n[2];
|
||||
n.normalize();
|
||||
|
||||
stream >> word >> word; // outer loop
|
||||
stream >> word;
|
||||
size_t startIndex = m_vertices.size();
|
||||
for(; word != QLatin1String("endloop") ; stream >> word)
|
||||
{
|
||||
QVector3D v; //vertex x y z
|
||||
stream >> v[0] >> v[1] >> v[2];
|
||||
m_vertices.push_back(v);
|
||||
// qDebug() << "==== outer loop ===";
|
||||
}
|
||||
|
||||
for(qsizetype i = startIndex + 2 ; i < m_vertices.size() ; ++i)
|
||||
{
|
||||
m_vertexIndices.push_back(startIndex);
|
||||
m_vertexIndices.push_back(i - 1);
|
||||
m_vertexIndices.push_back(i);
|
||||
|
||||
qDebug() << QLatin1String("edgeA(%1), edgeB(%2)").arg(QString::number(startIndex), QString::number(i)) ;
|
||||
|
||||
// if (startIndex < (i-1))
|
||||
m_edgeIndices << (startIndex) << (i - 1) << i;
|
||||
}
|
||||
stream >> word; // endfacet
|
||||
setHasNormals(false);
|
||||
}
|
||||
} else {
|
||||
file.setTextModeEnabled(false);
|
||||
file.seek(80);
|
||||
quint32 triangleCount;
|
||||
file.read((char*)&triangleCount, sizeof(triangleCount));
|
||||
m_vertices.reserve(triangleCount * 3U);
|
||||
m_normals.reserve(triangleCount * 3U);
|
||||
m_vertexIndices.reserve(triangleCount * 3U);
|
||||
for(size_t i = 0 ; i < triangleCount ; ++i)
|
||||
{
|
||||
QVector3D n, a, b, c;
|
||||
|
||||
READ_VECTOR(n);
|
||||
READ_VECTOR(a);
|
||||
READ_VECTOR(b);
|
||||
READ_VECTOR(c);
|
||||
|
||||
m_vertexIndices.push_back(m_vertices.size());
|
||||
m_vertexIndices.push_back(m_vertices.size() + 1);
|
||||
m_vertexIndices.push_back(m_vertices.size() + 2);
|
||||
m_vertices.push_back(a);
|
||||
m_vertices.push_back(b);
|
||||
m_vertices.push_back(c);
|
||||
|
||||
quint16 attribute_byte_count;
|
||||
file.read((char*)&attribute_byte_count, sizeof(attribute_byte_count));
|
||||
}
|
||||
setHasNormals(true);
|
||||
}
|
||||
m_verticesNew = m_vertices;
|
||||
recomputeAll();
|
||||
}
|
||||
|
||||
//Bounding Box : http://en.wikibooks.org/wiki/OpenGL_Programming/Bounding_box
|
||||
void GeometryProvider::recomputeAll()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
//calculate normals of each face
|
||||
int size = m_verticesNew.size();
|
||||
m_normals.resize(size);
|
||||
for (int i = 0; i < m_vertexIndices.size(); i += 3) {
|
||||
const QVector3D a = m_verticesNew.at(m_vertexIndices.at(i));
|
||||
const QVector3D b = m_verticesNew.at(m_vertexIndices.at(i+1));
|
||||
const QVector3D c = m_verticesNew.at(m_vertexIndices.at(i+2));
|
||||
|
||||
const QVector3D normal = QVector3D::crossProduct(b - a, c - a).normalized();
|
||||
|
||||
for (int j = 0; j < 3; ++j)
|
||||
m_normals[m_vertexIndices.at(i + j)] += normal;
|
||||
}
|
||||
|
||||
/* //debug output
|
||||
qDebug() << "=========== Output =============";
|
||||
for (int i = 0; i < m_verticesNew.size(); ++i) {
|
||||
qDebug() << QString("x(%1), y(%2), z(%3)").arg(m_verticesNew.at(i).x()).arg(m_verticesNew.at(i).y()).arg(m_verticesNew.at(i).z());
|
||||
}
|
||||
|
||||
for (int i = 0; i< m_vertexIndices.size(); ++i) {
|
||||
qDebug() << QString("index %1").arg(m_vertexIndices.at(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_normals.size(); ++i ) {
|
||||
qDebug() << QString("x(%1), y(%2), z(%3)").arg(m_normals.at(i).x()).arg(m_normals.at(i).y()).arg(m_normals.at(i).z());
|
||||
}
|
||||
qDebug() << "========================";
|
||||
*/
|
||||
//recompute normals and bounding volume
|
||||
qreal minX, maxX,
|
||||
minY, maxY,
|
||||
minZ, maxZ;
|
||||
|
||||
minX = maxX = m_verticesNew[0].x();
|
||||
minY = maxY = m_verticesNew[0].y();
|
||||
minZ = maxZ = m_verticesNew[0].z();
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
//compute normals
|
||||
m_normals[i] = m_normals[i].normalized();
|
||||
|
||||
//calculate values of maximum and minimum
|
||||
if (m_verticesNew[i].x() < minX) minX = m_verticesNew[i].x();
|
||||
if (m_verticesNew[i].x() > maxX) maxX = m_verticesNew[i].x();
|
||||
if (m_verticesNew[i].y() < minY) minY = m_verticesNew[i].y();
|
||||
if (m_verticesNew[i].y() > maxY) maxY = m_verticesNew[i].y();
|
||||
if (m_verticesNew[i].z() < minZ) minZ = m_verticesNew[i].z();
|
||||
if (m_verticesNew[i].z() > maxZ) maxZ = m_verticesNew[i].z();
|
||||
}
|
||||
|
||||
m_size = QVector3D(maxX-minX, maxY-minY, maxZ-minZ);
|
||||
m_center = QVector3D((minX+maxX)/2, (minY+maxY)/2, (minZ+maxZ)/2);
|
||||
|
||||
m_min = QVector3D(minX, minY, minZ);
|
||||
m_max = QVector3D(maxX, maxY, maxZ);
|
||||
|
||||
// qDebug() << QLatin1String("MIN : x(%1), y(%2), z(%3)").arg(m_min.x()).arg(m_min.y()).arg(m_min.z());
|
||||
// qDebug() << QLatin1String("MAN : x(%1), y(%2), z(%3)").arg(m_max.x()).arg(m_max.y()).arg(m_max.z());
|
||||
// qDebug() << QLatin1String("SIZE : x(%1), y(%2), z(%3)").arg(m_size.x()).arg(m_size.y()).arg(m_size.z());
|
||||
|
||||
// QMatrix4x4 center(1,1,1,1), scale(1,1,1,1);
|
||||
// m_transform.translate(QMatrix4x4(1,1,1) * center)
|
||||
|
||||
int stride = 3 * sizeof(qreal);
|
||||
|
||||
if(hasNormals())
|
||||
stride += 3 * sizeof(qreal);
|
||||
if(hasUv())
|
||||
stride += 2 * sizeof(qreal);
|
||||
|
||||
QByteArray vertexData(3 * stride, Qt::Uninitialized);
|
||||
qreal *pointer = reinterpret_cast<qreal*>(vertexData.data());
|
||||
|
||||
for(int i = 0; i < m_vertices.count(); i++)
|
||||
{
|
||||
*pointer++ = m_vertices.at(i).x();
|
||||
*pointer++ = m_vertices.at(i).y();
|
||||
*pointer++ = m_vertices.at(i).z();
|
||||
|
||||
if(hasNormals())
|
||||
{
|
||||
*pointer++ = m_normals.at(i).x();
|
||||
*pointer++ = m_normals.at(i).y();
|
||||
*pointer++ = m_normals.at(i).z();
|
||||
}
|
||||
|
||||
if(hasUv())
|
||||
{
|
||||
*pointer++ = m_uv.at(i).x() - m_uvAdjust;
|
||||
*pointer++ = m_uv.at(i).y() - m_uvAdjust;
|
||||
}
|
||||
}
|
||||
|
||||
setBounds(m_min, m_max);
|
||||
setVertexData(vertexData);
|
||||
setStride(stride);
|
||||
|
||||
setPrimitiveType(QQuick3DGeometry::PrimitiveType::Points);
|
||||
|
||||
addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
||||
0,
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
|
||||
if (m_hasNormals) {
|
||||
addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
|
||||
3 * sizeof(float),
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
}
|
||||
|
||||
if (m_hasUV) {
|
||||
addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic,
|
||||
m_hasNormals ? 6 * sizeof(float) : 3 * sizeof(float),
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
}
|
||||
}
|
||||
113
plugin/GeometryProvider.h
Normal file
113
plugin/GeometryProvider.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* GeometryProvider.h
|
||||
*
|
||||
* This class provides a way to use .obj and .stl in QML
|
||||
*
|
||||
* The loadObj() and loadStl() functions are from stl-gcode-viewer
|
||||
* Copyright 2015-2025 @sokunmin | github.com/sokunmin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef GeometryProvider_H
|
||||
#define GeometryProvider_H
|
||||
|
||||
#define READ_VECTOR(v)\
|
||||
do {\
|
||||
float f;\
|
||||
file.read((char*)&f, sizeof(float)); v[0] = f;\
|
||||
file.read((char*)&f, sizeof(float)); v[1] = f;\
|
||||
file.read((char*)&f, sizeof(float)); v[2] = f;\
|
||||
} while(false)
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuick3DGeometry>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QVector3D>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
#include "Komplex_global.h"
|
||||
|
||||
class KOMPLEX_EXPORT GeometryProvider : public QQuick3DGeometry
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(bool hasNormals READ hasNormals WRITE setHasNormals NOTIFY hasNormalsChanged)
|
||||
Q_PROPERTY(bool hasUv READ hasUv WRITE setHasUv NOTIFY hasUvChanged)
|
||||
Q_PROPERTY(qreal uvAdjust READ uvAdjust WRITE setUVAdjust NOTIFY uvAdjustChanged)
|
||||
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
|
||||
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
Idle,
|
||||
Loading,
|
||||
Loaded,
|
||||
Error
|
||||
};
|
||||
|
||||
GeometryProvider(QQuick3DObject *parent = nullptr);
|
||||
|
||||
bool hasNormals() const { return m_hasNormals; }
|
||||
void setHasNormals(bool enable);
|
||||
|
||||
bool hasUv() const { return m_hasUV; }
|
||||
void setHasUv(bool enable);
|
||||
|
||||
float uvAdjust() const { return m_uvAdjust; }
|
||||
void setUVAdjust(qreal f);
|
||||
|
||||
QString source() const { return m_source; }
|
||||
void setSource(QString &source);
|
||||
|
||||
Q_SIGNALS:
|
||||
void normalsChanged();
|
||||
void hasNormalsChanged();
|
||||
void uvChanged();
|
||||
void hasUvChanged();
|
||||
void uvAdjustChanged();
|
||||
void sourceChanged();
|
||||
|
||||
private:
|
||||
void loadObj(QFile &file);
|
||||
void loadStl(QFile &file);
|
||||
void recomputeAll();
|
||||
|
||||
bool m_hasNormals = false;
|
||||
bool m_hasUV = false;
|
||||
|
||||
qreal m_uvAdjust = 0.0f;
|
||||
QString m_source;
|
||||
|
||||
QVector3D m_size;
|
||||
QVector3D m_center;
|
||||
QVector3D m_min;
|
||||
QVector3D m_max;
|
||||
QMatrix4x4 m_transform;
|
||||
|
||||
QVector<QVector3D> m_vertices;
|
||||
QVector<QVector3D> m_verticesNew;
|
||||
QVector<QVector3D> m_normals;
|
||||
QVector<QVector2D> m_uv;
|
||||
|
||||
QVector<int> m_edgeIndices;
|
||||
QVector<int> m_vertexIndices;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(GeometryProvider)
|
||||
#endif
|
||||
Reference in New Issue
Block a user