6#include <QtGui/QVector3D>
21 QQuick3DGeometry::componentComplete();
25void PatchGeometry::_requestRebuild()
27 if (isComponentComplete()) {
35 if (clamped == _gridSize) {
39 for (
const int delta : _lodDelta) {
40 if ((delta > 0) && ((_gridSize % (1 << delta)) != 0)) {
41 qCWarning(GeoMapPatchGeometryLog) <<
"setGridSize reset edge LOD deltas: delta" << delta
42 <<
"has no coincident vertices at gridSize" << _gridSize;
54 if (qFuzzyCompare(
span, _span) || (
span <= 0.0)) {
75 qCWarning(GeoMapPatchGeometryLog) <<
"sampleFromField rejected: no height field set, key" << key;
78 const QList<float> sampled = _heightField->
samplePatch(key, _gridSize);
79 if (sampled.isEmpty()) {
80 qCWarning(GeoMapPatchGeometryLog)
81 <<
"sampleFromField rejected: field returned no samples, key" << key <<
"gridSize" << _gridSize;
84 if (sampled != _heights) {
98 disconnect(_heightField,
nullptr,
this,
nullptr);
104 connect(_heightField, &QObject::destroyed,
this, [
this] {
105 _heightField =
nullptr;
115 static_assert(std::has_single_bit(
static_cast<unsigned>(
kMaxGridSize)),
"kMaxLodDelta assumes a power of two");
116 constexpr int kMaxLodDelta = std::countr_zero(
static_cast<unsigned>(
kMaxGridSize));
117 for (
const int delta : {north, south, west, east}) {
118 if ((delta < 0) || (delta > kMaxLodDelta) || ((delta > 0) && ((_gridSize % (1 << delta)) != 0))) {
119 qCWarning(GeoMapPatchGeometryLog)
120 <<
"setEdgeLodDeltas rejected: delta" << delta <<
"has no coincident vertices at gridSize" << _gridSize;
124 if ((north == _lodDelta[kNorth]) && (south == _lodDelta[kSouth]) && (west == _lodDelta[kWest]) &&
125 (east == _lodDelta[kEast])) {
128 _lodDelta[kNorth] = north;
129 _lodDelta[kSouth] = south;
130 _lodDelta[kWest] = west;
131 _lodDelta[kEast] = east;
138 if (deltas.count() != kEdgeCount) {
139 qCWarning(GeoMapPatchGeometryLog)
140 <<
"setEdgeLodDeltas rejected: expected {N,S,W,E}, got" << deltas.count() <<
"deltas";
146float PatchGeometry::_heightAt(
int row,
int col)
const
151void PatchGeometry::_rebuild()
153 const int verticesPerEdge = _gridSize + 1;
154 const int gridVertexCount = verticesPerEdge * verticesPerEdge;
155 const int skirtVertexCount = 4 * verticesPerEdge;
156 const int vertexCount = gridVertexCount + skirtVertexCount;
158 const float span =
static_cast<float>(_span);
159 const float half =
span / 2.0f;
160 const float step =
span / _gridSize;
167 const int maxDelta = *std::max_element(_lodDelta.cbegin(), _lodDelta.cend());
171 constexpr int kFloatsPerVertex = 8;
172 constexpr int kStride = kFloatsPerVertex *
sizeof(float);
174 QByteArray vertexData(vertexCount * kStride, Qt::Uninitialized);
175 float* v =
reinterpret_cast<float*
>(vertexData.data());
180 const auto writeVertex = [&](
float x,
float y,
float z,
const QVector3D& normal,
float u,
float texV) {
189 minZ = std::min(minZ, z);
190 maxZ = std::max(maxZ, z);
194 const auto normalAt = [&](
int row,
int col) {
195 const float dzdx = (_heightAt(row, col + 1) - _heightAt(row, col - 1)) / (2.0f * step);
197 (_heightAt(row - 1, col) - _heightAt(row + 1, col)) / (2.0f * step);
198 QVector3D normal(-dzdx, -dzdy, 1.0f);
204 for (
int row = 0; row < verticesPerEdge; row++) {
205 const float y = half - (row * step);
206 const float texV =
static_cast<float>(row) / _gridSize;
207 for (
int col = 0; col < verticesPerEdge; col++) {
208 const float x = -half + (col * step);
209 const float u =
static_cast<float>(col) / _gridSize;
210 writeVertex(x, y, _heightAt(row, col), normalAt(row, col), u, texV);
216 const auto writeSkirtVertex = [&](
int row,
int col) {
217 const float x = -half + (col * step);
218 const float y = half - (row * step);
219 const float u =
static_cast<float>(col) / _gridSize;
220 const float texV =
static_cast<float>(row) / _gridSize;
221 writeVertex(x, y, _heightAt(row, col) - skirtDepth, normalAt(row, col), u, texV);
223 for (
int col = 0; col < verticesPerEdge; col++) {
224 writeSkirtVertex(0, col);
226 for (
int col = 0; col < verticesPerEdge; col++) {
227 writeSkirtVertex(_gridSize, col);
229 for (
int row = 0; row < verticesPerEdge; row++) {
230 writeSkirtVertex(row, 0);
232 for (
int row = 0; row < verticesPerEdge; row++) {
233 writeSkirtVertex(row, _gridSize);
237 const int gridIndexCount = _gridSize * _gridSize * 6;
238 const int skirtIndexCount = 4 * _gridSize * 6;
239 QByteArray indexData((gridIndexCount + skirtIndexCount) *
sizeof(quint32), Qt::Uninitialized);
240 quint32* idx =
reinterpret_cast<quint32*
>(indexData.data());
242 const auto gridIndex = [&](
int row,
int col) {
return static_cast<quint32
>((row * verticesPerEdge) + col); };
243 for (
int row = 0; row < _gridSize; row++) {
244 for (
int col = 0; col < _gridSize; col++) {
245 *idx++ = gridIndex(row, col);
246 *idx++ = gridIndex(row + 1, col);
247 *idx++ = gridIndex(row, col + 1);
248 *idx++ = gridIndex(row, col + 1);
249 *idx++ = gridIndex(row + 1, col);
250 *idx++ = gridIndex(row + 1, col + 1);
256 const auto writeSkirtQuads = [&](quint32 skirtBase,
const auto& topIndex) {
257 for (
int i = 0; i < _gridSize; i++) {
258 const quint32 topA = topIndex(i);
259 const quint32 topB = topIndex(i + 1);
260 const quint32 skirtA = skirtBase + i;
261 const quint32 skirtB = skirtBase + i + 1;
270 const quint32 skirtStart =
static_cast<quint32
>(gridVertexCount);
271 writeSkirtQuads(skirtStart, [&](
int i) {
return gridIndex(0, i); });
272 writeSkirtQuads(skirtStart + verticesPerEdge, [&](
int i) {
return gridIndex(_gridSize, i); });
273 writeSkirtQuads(skirtStart + (2 * verticesPerEdge), [&](
int i) {
return gridIndex(i, 0); });
274 writeSkirtQuads(skirtStart + (3 * verticesPerEdge), [&](
int i) {
return gridIndex(i, _gridSize); });
278 setVertexData(vertexData);
279 setIndexData(indexData);
280 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
281 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
282 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 3 *
sizeof(
float), QQuick3DGeometry::Attribute::F32Type);
283 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic, 6 *
sizeof(
float),
284 QQuick3DGeometry::Attribute::F32Type);
285 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::U32Type);
286 setBounds(QVector3D(-half, -half, minZ), QVector3D(half, half, maxZ));
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QList< float > samplePatch(const TileMath::TileKey &key, int gridSize, PatchSampler::EdgeStep *edgeStep=nullptr) 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()
float vertexHeight(const QList< float > &heights, int gridSize, const LodDeltas &lodDeltas, int row, int col)