3#include <QtGui/QVector3D>
20 QQuick3DGeometry::componentComplete();
24void PatchGeometry::_requestRebuild()
26 if (isComponentComplete()) {
34 if (clamped == _gridSize) {
38 for (
const int delta : _lodDelta) {
39 if ((delta > 0) && ((_gridSize % (1 << delta)) != 0)) {
40 qCWarning(GeoMapPatchGeometryLog) <<
"setGridSize reset edge LOD deltas: delta" << delta
41 <<
"has no coincident vertices at gridSize" << _gridSize;
53 if (qFuzzyCompare(
span, _span) || (
span <= 0.0)) {
74 qCWarning(GeoMapPatchGeometryLog) <<
"sampleFromField rejected: no height field set, key" << key;
77 const QList<float> sampled = _heightField->
samplePatch(key, _gridSize);
78 if (sampled.isEmpty()) {
79 qCWarning(GeoMapPatchGeometryLog)
80 <<
"sampleFromField rejected: field returned no samples, key" << key <<
"gridSize" << _gridSize;
83 if (sampled != _heights) {
97 disconnect(_heightField,
nullptr,
this,
nullptr);
103 connect(_heightField, &QObject::destroyed,
this, [
this] {
104 _heightField =
nullptr;
114 static_assert(std::has_single_bit(
static_cast<unsigned>(
kMaxGridSize)),
"kMaxLodDelta assumes a power of two");
115 constexpr int kMaxLodDelta = std::countr_zero(
static_cast<unsigned>(
kMaxGridSize));
116 for (
const int delta : {north, south, west, east}) {
117 if ((delta < 0) || (delta > kMaxLodDelta) || ((delta > 0) && ((_gridSize % (1 << delta)) != 0))) {
118 qCWarning(GeoMapPatchGeometryLog)
119 <<
"setEdgeLodDeltas rejected: delta" << delta <<
"has no coincident vertices at gridSize" << _gridSize;
123 if ((north == _lodDelta[kNorth]) && (south == _lodDelta[kSouth]) && (west == _lodDelta[kWest]) &&
124 (east == _lodDelta[kEast])) {
127 _lodDelta[kNorth] = north;
128 _lodDelta[kSouth] = south;
129 _lodDelta[kWest] = west;
130 _lodDelta[kEast] = east;
137 if (deltas.count() != kEdgeCount) {
138 qCWarning(GeoMapPatchGeometryLog)
139 <<
"setEdgeLodDeltas rejected: expected {N,S,W,E}, got" << deltas.count() <<
"deltas";
145float PatchGeometry::_rawHeightAt(
int row,
int col)
const
147 const int verticesPerEdge = _gridSize + 1;
148 if (_heights.count() != (verticesPerEdge * verticesPerEdge)) {
151 return _heights.at((row * verticesPerEdge) + col);
154float PatchGeometry::_heightAt(
int row,
int col)
const
156 const int r = std::clamp(row, 0, _gridSize);
157 const int c = std::clamp(col, 0, _gridSize);
168 bool matched =
false;
169 bool alongCol =
false;
170 if ((r == 0) && (_lodDelta[kNorth] > 0)) {
174 }
else if ((r == _gridSize) && (_lodDelta[kSouth] > 0)) {
178 }
else if ((c == 0) && (_lodDelta[kWest] > 0)) {
181 }
else if ((c == _gridSize) && (_lodDelta[kEast] > 0)) {
186 const int step = 1 << _lodDelta[edge];
187 const int idx = alongCol ? c : r;
188 const int base = (idx / step) * step;
190 return _rawHeightAt(r, c);
192 const float t = float(idx - base) / step;
193 const float a = alongCol ? _rawHeightAt(r, base) : _rawHeightAt(base, c);
194 const float b = alongCol ? _rawHeightAt(r, base + step) : _rawHeightAt(base + step, c);
195 return a + ((b - a) * t);
197 return _rawHeightAt(r, c);
200void PatchGeometry::_rebuild()
202 const int verticesPerEdge = _gridSize + 1;
203 const int gridVertexCount = verticesPerEdge * verticesPerEdge;
204 const int skirtVertexCount = 4 * verticesPerEdge;
205 const int vertexCount = gridVertexCount + skirtVertexCount;
207 const float span =
static_cast<float>(_span);
208 const float half =
span / 2.0f;
209 const float step =
span / _gridSize;
216 const int maxDelta = *std::max_element(_lodDelta.cbegin(), _lodDelta.cend());
220 constexpr int kFloatsPerVertex = 8;
221 constexpr int kStride = kFloatsPerVertex *
sizeof(float);
223 QByteArray vertexData(vertexCount * kStride, Qt::Uninitialized);
224 float* v =
reinterpret_cast<float*
>(vertexData.data());
229 const auto writeVertex = [&](
float x,
float y,
float z,
const QVector3D& normal,
float u,
float texV) {
238 minZ = std::min(minZ, z);
239 maxZ = std::max(maxZ, z);
243 const auto normalAt = [&](
int row,
int col) {
244 const float dzdx = (_heightAt(row, col + 1) - _heightAt(row, col - 1)) / (2.0f * step);
246 (_heightAt(row - 1, col) - _heightAt(row + 1, col)) / (2.0f * step);
247 QVector3D normal(-dzdx, -dzdy, 1.0f);
253 for (
int row = 0; row < verticesPerEdge; row++) {
254 const float y = half - (row * step);
255 const float texV =
static_cast<float>(row) / _gridSize;
256 for (
int col = 0; col < verticesPerEdge; col++) {
257 const float x = -half + (col * step);
258 const float u =
static_cast<float>(col) / _gridSize;
259 writeVertex(x, y, _heightAt(row, col), normalAt(row, col), u, texV);
265 const auto writeSkirtVertex = [&](
int row,
int col) {
266 const float x = -half + (col * step);
267 const float y = half - (row * step);
268 const float u =
static_cast<float>(col) / _gridSize;
269 const float texV =
static_cast<float>(row) / _gridSize;
270 writeVertex(x, y, _heightAt(row, col) - skirtDepth, normalAt(row, col), u, texV);
272 for (
int col = 0; col < verticesPerEdge; col++) {
273 writeSkirtVertex(0, col);
275 for (
int col = 0; col < verticesPerEdge; col++) {
276 writeSkirtVertex(_gridSize, col);
278 for (
int row = 0; row < verticesPerEdge; row++) {
279 writeSkirtVertex(row, 0);
281 for (
int row = 0; row < verticesPerEdge; row++) {
282 writeSkirtVertex(row, _gridSize);
286 const int gridIndexCount = _gridSize * _gridSize * 6;
287 const int skirtIndexCount = 4 * _gridSize * 6;
288 QByteArray indexData((gridIndexCount + skirtIndexCount) *
sizeof(quint32), Qt::Uninitialized);
289 quint32* idx =
reinterpret_cast<quint32*
>(indexData.data());
291 const auto gridIndex = [&](
int row,
int col) {
return static_cast<quint32
>((row * verticesPerEdge) + col); };
292 for (
int row = 0; row < _gridSize; row++) {
293 for (
int col = 0; col < _gridSize; col++) {
294 *idx++ = gridIndex(row, col);
295 *idx++ = gridIndex(row + 1, col);
296 *idx++ = gridIndex(row, col + 1);
297 *idx++ = gridIndex(row, col + 1);
298 *idx++ = gridIndex(row + 1, col);
299 *idx++ = gridIndex(row + 1, col + 1);
305 const auto writeSkirtQuads = [&](quint32 skirtBase,
const auto& topIndex) {
306 for (
int i = 0; i < _gridSize; i++) {
307 const quint32 topA = topIndex(i);
308 const quint32 topB = topIndex(i + 1);
309 const quint32 skirtA = skirtBase + i;
310 const quint32 skirtB = skirtBase + i + 1;
319 const quint32 skirtStart =
static_cast<quint32
>(gridVertexCount);
320 writeSkirtQuads(skirtStart, [&](
int i) {
return gridIndex(0, i); });
321 writeSkirtQuads(skirtStart + verticesPerEdge, [&](
int i) {
return gridIndex(_gridSize, i); });
322 writeSkirtQuads(skirtStart + (2 * verticesPerEdge), [&](
int i) {
return gridIndex(i, 0); });
323 writeSkirtQuads(skirtStart + (3 * verticesPerEdge), [&](
int i) {
return gridIndex(i, _gridSize); });
327 setVertexData(vertexData);
328 setIndexData(indexData);
329 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
330 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
331 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 3 *
sizeof(
float), QQuick3DGeometry::Attribute::F32Type);
332 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, 6 *
sizeof(
float),
333 QQuick3DGeometry::Attribute::F32Type);
334 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::U32Type);
335 setBounds(QVector3D(-half, -half, minZ), QVector3D(half, half, maxZ));
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QList< float > samplePatch(const TileMath::TileKey &key, int gridSize) const
static constexpr int kMinGridSize
void setEdgeLodDeltas(int north, int south, int west, int east)
void setHeightField(HeightField *heightField)
HeightField * heightField() const
Field for sampleFromField to sample from; not owned, may be null.
QList< float > heights() const
(gridSize+1)^2 heights row-major from the north-west corner; empty = flat
void componentComplete() override
bool sampleFromField(const TileMath::TileKey &key)
static constexpr int kMaxGridSize
static constexpr double kSkirtDepthFraction
base skirt depth (fraction of span); scaled by 2^maxEdgeDelta
void setGridSize(int gridSize)
void setHeights(const QList< float > &heights)
void edgeLodDeltasChanged()
void heightFieldChanged()