QGIS API Documentation 4.3.0-Master (d3b565c628d)
Loading...
Searching...
No Matches
qgsalgorithmb3dmtogltf.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmb3dmtogltf.cpp
3 ---------------------
4 begin : August 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgscesiumutils.h"
21#include "qgsgltfutils.h"
22
23#include <QString>
24
25using namespace Qt::StringLiterals;
26
27#define TINYGLTF_IMPLEMENTATION
28#define TINYGLTF_NO_STB_IMAGE // we use QImage-based reading of images
29#define TINYGLTF_NO_STB_IMAGE_WRITE // we don't need writing of images
30
31#include "tiny_gltf.h"
32#include <fstream>
33
35
36QString QgsB3DMToGltfAlgorithm::name() const
37{
38 return u"b3dmtogltf"_s;
39}
40
41QString QgsB3DMToGltfAlgorithm::displayName() const
42{
43 return QObject::tr( "Convert B3DM to GLTF" );
44}
45
46QStringList QgsB3DMToGltfAlgorithm::tags() const
47{
48 return QObject::tr( "3d,tiles,cesium" ).split( ',' );
49}
50
51QString QgsB3DMToGltfAlgorithm::group() const
52{
53 return QObject::tr( "3D Tiles" );
54}
55
56QString QgsB3DMToGltfAlgorithm::groupId() const
57{
58 return u"3dtiles"_s;
59}
60
61QString QgsB3DMToGltfAlgorithm::shortHelpString() const
62{
63 return QObject::tr( "This algorithm converts files from the legacy B3DM format to GLTF." );
64}
65
66QString QgsB3DMToGltfAlgorithm::shortDescription() const
67{
68 return QObject::tr( "Converts files from the legacy B3DM format to GLTF." );
69}
70
71QgsB3DMToGltfAlgorithm *QgsB3DMToGltfAlgorithm::createInstance() const
72{
73 return new QgsB3DMToGltfAlgorithm();
74}
75
76void QgsB3DMToGltfAlgorithm::initAlgorithm( const QVariantMap & )
77{
78 addParameter( new QgsProcessingParameterFile( u"INPUT"_s, QObject::tr( "Input B3DM" ), Qgis::ProcessingFileParameterBehavior::File, u"b3dm"_s, QVariant(), false, u"B3DM (*.b3dm *.B3DM)"_s ) );
79
80 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT"_s, QObject::tr( "Output file" ), u"GLTF (*.gltf *.GLTF);;GLB (*.glb *.GLB)"_s ) );
81}
82
83QVariantMap QgsB3DMToGltfAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
84{
85 QGS_MARK_ALGORITHM_SOURCE
86
87 const QString path = parameterAsFile( parameters, u"INPUT"_s, context );
88 const QString outputPath = parameterAsFile( parameters, u"OUTPUT"_s, context );
89
90 if ( !QFile::exists( path ) )
91 throw QgsProcessingException( QObject::tr( "Could not load source file %1." ).arg( path ) );
92
93 QFile f( path );
94 QByteArray fileContent;
95 if ( f.open( QIODevice::ReadOnly ) )
96 {
97 fileContent = f.readAll();
98 }
99 else
100 {
101 throw QgsProcessingException( QObject::tr( "Could not load source file %1." ).arg( path ) );
102 }
103
104 const QgsCesiumUtils::B3DMContents b3dmContent = QgsCesiumUtils::extractGltfFromB3dm( fileContent );
105
106 // load gltf and then rewrite -- this allows us to both validate the B3DM GLTF content, and
107 // also gives the opportunity to "upgrade" B3DM specific options (like CESIUM_RTC) to standard
108 // GLTF extensions
109 tinygltf::Model model;
110 QString errors;
111 QString warnings;
112 const bool res = QgsGltfUtils::loadGltfModel( b3dmContent.gltf, model, &errors, &warnings );
113 if ( !errors.isEmpty() )
114 {
115 feedback->reportError( errors );
116 }
117 if ( !warnings.isEmpty() )
118 {
119 feedback->pushWarning( warnings );
120 }
121 if ( !res )
122 {
123 // if we can't read the GLTF, then just write the original GLTF content to the output file
124 QFile outputFile( outputPath );
125 if ( !outputFile.open( QFile::WriteOnly ) )
126 {
127 throw QgsProcessingException( QObject::tr( "Could not create destination file %1." ).arg( outputPath ) );
128 }
129 outputFile.write( b3dmContent.gltf );
130 }
131 else
132 {
133 bool sceneOk = false;
134 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
135 if ( !sceneOk )
136 {
137 throw QgsProcessingException( QObject::tr( "No scenes found in model" ) );
138 }
139
140 feedback->pushDebugInfo( QObject::tr( "Found %1 scenes" ).arg( model.scenes.size() ) );
141
142 const tinygltf::Scene &scene = model.scenes[sceneIndex];
143 feedback->pushDebugInfo( QObject::tr( "Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
144 if ( !scene.nodes.empty() )
145 {
146 const int nodeIndex = scene.nodes[0];
147 const tinygltf::Node &gltfNode = model.nodes[nodeIndex];
148 if ( gltfNode.mesh >= 0 )
149 {
150 const tinygltf::Mesh &mesh = model.meshes[gltfNode.mesh];
151 feedback->pushDebugInfo( QObject::tr( "Found %1 primitives in default scene node [%2]" ).arg( mesh.primitives.size() ).arg( nodeIndex ) );
152 }
153 }
154
155 if ( !b3dmContent.rtcCenter.isNull() )
156 {
157 // transfer B3DM RTC center to GLTF CESIUM_RTC extension
158 tinygltf::Value::Object cesiumRtc;
159 cesiumRtc["center"] = tinygltf::Value(
160 tinygltf::Value::Array { tinygltf::Value( b3dmContent.rtcCenter.x() ), tinygltf::Value( b3dmContent.rtcCenter.y() ), tinygltf::Value( b3dmContent.rtcCenter.z() ) }
161 );
162
163 model.extensions["CESIUM_RTC"] = tinygltf::Value( cesiumRtc );
164 model.extensionsRequired.emplace_back( "CESIUM_RTC" );
165 model.extensionsUsed.emplace_back( "CESIUM_RTC" );
166 }
167
168 const QString outputExtension = QFileInfo( outputPath ).suffix();
169 const bool isGlb = outputExtension.compare( "glb"_L1, Qt::CaseInsensitive ) == 0;
170 const QByteArray outputFile = QFile::encodeName( outputPath );
171 std::ofstream of( outputFile.constData(), std::ios::binary | std::ios::trunc );
172 if ( !of )
173 throw QgsProcessingException( QObject::tr( "Could not create destination file %1." ).arg( outputPath ) );
174
175 tinygltf::TinyGLTF writer;
176 if ( !writer.WriteGltfSceneToStream( &model, of, true, isGlb ) )
177 {
178 of.close();
179 throw QgsProcessingException( QObject::tr( "Could not write GLTF model to %1." ).arg( outputPath ) );
180 }
181 of.close();
182 }
183
184 QVariantMap outputs;
185 outputs.insert( u"OUTPUT"_s, outputPath );
186 return outputs;
187}
188
@ File
Parameter is a single file.
Definition qgis.h:4008
static B3DMContents extractGltfFromB3dm(const QByteArray &tileContent)
Extracts GLTF binary data and other contents from the legacy b3dm (Batched 3D Model) tile format.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
An input file or folder parameter for processing algorithms.
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
bool isNull() const
Returns true if all three coordinates are zero.
Definition qgsvector3d.h:54
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
Encapsulates the contents of a B3DM file.
QByteArray gltf
GLTF binary content.
QgsVector3D rtcCenter
Optional RTC center.