QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 <QDebug>
20 #include <QDir>
21 #include <QImage>
22 
23 #include <Qt3DRender/QAttribute>
24 #include <Qt3DRender/QBuffer>
25 #include <Qt3DRender/QBufferDataGenerator>
26 #include <Qt3DRender/QBufferDataGeneratorPtr>
27 
28 #include "qgslogger.h"
30 
31 
32 template<typename T>
33 void insertIndexData( QVector<uint> &vertexIndex, const QVector<T> &faceIndex )
34 {
35  for ( int i = 0; i < faceIndex.size(); i += 3 )
36  {
37  if ( i + 2 >= faceIndex.size() ) continue;
38  // skip invalid triangles
39  if ( faceIndex[i] == faceIndex[i + 1] || faceIndex[i + 1] == faceIndex[i + 2] || faceIndex[i] == faceIndex[i + 2] )
40  continue;
41  for ( int j = 0; j < 3; ++j )
42  vertexIndex << faceIndex[i + j] + 1;
43  }
44 }
45 
46 void Qgs3DExportObject::setupPositionCoordinates( const QVector<float> &positionsBuffer, float scale, const QVector3D &translation )
47 {
48  for ( int i = 0; i < positionsBuffer.size(); i += 3 )
49  {
50  for ( int j = 0; j < 3; ++j )
51  {
52  mVertexPosition << positionsBuffer[i + j] * scale + translation[j];
53  }
54  }
55 }
56 
57 void Qgs3DExportObject::setupFaces( const QVector<uint> &facesIndexes )
58 {
59  insertIndexData<uint>( mIndexes, facesIndexes );
60 }
61 
62 void Qgs3DExportObject::setupLine( const QVector<uint> &lineIndexes )
63 {
64  Q_UNUSED( lineIndexes );
65  for ( int i = 0; i < mVertexPosition.size(); i += 3 ) mIndexes << i / 3 + 1;
66 }
67 
68 void Qgs3DExportObject::setupNormalCoordinates( const QVector<float> &normalsBuffer )
69 {
70  mNormals << normalsBuffer;
71 }
72 
73 void Qgs3DExportObject::setupTextureCoordinates( const QVector<float> &texturesBuffer )
74 {
75  mTexturesUV << texturesBuffer;
76 }
77 
79 {
80  QMap<QString, QString> parameters = material->toExportParameters();
81  for ( auto it = parameters.begin(); it != parameters.end(); ++it )
82  {
83  setMaterialParameter( it.key(), it.value() );
84  }
85 }
86 
87 void Qgs3DExportObject::objectBounds( float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ )
88 {
89  if ( mType != TriangularFaces ) return;
90  for ( unsigned int vertice : mIndexes )
91  {
92  int heightIndex = ( vertice - 1 ) * 3 + 1;
93  minX = std::min( minX, mVertexPosition[heightIndex - 1] );
94  maxX = std::max( maxX, mVertexPosition[heightIndex - 1] );
95  minY = std::min( minY, mVertexPosition[heightIndex] );
96  maxY = std::max( maxY, mVertexPosition[heightIndex] );
97  minZ = std::min( minZ, mVertexPosition[heightIndex + 1] );
98  maxZ = std::max( maxZ, mVertexPosition[heightIndex + 1] );
99  }
100 }
101 
102 void Qgs3DExportObject::saveTo( QTextStream &out, float scale, const QVector3D &center )
103 {
104  // Set groups
105  // turns out grouping doest work as expected in blender
106 
107  // smoothen edges
108  if ( mSmoothEdges )
109  out << "s on\n";
110  else
111  out << "s off\n";
112 
113  // Construct vertices
114  for ( int i = 0; i < mVertexPosition.size(); i += 3 )
115  {
116  // for now just ignore wrong vertex positions
117  out << "v ";
118  out << ( mVertexPosition[i] - center.x() ) / scale << " ";
119  out << ( mVertexPosition[i + 1] - center.y() ) / scale << " ";
120  out << ( mVertexPosition[i + 2] - center.z() ) / scale << "\n";
121  if ( i + 3 <= mNormals.size() )
122  {
123  out << "vn " << mNormals[i] << " " << mNormals[i + 1] << " " << mNormals[i + 2] << "\n";
124  }
125  int u_index = i / 3 * 2;
126  if ( u_index + 1 < mTexturesUV.size() )
127  {
128  // TODO: flip texture in a more appropriate way (for repeated textures)
129  out << "vt " << mTexturesUV[u_index] << " " << 1.0f - mTexturesUV[u_index + 1] << "\n";
130  }
131  }
132 
133  bool hasTextures = mTexturesUV.size() == mVertexPosition.size() / 3 * 2;
134  // if the object has normals then the normals and positions buffers should be the same size
135  bool hasNormals = mNormals.size() == mVertexPosition.size();
136 
137  if ( !hasNormals && !mNormals.empty() )
138  {
139  QgsDebugMsg( "Vertex normals count and vertex positions count are different" );
140  }
141  int verticesCount = mVertexPosition.size() / 3;
142 
143  auto getVertexIndex = [&]( int i ) -> QString
144  {
145  int negativeIndex = -1 - ( verticesCount - i );
146  if ( hasNormals && !hasTextures )
147  return QStringLiteral( "%1//%2" ).arg( negativeIndex ).arg( negativeIndex );
148  if ( !hasNormals && hasTextures )
149  return QStringLiteral( "%1/%2" ).arg( negativeIndex ).arg( negativeIndex );
150  if ( hasNormals && hasTextures )
151  return QStringLiteral( "%1/%2/%3" ).arg( negativeIndex ).arg( negativeIndex ).arg( negativeIndex );
152  return QString::number( negativeIndex );
153  };
154 
155  if ( mType == TriangularFaces )
156  {
157  // Construct triangular faces
158  for ( int i = 0; i < mIndexes.size(); i += 3 )
159  {
160  if ( mIndexes[i] == mIndexes[i + 1] && mIndexes[i + 1] == mIndexes[i + 2] )
161  continue;
162  out << "f " << getVertexIndex( mIndexes[i] );
163  out << " " << getVertexIndex( mIndexes[i + 1] );
164  out << " " << getVertexIndex( mIndexes[i + 2] );
165  out << "\n";
166  }
167  }
168  else if ( mType == LineStrip )
169  {
170  out << "l";
171  for ( int i : mIndexes ) out << " " << getVertexIndex( i );
172  out << "\n";
173  }
174  else if ( mType == Points )
175  {
176  out << "p";
177  for ( int i = 0; i < mVertexPosition.size(); i += 3 )
178  out << " " << getVertexIndex( i / 3 + 1 );
179  out << "\n";
180  }
181 }
182 
183 QString Qgs3DExportObject::saveMaterial( QTextStream &mtlOut, const QString &folderPath )
184 {
185  QString materialName = mName + "_material";
186  if ( mMaterialParameters.size() == 0 && ( mTexturesUV.size() == 0 || mTextureImage.isNull() ) ) return QString();
187  mtlOut << "newmtl " << materialName << "\n";
188  if ( mTexturesUV.size() != 0 && !mTextureImage.isNull() )
189  {
190  QString filePath = QDir( folderPath ).filePath( materialName + ".jpg" );
191  mTextureImage.save( filePath, "JPG" );
192  mtlOut << "\tmap_Kd " << materialName << ".jpg" << "\n";
193  }
194  for ( QString key : mMaterialParameters.keys() )
195  {
196  mtlOut << "\t" << key << " " << mMaterialParameters[key] << "\n";
197  }
198  mtlOut << "\tillum 2\n";
199  return materialName;
200 }
Qgs3DExportObject::TriangularFaces
@ TriangularFaces
Definition: qgs3dexportobject.h:47
qgs3dexportobject.h
Qgs3DExportObject::setupMaterial
void setupMaterial(QgsAbstractMaterialSettings *material)
Sets the material parameters (diffuse color, shininess...) from phong material.
Definition: qgs3dexportobject.cpp:78
Qgs3DExportObject::setupTextureCoordinates
void setupTextureCoordinates(const QVector< float > &texturesBuffer)
Sets texture coordinates for each vertex.
Definition: qgs3dexportobject.cpp:73
QgsAbstractMaterialSettings::toExportParameters
virtual QMap< QString, QString > toExportParameters() const =0
Returns the parameters to be exported to .mtl file.
Qgs3DExportObject::setupNormalCoordinates
void setupNormalCoordinates(const QVector< float > &normalsBuffer)
Sets normal coordinates for each vertex.
Definition: qgs3dexportobject.cpp:68
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Qgs3DExportObject::objectBounds
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 ...
Definition: qgs3dexportobject.cpp:87
QgsAbstractMaterialSettings
3 Abstract base class for material settings.
Definition: qgsabstractmaterialsettings.h:105
Qgs3DExportObject::Points
@ Points
Definition: qgs3dexportobject.h:49
Qgs3DExportObject::setupFaces
void setupFaces(const QVector< uint > &facesIndexes)
Sets the faces in facesIndexes to the faces in the object.
Definition: qgs3dexportobject.cpp:57
insertIndexData
void insertIndexData(QVector< uint > &vertexIndex, const QVector< T > &faceIndex)
Definition: qgs3dexportobject.cpp:33
Qgs3DExportObject::setupLine
void setupLine(const QVector< uint > &facesIndexes)
sets line vertex indexes
Definition: qgs3dexportobject.cpp:62
Qgs3DExportObject::LineStrip
@ LineStrip
Definition: qgs3dexportobject.h:48
qgsphongmaterialsettings.h
qgslogger.h
Qgs3DExportObject::saveMaterial
QString saveMaterial(QTextStream &mtlOut, const QString &folder)
saves the texture of the object and material information
Definition: qgs3dexportobject.cpp:183
Qgs3DExportObject::setupPositionCoordinates
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.
Definition: qgs3dexportobject.cpp:46
Qgs3DExportObject::setMaterialParameter
void setMaterialParameter(const QString &parameter, const QString &value)
Sets a material parameter to be exported in the .mtl file.
Definition: qgs3dexportobject.h:104
Qgs3DExportObject::saveTo
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...
Definition: qgs3dexportobject.cpp:102