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