fix(vtk): 底图LOD消除缩放空白闪烁(延迟清理旧层,落地后再删)+按层级Z偏移防共面z-fighting
This commit is contained in:
parent
aaf150ca2e
commit
29ea44560d
|
|
@ -38,6 +38,8 @@ constexpr int kRadius = 3; // 中心瓦片 ±3 → 最多 7x7=49 块
|
|||
constexpr int kMinZoom = 3;
|
||||
constexpr int kMaxZoom = 18;
|
||||
constexpr double kGroundZ = 0.0; // 底图置于 z=0 地面参考(剖面深度向下为负,落其下)
|
||||
constexpr double kZEps = 0.02; // 每层级 Z 微偏移:高层级压上面,避免共面瓦片 z-fighting
|
||||
constexpr int kHardCap = 400; // 瓦片硬上限:超过则即便未落地也强制清理,兜底内存
|
||||
constexpr double kPi = 3.14159265358979323846;
|
||||
constexpr double kEarthCirc = 40075016.686; // 赤道周长(米) = z0 单瓦片地面尺寸
|
||||
constexpr double kTilesAcross = 4.0; // 视野跨度目标覆盖瓦片数(决定层级)
|
||||
|
|
@ -149,17 +151,7 @@ void TileBasemap::refresh() {
|
|||
}
|
||||
}
|
||||
|
||||
// 移除离开视野/换层级的瓦片。
|
||||
for (auto it = placed_.begin(); it != placed_.end();) {
|
||||
if (desired_.find(it->first) == desired_.end()) {
|
||||
scene_.renderer()->RemoveViewProp(it->second);
|
||||
it = placed_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// 拉取缺失瓦片。
|
||||
// 拉取缺失瓦片(旧层暂不删,留作回退;新层落地后由 purgeStale 清理)。
|
||||
for (long long key : desired_) {
|
||||
if (placed_.count(key) || inFlight_.count(key)) continue;
|
||||
int z, x, y;
|
||||
|
|
@ -167,10 +159,28 @@ void TileBasemap::refresh() {
|
|||
fetchTile(z, x, y, key);
|
||||
}
|
||||
|
||||
purgeStale(); // 若无需新拉(inFlight 空)则立即清理旧层
|
||||
if (rw_) rw_->Render();
|
||||
refreshing_ = false;
|
||||
}
|
||||
|
||||
void TileBasemap::purgeStale() {
|
||||
// 仅当本轮所有请求都落地(inFlight 空)后再删旧层;否则缩放/平移期间老瓦片留作回退,避免空白闪烁。
|
||||
// 超过硬上限则强制清理兜底内存(可能短暂空白,极少触发)。
|
||||
if (!inFlight_.empty() && placed_.size() <= static_cast<size_t>(kHardCap)) return;
|
||||
bool removed = false;
|
||||
for (auto it = placed_.begin(); it != placed_.end();) {
|
||||
if (desired_.find(it->first) == desired_.end()) {
|
||||
scene_.renderer()->RemoveViewProp(it->second);
|
||||
it = placed_.erase(it);
|
||||
removed = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (removed && rw_) rw_->Render();
|
||||
}
|
||||
|
||||
void TileBasemap::fetchTile(int z, int x, int y, long long key) {
|
||||
const QString layerDir = (kind_ == Satellite) ? QStringLiteral("img_w") : QStringLiteral("vec_w");
|
||||
const QString layer = (kind_ == Satellite) ? QStringLiteral("img") : QStringLiteral("vec");
|
||||
|
|
@ -195,11 +205,12 @@ void TileBasemap::fetchTile(int z, int x, int y, long long key) {
|
|||
if (gen != generation_) return; // 换源/隐藏后丢弃
|
||||
if (kind_ == Hidden) return;
|
||||
if (desired_.find(key) == desired_.end()) return; // 已移出视野
|
||||
if (placed_.count(key)) return;
|
||||
if (reply->error() != QNetworkReply::NoError) return;
|
||||
QImage img;
|
||||
if (!img.loadFromData(reply->readAll())) return;
|
||||
placeTile(key, z, x, y, img);
|
||||
if (!placed_.count(key) && reply->error() == QNetworkReply::NoError &&
|
||||
img.loadFromData(reply->readAll())) {
|
||||
placeTile(key, z, x, y, img); // 仅新瓦片落地;其余(失败/已存在)只推动清理
|
||||
}
|
||||
purgeStale(); // 本块已解决 → 若整轮落地则清理被顶替的旧层
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +219,7 @@ void TileBasemap::placeTile(long long key, int z, int x, int y, const QImage& im
|
|||
const auto sw = frame_->toLocal(b.south, b.west);
|
||||
const auto se = frame_->toLocal(b.south, b.east);
|
||||
const auto nw = frame_->toLocal(b.north, b.west);
|
||||
const double gz = kGroundZ + (z - kMinZoom) * kZEps; // 高层级略抬高,压在旧层之上防共面闪烁
|
||||
|
||||
// QImage → vtkImageData(RGBA),垂直翻转:使纹理 v=0 对应瓦片南边(与下方 PlaneSource tcoord 一致)。
|
||||
const QImage rgba = img.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
|
@ -228,9 +240,9 @@ void TileBasemap::placeTile(long long key, int z, int x, int y, const QImage& im
|
|||
// PlaneSource 自动 tcoord:origin→(0,0)、point1→(1,0)、point2→(0,1)。
|
||||
// origin=SW(西南)、point1=SE(东) → u 西→东;point2=NW(北) → v 南→北。与翻转后纹理对齐。
|
||||
vtkNew<vtkPlaneSource> plane;
|
||||
plane->SetOrigin(sw.x, sw.y, kGroundZ);
|
||||
plane->SetPoint1(se.x, se.y, kGroundZ);
|
||||
plane->SetPoint2(nw.x, nw.y, kGroundZ);
|
||||
plane->SetOrigin(sw.x, sw.y, gz);
|
||||
plane->SetPoint1(se.x, se.y, gz);
|
||||
plane->SetPoint2(nw.x, nw.y, gz);
|
||||
|
||||
vtkNew<vtkPolyDataMapper> mapper;
|
||||
mapper->SetInputConnection(plane->GetOutputPort());
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public:
|
|||
private:
|
||||
static long long tileKey(int z, int x, int y);
|
||||
void ensureObserver(); // 首次显示时挂到交互样式的 EndInteractionEvent
|
||||
void purgeStale(); // 本轮请求全部落地后再删旧层瓦片,避免缩放空白闪烁
|
||||
bool computeView(double& centerLat, double& centerLon, int& zoom) const;
|
||||
void fetchTile(int z, int x, int y, long long key);
|
||||
void placeTile(long long key, int z, int x, int y, const QImage& img);
|
||||
|
|
|
|||
Loading…
Reference in New Issue