12#include <QtCore/QAbstractItemModel>
13#include <QtQuick/QSGGeometryNode>
14#include <QtQuick/QSGTextureMaterial>
15#include <QtQuick/QSGTextureProvider>
29 setFlag(ItemHasContents);
30 _terrainResampleTimer.setSingleShot(
true);
31 _terrainResampleTimer.setInterval(250);
32 connect(&_terrainResampleTimer, &QTimer::timeout,
this, &GeoMapSpriteLayer::_markPointsDirty);
37 if (
scene == _scene) {
40 qCDebug(GeoMapSpriteLayerLog) <<
"scene" << (
scene ?
"set" :
"cleared");
42 disconnect(_scene,
nullptr,
this,
nullptr);
49 connect(_scene, &QObject::destroyed,
this, [
this] {
65 disconnect(_surfaceModel,
nullptr,
this,
nullptr);
70 &GeoMapSpriteLayer::_scheduleTerrainResample);
71 connect(_surfaceModel, &QObject::destroyed,
this, [
this] {
72 _surfaceModel =
nullptr;
83 if (
model == _model) {
86 qCDebug(GeoMapSpriteLayerLog) <<
"model" << (
model ?
"set" :
"cleared");
88 disconnect(_model,
nullptr,
this,
nullptr);
92 connect(_model, &QAbstractItemModel::rowsInserted,
this, &GeoMapSpriteLayer::_markPointsDirty);
93 connect(_model, &QAbstractItemModel::rowsRemoved,
this, &GeoMapSpriteLayer::_markPointsDirty);
94 connect(_model, &QAbstractItemModel::modelReset,
this, &GeoMapSpriteLayer::_markPointsDirty);
95 connect(_model, &QAbstractItemModel::dataChanged,
this, &GeoMapSpriteLayer::_markPointsDirty);
96 connect(_model, &QAbstractItemModel::layoutChanged,
this, &GeoMapSpriteLayer::_markPointsDirty);
97 connect(_model, &QObject::destroyed,
this, [
this] {
123 disconnect(_sprite,
nullptr,
this,
nullptr);
126 _warnedNotProvider =
false;
128 connect(_sprite, &QObject::destroyed,
this, [
this] {
148void GeoMapSpriteLayer::_markPointsDirty()
154void GeoMapSpriteLayer::_scheduleTerrainResample()
157 if (!_terrainResampleTimer.isActive()) {
158 _terrainResampleTimer.start();
162void GeoMapSpriteLayer::_connectCamera()
165 if (camera == _connectedCamera) {
169 if (_connectedCamera) {
170 disconnect(_connectedCamera,
nullptr,
this,
nullptr);
172 _connectedCamera = camera;
173 if (_connectedCamera) {
177 connect(_connectedCamera, &QObject::destroyed,
this, [
this] {
178 _connectedCamera =
nullptr;
185void GeoMapSpriteLayer::_rebuildPointCache()
187 _pointsDirty =
false;
190 const auto appendCoordinate = [
this](
const QGeoCoordinate&
coordinate) {
197 point.terrainAlt = _surfaceModel ? _surfaceModel->
terrainHeightAt(coordinate) : 0.0;
198 _points.push_back(point);
202 const int rows = _model->rowCount();
203 _points.reserve(rows);
204 for (
int row = 0; row < rows; ++row) {
205 const QObject*
const object = _model->data(_model->index(row, 0), Qt::UserRole).value<QObject*>();
207 appendCoordinate(object->property(
"coordinate").value<QGeoCoordinate>());
211 _points.reserve(_coordinates.size());
212 for (
const QVariant& variant : _coordinates) {
213 appendCoordinate(variant.value<QGeoCoordinate>());
216 qCDebug(GeoMapSpriteLayerLog) <<
"point cache rebuilt:" << _points.size() <<
"points";
223 auto* node =
static_cast<QSGGeometryNode*
>(oldNode);
225 QSGTexture* texture =
nullptr;
227 if (_sprite->isTextureProvider()) {
228 texture = _sprite->textureProvider()->texture();
232 if (
auto*
const dynamicTexture = qobject_cast<QSGDynamicTexture*>(texture)) {
233 dynamicTexture->updateTexture();
237 QMetaObject::invokeMethod(
this, qOverload<>(&QQuickItem::update), Qt::QueuedConnection);
239 }
else if (!_warnedNotProvider) {
241 qCWarning(GeoMapSpriteLayerLog) <<
"sprite item is not a texture provider";
242 _warnedNotProvider =
true;
247 _rebuildPointCache();
251 if (!texture || !camera || _points.empty()) {
257 std::vector<QPointF> screenPositions;
258 screenPositions.reserve(_points.size());
259 for (
const CachedPoint& point : _points) {
260 const auto screen = camera->
worldToScreen(point.world, point.terrainAlt * zScale);
262 screenPositions.push_back(*screen);
265 if (screenPositions.empty()) {
270 QSGGeometry* geometry =
nullptr;
272 node =
new QSGGeometryNode;
273 geometry =
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0);
274 geometry->setDrawingMode(QSGGeometry::DrawTriangles);
275 node->setGeometry(geometry);
276 node->setFlag(QSGNode::OwnsGeometry);
277 auto*
const material =
new QSGTextureMaterial;
278 material->setFiltering(QSGTexture::Linear);
279 node->setMaterial(material);
280 node->setFlag(QSGNode::OwnsMaterial);
282 geometry = node->geometry();
285 auto*
const material =
static_cast<QSGTextureMaterial*
>(node->material());
286 if (material->texture() != texture) {
287 material->setTexture(texture);
288 node->markDirty(QSGNode::DirtyMaterial);
291 const QRectF uv = texture->normalizedTextureSubRect();
292 const float u0 =
static_cast<float>(uv.left());
293 const float v0 =
static_cast<float>(uv.top());
294 const float u1 =
static_cast<float>(uv.right());
295 const float v1 =
static_cast<float>(uv.bottom());
296 const float half =
static_cast<float>(_spriteSize / 2.0);
298 geometry->allocate(
static_cast<int>(screenPositions.size()) * 6);
299 QSGGeometry::TexturedPoint2D* vertex = geometry->vertexDataAsTexturedPoint2D();
300 for (
const QPointF& position : screenPositions) {
301 const float cx =
static_cast<float>(position.x());
302 const float cy =
static_cast<float>(position.y());
303 vertex[0].set(cx - half, cy - half, u0, v0);
304 vertex[1].set(cx + half, cy - half, u1, v0);
305 vertex[2].set(cx - half, cy + half, u0, v1);
306 vertex[3].set(cx + half, cy - half, u1, v0);
307 vertex[4].set(cx + half, cy + half, u1, v1);
308 vertex[5].set(cx - half, cy + half, u0, v1);
311 node->markDirty(QSGNode::DirtyGeometry);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
std::optional< QPointF > worldToScreen(const QPointF &worldGround, double worldZ=0.0) const
void fieldOfViewChanged()
void viewportSizeChanged()
void setCoordinates(const QVariantList &coordinates)
void setModel(QAbstractItemModel *model)
void setSpriteSize(qreal spriteSize)
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
void coordinatesChanged()
qreal spriteSize() const
On-screen sprite width/height in (logical) pixels.
QAbstractItemModel * model() const
void setSurfaceModel(SurfacePatchModel *surfaceModel)
QQuickItem * sprite() const
void surfaceModelChanged()
void setScene(GeoScene *scene)
SurfacePatchModel * surfaceModel() const
QVariantList coordinates() const
Coordinate list alternative to model (model takes precedence when set)
void setSprite(QQuickItem *sprite)
qreal verticalScale() const
void sceneOriginChanged()
qreal terrainScale() const
void terrainScaleChanged()
GeoMapCamera * camera() const
void terrainHeightsChanged()
terrainHeightAt answers changed somewhere: consumers re-query
Q_INVOKABLE double terrainHeightAt(const QGeoCoordinate &coordinate) const
std::optional< double > coordinate(std::string_view field, std::string_view hemisphere, bool latitude)
QPointF geoToWorld(const QGeoCoordinate &coord)
Geo -> world meters. Latitude is clamped to +/-kMaxLatitude.