QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
Viewer3DTerrainGeometry.cc
Go to the documentation of this file.
2
3#include "QGCGeo.h"
5#include "SettingsManager.h"
6#include "Viewer3DSettings.h"
7
8#include <cmath>
9
10QGC_LOGGING_CATEGORY(Viewer3DTerrainGeometryLog, "Viewer3d.Viewer3DTerrainGeometry")
11
12static constexpr double kMaxLatitude = 85.05112878;
13
15{
16 auto *viewer3DSettings = SettingsManager::instance()->viewer3DSettings();
17 connect(viewer3DSettings->osmFilePath(), &Fact::rawValueChanged, this, &Viewer3DTerrainGeometry::_clearScene);
19}
20
22{
23 clear();
24 const int stride = 3 * sizeof(float)
25 + 3 * sizeof(float) // normals
26 + 2 * sizeof(float); // UV
27
28 if (!_buildTerrain(roiMin(), roiMax(), refCoordinate(), true)) {
29 qCDebug(Viewer3DTerrainGeometryLog) << "buildTerrain returned false (sector/stack count likely 0)";
30 return;
31 }
32
33 QByteArray vertexData;
34 vertexData.resize(_vertices.size() * stride);
35 float *p = reinterpret_cast<float *>(vertexData.data());
36
37 for (size_t i = 0; i < _vertices.size(); ++i) {
38 *p++ = _vertices[i].x();
39 *p++ = _vertices[i].y();
40 *p++ = _vertices[i].z();
41
42 *p++ = _normals[i].x();
43 *p++ = _normals[i].y();
44 *p++ = _normals[i].z();
45
46 *p++ = _texCoords[i].x();
47 *p++ = _texCoords[i].y();
48 }
49
50 qCDebug(Viewer3DTerrainGeometryLog) << "Terrain built:" << _vertices.size() << "vertices," << _sectorCount << "sectors," << _stackCount << "stacks";
51
52 setVertexData(vertexData);
53 setStride(stride);
54
55 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
56 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
57 0,
58 QQuick3DGeometry::Attribute::F32Type);
59 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
60 3 * sizeof(float),
61 QQuick3DGeometry::Attribute::F32Type);
62 addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic,
63 6 * sizeof(float),
64 QQuick3DGeometry::Attribute::F32Type);
65
66 update();
67}
68
69QVector3D Viewer3DTerrainGeometry::_computeFaceNormal(const QVector3D &x1, const QVector3D &x2, const QVector3D &x3)
70{
71 constexpr float EPSILON = 0.000001f;
72
73 QVector3D normal(0, 0, 0);
74
75 const float ex1 = x2.x() - x1.x();
76 const float ey1 = x2.y() - x1.y();
77 const float ez1 = x2.z() - x1.z();
78 const float ex2 = x3.x() - x1.x();
79 const float ey2 = x3.y() - x1.y();
80 const float ez2 = x3.z() - x1.z();
81
82 const float nx = ey1 * ez2 - ez1 * ey2;
83 const float ny = ez1 * ex2 - ex1 * ez2;
84 const float nz = ex1 * ey2 - ey1 * ex2;
85
86 const float length = std::sqrt(nx * nx + ny * ny + nz * nz);
87 if (length > EPSILON) {
88 const float lengthInv = 1.0f / length;
89 normal.setX(nx * lengthInv);
90 normal.setY(ny * lengthInv);
91 normal.setZ(nz * lengthInv);
92 }
93
94 return normal;
95}
96
97void Viewer3DTerrainGeometry::_clearScene()
98{
99 clear();
101 setStackCount(0);
102 _vertices.clear();
103 _normals.clear();
104 _texCoords.clear();
105 update();
106}
107
109{
110 if (_sectorCount == newSectorCount) {
111 return;
112 }
113 _sectorCount = newSectorCount;
114 emit sectorCountChanged();
115}
116
118{
119 if (_stackCount == newStackCount) {
120 return;
121 }
122 _stackCount = newStackCount;
123 emit stackCountChanged();
124}
125
126bool Viewer3DTerrainGeometry::_buildTerrain(const QGeoCoordinate &roiMinCoordinate, const QGeoCoordinate &roiMaxCoordinate, const QGeoCoordinate &refCoordinate, bool scale)
127{
128 if (_sectorCount == 0 || _stackCount == 0) {
129 return false;
130 }
131
132 const float sectorLength = std::abs(roiMaxCoordinate.longitude() - roiMinCoordinate.longitude());
133 const float stackLength = std::abs(roiMaxCoordinate.latitude() - roiMinCoordinate.latitude());
134 const float stackRef = roiMaxCoordinate.latitude();
135 const float sectorRef = roiMinCoordinate.longitude();
136
137 struct Vertex {
138 float x, y, z, s, t;
139 };
140 std::vector<Vertex> tmpVertices;
141
142 float minT = 10;
143 float maxT = -10;
144 float minS = 10;
145 float maxS = -10;
146
147 // Resolution of each polygon changes by the portion
148 const float sectorStep = sectorLength / _sectorCount;
149 const float stackStep = stackLength / _stackCount;
150 float sectorAngle, stackAngle;
151
152 for (int i = 0; i <= _stackCount; ++i) {
153 stackAngle = stackRef - i * stackStep;
154
155 for (int j = 0; j <= _sectorCount; ++j) {
156 sectorAngle = sectorRef + j * sectorStep;
157
158 Vertex vertex;
159 const QVector3D localPoint = QGCGeo::convertGpsToEnu(QGeoCoordinate(stackAngle, sectorAngle, 0), refCoordinate);
160 vertex.x = localPoint.x();
161 vertex.y = localPoint.y();
162 vertex.z = 0;
163
164 vertex.s = (sectorAngle + 180.0f) / 360.0f;
165 minS = std::fmin(minS, vertex.s);
166 maxS = std::fmax(maxS, vertex.s);
167
168 if (std::abs(stackAngle) < kMaxLatitude) {
169 const double sinLatitude = std::sin(qDegreesToRadians(stackAngle));
170 vertex.t = 0.5 - std::log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * M_PI);
171 } else {
172 vertex.t = (stackRef - stackAngle) / 180;
173 }
174 minT = std::fmin(minT, vertex.t);
175 maxT = std::fmax(maxT, vertex.t);
176
177 tmpVertices.push_back(vertex);
178 }
179 }
180
181 const float scaleT = maxT - minT;
182 const float scaleS = maxS - minS;
183 _vertices.clear();
184 _texCoords.clear();
185 _normals.clear();
186
187 Vertex v1, v2, v3, v4;
188 int vi1, vi2;
189 QVector3D n;
190
191 for (int i = 0; i < _stackCount; ++i) {
192 vi1 = i * (_sectorCount + 1);
193 vi2 = (i + 1) * (_sectorCount + 1);
194 stackAngle = stackRef - i * stackStep;
195
196 for (int j = 0; j < _sectorCount; ++j, ++vi1, ++vi2) {
197 v1 = tmpVertices[vi1];
198 v2 = tmpVertices[vi2];
199 v3 = tmpVertices[vi1 + 1];
200 v4 = tmpVertices[vi2 + 1];
201
202 if (scale) {
203 v1.s = (v1.s - minS) / scaleS;
204 v1.t = (v1.t - minT) / scaleT;
205 v2.s = (v2.s - minS) / scaleS;
206 v2.t = (v2.t - minT) / scaleT;
207 v3.s = (v3.s - minS) / scaleS;
208 v3.t = (v3.t - minT) / scaleT;
209 v4.s = (v4.s - minS) / scaleS;
210 v4.t = (v4.t - minT) / scaleT;
211 }
212
213 if (stackAngle < 90) {
214 _vertices.push_back(QVector3D(v1.x, v1.y, v1.z));
215 _vertices.push_back(QVector3D(v2.x, v2.y, v2.z));
216 _vertices.push_back(QVector3D(v3.x, v3.y, v3.z));
217
218 _texCoords.push_back(QVector2D(v1.s, v1.t));
219 _texCoords.push_back(QVector2D(v2.s, v2.t));
220 _texCoords.push_back(QVector2D(v3.s, v3.t));
221
222 n = _computeFaceNormal(QVector3D(v1.x, v1.y, v1.z),
223 QVector3D(v2.x, v2.y, v2.z),
224 QVector3D(v3.x, v3.y, v3.z));
225
226 for (int k = 0; k < 3; ++k) {
227 _normals.push_back(n);
228 }
229 }
230
231 if (stackAngle > -90) {
232 _vertices.push_back(QVector3D(v3.x, v3.y, v3.z));
233 _vertices.push_back(QVector3D(v2.x, v2.y, v2.z));
234 _vertices.push_back(QVector3D(v4.x, v4.y, v4.z));
235
236 _texCoords.push_back(QVector2D(v3.s, v3.t));
237 _texCoords.push_back(QVector2D(v2.s, v2.t));
238 _texCoords.push_back(QVector2D(v4.s, v4.t));
239
240 n = _computeFaceNormal(QVector3D(v3.x, v3.y, v3.z),
241 QVector3D(v2.x, v2.y, v2.z),
242 QVector3D(v4.x, v4.y, v4.z));
243
244 for (int k = 0; k < 3; ++k) {
245 _normals.push_back(n);
246 }
247 }
248 }
249 }
250
251 return true;
252}
253
254void Viewer3DTerrainGeometry::setRoiMin(const QGeoCoordinate &newRoiMin)
255{
256 if (_roiMin == newRoiMin) {
257 return;
258 }
259 _roiMin = newRoiMin;
260 emit roiMinChanged();
261}
262
263void Viewer3DTerrainGeometry::setRoiMax(const QGeoCoordinate &newRoiMax)
264{
265 if (_roiMax == newRoiMax) {
266 return;
267 }
268 _roiMax = newRoiMax;
269 emit roiMaxChanged();
270}
271
272void Viewer3DTerrainGeometry::setRefCoordinate(const QGeoCoordinate &newRefCoordinate)
273{
274 if (_refCoordinate == newRefCoordinate) {
275 return;
276 }
277 _refCoordinate = newRefCoordinate;
279}
Geographic coordinate conversion utilities using GeographicLib.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr double kMaxLatitude
void rawValueChanged(const QVariant &value)
void setRoiMax(const QGeoCoordinate &newRoiMax)
void setRoiMin(const QGeoCoordinate &newRoiMin)
QGeoCoordinate roiMin() const
void setStackCount(int newStackCount)
QGeoCoordinate roiMax() const
QGeoCoordinate refCoordinate() const
void setSectorCount(int newSectorCount)
void setRefCoordinate(const QGeoCoordinate &newRefCoordinate)
QVector3D convertGpsToEnu(const QGeoCoordinate &coord, const QGeoCoordinate &ref)
Definition QGCGeo.cc:80