QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgs3dexportobject.cpp
Go to the documentation of this file.
1/***************************************************************************
2 Qgs3DExportObject.cpp
3 --------------------------------------
4 Date : June 2020
5 Copyright : (C) 2020 by Belgacem Nedjima
6 Email : gb underscore nedjima at esi dot dz
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgs3dexportobject.h"
17
18#include <QVector3D>
19#include <QDir>
20#include <QImage>
21
22#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
23#include <Qt3DRender/QAttribute>
24#include <Qt3DRender/QBuffer>
25typedef Qt3DRender::QAttribute Qt3DQAttribute;
26typedef Qt3DRender::QBuffer Qt3DQBuffer;
27#else
28#include <Qt3DCore/QAttribute>
29#include <Qt3DCore/QBuffer>
30typedef Qt3DCore::QAttribute Qt3DQAttribute;
31typedef Qt3DCore::QBuffer Qt3DQBuffer;
32#endif
33
34#include "qgslogger.h"
36
37
38template<typename T>
39void insertIndexData( QVector<uint> &vertexIndex, const QVector<T> &faceIndex )
40{
41 for ( int i = 0; i < faceIndex.size(); i += 3 )
42 {
43 if ( i + 2 >= faceIndex.size() ) continue;
44 // skip invalid triangles
45 if ( faceIndex[i] == faceIndex[i + 1] || faceIndex[i + 1] == faceIndex[i + 2] || faceIndex[i] == faceIndex[i + 2] )
46 continue;
47 for ( int j = 0; j < 3; ++j )
48 vertexIndex << faceIndex[i + j];
49 }
50}
51
52void Qgs3DExportObject::setupPositionCoordinates( const QVector<float> &positionsBuffer, float scale, const QVector3D &translation )
53{
54 for ( int i = 0; i < positionsBuffer.size(); i += 3 )
55 {
56 for ( int j = 0; j < 3; ++j )
57 {
58 mVertexPosition << positionsBuffer[i + j] * scale + translation[j];
59 }
60 }
61}
62
63void Qgs3DExportObject::setupFaces( const QVector<uint> &facesIndexes )
64{
65 insertIndexData<uint>( mIndexes, facesIndexes );
66}
67
68void Qgs3DExportObject::setupLine( const QVector<uint> &lineIndexes )
69{
70 Q_UNUSED( lineIndexes );
71 for ( int i = 0; i < mVertexPosition.size(); i += 3 ) mIndexes << i / 3 + 1;
72}
73
74void Qgs3DExportObject::setupNormalCoordinates( const QVector<float> &normalsBuffer )
75{
76 mNormals << normalsBuffer;
77}
78
79void Qgs3DExportObject::setupTextureCoordinates( const QVector<float> &texturesBuffer )
80{
81 mTexturesUV << texturesBuffer;
82}
83
85{
86 QMap<QString, QString> parameters = material->toExportParameters();
87 for ( auto it = parameters.begin(); it != parameters.end(); ++it )
88 {
89 setMaterialParameter( it.key(), it.value() );
90 }
91}
92
93void Qgs3DExportObject::objectBounds( float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ )
94{
95 if ( mType != TriangularFaces ) return;
96 for ( const unsigned int vertice : mIndexes )
97 {
98 const unsigned int heightIndex = vertice * 3 + 1;
99 minX = std::min( minX, mVertexPosition[heightIndex - 1] );
100 maxX = std::max( maxX, mVertexPosition[heightIndex - 1] );
101 minY = std::min( minY, mVertexPosition[heightIndex] );
102 maxY = std::max( maxY, mVertexPosition[heightIndex] );
103 minZ = std::min( minZ, mVertexPosition[heightIndex + 1] );
104 maxZ = std::max( maxZ, mVertexPosition[heightIndex + 1] );
105 }
106}
107
108void Qgs3DExportObject::saveTo( QTextStream &out, float scale, const QVector3D &center )
109{
110 // Set groups
111 // turns out grouping doest work as expected in blender
112
113 // smoothen edges
114 if ( mSmoothEdges )
115 out << "s on\n";
116 else
117 out << "s off\n";
118
119 // Construct vertices
120 for ( const unsigned int vertice : mIndexes )
121 {
122 const int i = static_cast<int>( vertice * 3 );
123 // for now just ignore wrong vertex positions
124 out << "v ";
125 out << ( mVertexPosition[i] - center.x() ) / scale << " ";
126 out << ( mVertexPosition[i + 1] - center.y() ) / scale << " ";
127 out << ( mVertexPosition[i + 2] - center.z() ) / scale << "\n";
128 if ( i + 3 <= mNormals.size() )
129 {
130 out << "vn " << mNormals[i] << " " << mNormals[i + 1] << " " << mNormals[i + 2] << "\n";
131 }
132 const int u_index = i / 3 * 2;
133 if ( u_index + 1 < mTexturesUV.size() )
134 {
135 // TODO: flip texture in a more appropriate way (for repeated textures)
136 out << "vt " << mTexturesUV[u_index] << " " << 1.0f - mTexturesUV[u_index + 1] << "\n";
137 }
138 }
139
140 bool hasTextures = mTexturesUV.size() == mVertexPosition.size() / 3 * 2;
141 // if the object has normals then the normals and positions buffers should be the same size
142 bool hasNormals = mNormals.size() == mVertexPosition.size();
143
144 if ( !hasNormals && !mNormals.empty() )
145 {
146 QgsDebugError( "Vertex normals count and vertex positions count are different" );
147 }
148 int verticesCount = mIndexes.size();
149
150 // we use negative indexes as this is the way to use relative values to reference vertex positions
151 // Positive values are absolute vertex position from the beginning of the file.
152 auto getVertexIndex = [&]( unsigned int i ) -> QString
153 {
154 const int negativeIndex = static_cast<int>( - ( verticesCount - i ) - mIndexes[0] ); // mIndexes[0] is used to shift the index according to the first index (not always 0)
155 if ( hasNormals && !hasTextures )
156 return QStringLiteral( "%1//%2" ).arg( negativeIndex ).arg( negativeIndex );
157 if ( !hasNormals && hasTextures )
158 return QStringLiteral( "%1/%2" ).arg( negativeIndex ).arg( negativeIndex );
159 if ( hasNormals && hasTextures )
160 return QStringLiteral( "%1/%2/%3" ).arg( negativeIndex ).arg( negativeIndex ).arg( negativeIndex );
161 return QString::number( negativeIndex );
162 };
163
164 if ( mType == TriangularFaces )
165 {
166 // Construct triangular faces
167 for ( int i = 0; i < mIndexes.size(); i += 3 )
168 {
169 if ( mIndexes[i] == mIndexes[i + 1] && mIndexes[i + 1] == mIndexes[i + 2] )
170 continue;
171 out << "f " << getVertexIndex( mIndexes[i] );
172 out << " " << getVertexIndex( mIndexes[i + 1] );
173 out << " " << getVertexIndex( mIndexes[i + 2] );
174 out << "\n";
175 }
176 }
177 else if ( mType == LineStrip )
178 {
179 out << "l";
180 for ( const unsigned int i : mIndexes )
181 out << " " << getVertexIndex( i );
182 out << "\n";
183 }
184 else if ( mType == Points )
185 {
186 out << "p";
187 for ( const unsigned int i : mIndexes )
188 out << " " << getVertexIndex( i );
189 out << "\n";
190 }
191}
192
193QString Qgs3DExportObject::saveMaterial( QTextStream &mtlOut, const QString &folderPath )
194{
195 QString materialName = mName + "_material";
196 if ( mMaterialParameters.size() == 0 && ( mTexturesUV.size() == 0 || mTextureImage.isNull() ) ) return QString();
197 mtlOut << "newmtl " << materialName << "\n";
198 if ( mTexturesUV.size() != 0 && !mTextureImage.isNull() )
199 {
200 const QString filePath = QDir( folderPath ).filePath( materialName + ".jpg" );
201 mTextureImage.save( filePath, "JPG" );
202 mtlOut << "\tmap_Kd " << materialName << ".jpg" << "\n";
203 }
204 for ( auto it = mMaterialParameters.constBegin(); it != mMaterialParameters.constEnd(); it++ )
205 {
206 mtlOut << "\t" << it.key() << " " << it.value() << "\n";
207 }
208 mtlOut << "\tillum 2\n";
209 return materialName;
210}
void setMaterialParameter(const QString &parameter, const QString &value)
Sets a material parameter to be exported in the .mtl file.
void saveTo(QTextStream &out, float scale, const QVector3D &center)
Saves the current object to the output stream while scaling the object and centering it to be visible...
void objectBounds(float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ)
Updates the box bounds explained with the current object bounds This expands the bounding box if the ...
void setupNormalCoordinates(const QVector< float > &normalsBuffer)
Sets normal coordinates for each vertex.
void setupPositionCoordinates(const QVector< float > &positionsBuffer, float scale=1.0f, const QVector3D &translation=QVector3D(0, 0, 0))
Sets positions coordinates and does the translation and scaling.
void setupMaterial(QgsAbstractMaterialSettings *material)
Sets the material parameters (diffuse color, shininess...) from phong material.
QString saveMaterial(QTextStream &mtlOut, const QString &folder)
saves the texture of the object and material information
void setupLine(const QVector< uint > &facesIndexes)
sets line vertex indexes
void setupFaces(const QVector< uint > &facesIndexes)
Sets the faces in facesIndexes to the faces in the object.
void setupTextureCoordinates(const QVector< float > &texturesBuffer)
Sets texture coordinates for each vertex.
virtual QMap< QString, QString > toExportParameters() const =0
Returns the parameters to be exported to .mtl file.
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
void insertIndexData(QVector< uint > &vertexIndex, const QVector< T > &faceIndex)
#define QgsDebugError(str)
Definition: qgslogger.h:38