28#include <Qt3DCore/QAttribute>
29#include <Qt3DCore/QBuffer>
31using namespace Qt::StringLiterals;
33void Qgs3DExportObject::setupPositionCoordinates(
const QVector<float> &positionsBuffer,
const QMatrix4x4 &transform )
35 mVertexPosition.clear();
36 for (
int i = 0; i < positionsBuffer.size(); i += 3 )
38 const QVector3D position( positionsBuffer[i], positionsBuffer[i + 1], positionsBuffer[i + 2] );
39 const QVector3D positionFinal = transform.map( position );
40 mVertexPosition << positionFinal.x() << positionFinal.y() << positionFinal.z();
47 setupPositionCoordinates( positionsBuffer, transform );
51 for (
int i = 0; i < facesIndexes.size(); i += 3 )
53 if ( i + 2 >= facesIndexes.size() )
56 if ( facesIndexes[i] == facesIndexes[i + 1] || facesIndexes[i + 1] == facesIndexes[i + 2] || facesIndexes[i] == facesIndexes[i + 2] )
58 for (
int j = 0; j < 3; ++j )
59 mIndexes << facesIndexes[i + j];
66 setupPositionCoordinates( positionsBuffer );
70 for (
int i = 0; i < mVertexPosition.size(); i += 3 )
71 mIndexes << i / 3 + 1;
77 setupPositionCoordinates( positionsBuffer );
85 QMatrix3x3 normal3x3 = transform.normalMatrix();
87 QMatrix4x4 normal4x4( normal3x3( 0, 0 ), normal3x3( 0, 1 ), normal3x3( 0, 2 ), 0,
88 normal3x3( 1, 0 ), normal3x3( 1, 1 ), normal3x3( 1, 2 ), 0,
89 normal3x3( 2, 0 ), normal3x3( 2, 1 ), normal3x3( 2, 2 ), 0,
93 for (
int i = 0; i < normalsBuffer.size(); i += 3 )
95 const QVector3D normalVector( normalsBuffer[i], normalsBuffer[i + 1], normalsBuffer[i + 2] );
96 QVector3D v = normal4x4.mapVector( normalVector );
104 mNormals << v.x() << v.y() << v.z();
111 mTexturesUV << texturesBuffer;
116 mMaterialParameters.clear();
118 QMap<QString, QString> parameters;
121 parameters = handler->toExportParameters( material );
124 for (
auto it = parameters.begin(); it != parameters.end(); ++it )
126 mMaterialParameters[it.key()] = it.value();
134 for (
const unsigned int vertice : std::as_const( mIndexes ) )
136 const int heightIndex =
static_cast<int>( vertice ) * 3 + 1;
137 minX = std::min( minX, mVertexPosition[heightIndex - 1] );
138 maxX = std::max( maxX, mVertexPosition[heightIndex - 1] );
139 minY = std::min( minY, mVertexPosition[heightIndex] );
140 maxY = std::max( maxY, mVertexPosition[heightIndex] );
141 minZ = std::min( minZ, mVertexPosition[heightIndex + 1] );
142 maxZ = std::max( maxZ, mVertexPosition[heightIndex + 1] );
148 switch ( exportFormat )
151 saveToObj( out, scale, center, precision, materialName );
154 saveToStl( out, scale, center, precision );
161void Qgs3DExportObject::saveToObj( QTextStream &out,
float scale,
const QVector3D ¢er,
int precision, QString materialName )
const
164 out <<
"o " << mName <<
"\n";
167 if ( !materialName.isEmpty() )
168 out <<
"usemtl " << materialName <<
"\n";
172 out << qSetRealNumberPrecision( precision );
183 for (
const unsigned int vertice : std::as_const( mIndexes ) )
185 const int i =
static_cast<int>( vertice * 3 );
188 out << ( mVertexPosition[i] - center.x() ) / scale <<
" ";
189 out << ( mVertexPosition[i + 1] - center.y() ) / scale <<
" ";
190 out << ( mVertexPosition[i + 2] - center.z() ) / scale <<
"\n";
191 if ( i + 3 <= mNormals.size() )
193 out <<
"vn " << mNormals[i] <<
" " << mNormals[i + 1] <<
" " << mNormals[i + 2] <<
"\n";
195 const int u_index = i / 3 * 2;
196 if ( u_index + 1 < mTexturesUV.size() )
199 out <<
"vt " << mTexturesUV[u_index] <<
" " << 1.0f - mTexturesUV[u_index + 1] <<
"\n";
203 bool hasTextures = mTexturesUV.size() == mVertexPosition.size() / 3 * 2;
205 bool hasNormals = mNormals.size() == mVertexPosition.size();
207 if ( !hasNormals && !mNormals.empty() )
209 QgsDebugError(
"Vertex normals count and vertex positions count are different" );
211 const unsigned int verticesCount = mIndexes.size();
215 auto getVertexIndex = [&](
unsigned int i ) -> QString {
216 const int negativeIndex =
static_cast<int>( i - verticesCount );
217 if ( hasNormals && !hasTextures )
218 return u
"%1//%2"_s.arg( negativeIndex ).arg( negativeIndex );
219 if ( !hasNormals && hasTextures )
220 return u
"%1/%2"_s.arg( negativeIndex ).arg( negativeIndex );
221 if ( hasNormals && hasTextures )
222 return u
"%1/%2/%3"_s.arg( negativeIndex ).arg( negativeIndex ).arg( negativeIndex );
223 return QString::number( negativeIndex );
231 for (
int i = 0; i < mIndexes.size(); i += 3 )
233 out <<
"f " << getVertexIndex( i );
234 out <<
" " << getVertexIndex( i + 1 );
235 out <<
" " << getVertexIndex( i + 2 );
242 for (
const unsigned int i : std::as_const( mIndexes ) )
243 out <<
" " << getVertexIndex( i );
246 else if ( mType ==
Points )
249 for (
const unsigned int i : std::as_const( mIndexes ) )
250 out <<
" " << getVertexIndex( i );
255void Qgs3DExportObject::saveToStl( QTextStream &out,
float scale,
const QVector3D ¢er,
int precision )
const
259 QgsDebugMsgLevel( u
"Cannot export object %s in %s type. Only triangular type is handled by STL export"_s.arg( mName ).arg( mType ), 3 );
264 out <<
"solid " << mName <<
"\n";
266 out << qSetRealNumberPrecision( precision );
268 for (
int i = 0; i < mIndexes.size(); i += 3 )
271 unsigned int i0 = mIndexes[i] * 3;
272 unsigned int i1 = mIndexes[i + 1] * 3;
273 unsigned int i2 = mIndexes[i + 2] * 3;
275 QVector3D v0( ( mVertexPosition[i0] - center.x() ) / scale, ( mVertexPosition[i0 + 1] - center.y() ) / scale, ( mVertexPosition[i0 + 2] - center.z() ) / scale );
276 QVector3D v1( ( mVertexPosition[i1] - center.x() ) / scale, ( mVertexPosition[i1 + 1] - center.y() ) / scale, ( mVertexPosition[i1 + 2] - center.z() ) / scale );
277 QVector3D v2( ( mVertexPosition[i2] - center.x() ) / scale, ( mVertexPosition[i2 + 1] - center.y() ) / scale, ( mVertexPosition[i2 + 2] - center.z() ) / scale );
279 QVector3D normal = QVector3D::crossProduct( v1 - v0, v2 - v0 ).normalized();
281 out <<
" facet normal " << normal.x() <<
" " << normal.y() <<
" " << normal.z() <<
"\n";
282 out <<
" outer loop\n";
283 out <<
" vertex " << v0.x() <<
" " << v0.y() <<
" " << v0.z() <<
"\n";
284 out <<
" vertex " << v1.x() <<
" " << v1.y() <<
" " << v1.z() <<
"\n";
285 out <<
" vertex " << v2.x() <<
" " << v2.y() <<
" " << v2.z() <<
"\n";
287 out <<
" endfacet\n";
290 out <<
"endsolid " << mName <<
"\n";
295 QString materialName = mName +
"_material";
296 if ( mMaterialParameters.size() == 0 && ( mTexturesUV.size() == 0 || mTextureImage.isNull() ) )
298 mtlOut <<
"newmtl " << materialName <<
"\n";
299 if ( mTexturesUV.size() != 0 && !mTextureImage.isNull() )
301 const QString filePath = QDir( folderPath ).filePath( materialName +
".jpg" );
302 mTextureImage.save( filePath,
"JPG" );
303 mtlOut <<
"\tmap_Kd " << materialName <<
".jpg" <<
"\n";
305 for (
auto it = mMaterialParameters.constBegin(); it != mMaterialParameters.constEnd(); it++ )
307 mtlOut <<
"\t" << it.key() <<
" " << it.value() <<
"\n";
309 mtlOut <<
"\tillum 2\n";
Export3DSceneFormat
The file format used when exporting a 3D scene.
@ StlAscii
STL ascii format.
@ Obj
Wavefront OBJ format.
void setupPoint(const QVector< float > &positionsBuffer)
sets point positions coordinates
QString saveMaterial(QTextStream &mtlOut, const QString &folder) const
saves the texture of the object and material information
void setupMaterial(QgsAbstractMaterialSettings *material)
Sets the material parameters (diffuse color, shininess...) to be exported in the ....
void setupTriangle(const QVector< float > &positionsBuffer, const QVector< uint > &facesIndexes, const QMatrix4x4 &transform)
sets triangle indexes and positions coordinates
void setupNormalCoordinates(const QVector< float > &normalsBuffer, const QMatrix4x4 &transform)
Sets normal coordinates for each vertex.
void objectBounds(float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ) const
Updates the box bounds explained with the current object bounds This expands the bounding box if the ...
void setupLine(const QVector< float > &positionsBuffer)
sets line indexes and positions coordinates
void setupTextureCoordinates(const QVector< float > &texturesBuffer)
Sets texture coordinates for each vertex.
void saveTo(QTextStream &out, float scale, const QVector3D ¢er, const Qgis::Export3DSceneFormat &exportFormat=Qgis::Export3DSceneFormat::Obj, int precision=6, QString materialName=QString()) const
Exports the 3D object to an OBJ or STL compatible output stream.
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
Abstract base class for material 3D handlers.
Abstract base class for material settings.
#define BUILTIN_UNREACHABLE
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference).
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)