mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
wgpu backend: match GL orbit + viewAll math so pivots align
Reported regression: --benchmark on the same sidecar visibly rotated
around a different point in the wgpu binary than in IfcViewerMinimal.
Root cause was two camera-convention drifts:
1. orbitEye placed the camera at (sin yaw, -cos yaw) from target;
the GL backend uses (cos yaw, sin yaw). Same target, but the
camera faces a different side of the model at yaw=0, which made
the orbit feel like it pivoted around a different point even
though the actual world-space target was the same. Now exactly
matches GL ViewportWindow::updateCamera:
eye.x = target.x + dist * cos(pitch) * cos(yaw)
eye.y = target.y + dist * cos(pitch) * sin(yaw)
eye.z = target.z + dist * sin(pitch)
2. viewAll's distance was an ad-hoc 0.6 * diag / tan(half_fov);
GL uses frameAabb(mn, mx, 1.10): tan_half = tan(fov/2),
min_aspect = min(aspect, 1), distance = (radius / (tan_half *
min_aspect)) * 1.10. Aspect-aware so portrait windows pull back
enough that the bounding sphere still fits on the tighter axis.
Now ported verbatim.
Also logs the computed target + distance on viewAll so a follow-up
side-by-side run prints both backends' framings and any remaining
discrepancy is easy to spot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1471,12 +1471,17 @@ void WgpuViewportWindow::releaseMsaaColorTexture() {
|
||||
|
||||
static QVector3D orbitEye(const float target[3], float dist,
|
||||
float yaw_deg, float pitch_deg) {
|
||||
// Matches the GL ViewportWindow::updateCamera convention exactly so the
|
||||
// orbit pivot, framing, and benchmark camera path align between backends.
|
||||
// eye.x = target.x + dist * cos(pitch) * cos(yaw)
|
||||
// eye.y = target.y + dist * cos(pitch) * sin(yaw)
|
||||
// eye.z = target.z + dist * sin(pitch)
|
||||
const float yaw = qDegreesToRadians(yaw_deg);
|
||||
const float pit = qDegreesToRadians(pitch_deg);
|
||||
const float cp = std::cos(pit), sp = std::sin(pit);
|
||||
const float cy = std::cos(yaw), sy = std::sin(yaw);
|
||||
return QVector3D(target[0] + dist * cp * sy,
|
||||
target[1] - dist * cp * cy,
|
||||
return QVector3D(target[0] + dist * cp * cy,
|
||||
target[1] + dist * cp * sy,
|
||||
target[2] + dist * sp);
|
||||
}
|
||||
|
||||
@@ -1541,16 +1546,38 @@ bool WgpuViewportWindow::computeSceneAabb(float mn[3], float mx[3]) const {
|
||||
void WgpuViewportWindow::viewAll() {
|
||||
float mn[3], mx[3];
|
||||
if (!computeSceneAabb(mn, mx)) return;
|
||||
camera_target_[0] = 0.5f * (mn[0] + mx[0]);
|
||||
camera_target_[1] = 0.5f * (mn[1] + mx[1]);
|
||||
camera_target_[2] = 0.5f * (mn[2] + mx[2]);
|
||||
const float diag = std::sqrt(
|
||||
(mx[0] - mn[0]) * (mx[0] - mn[0]) +
|
||||
(mx[1] - mn[1]) * (mx[1] - mn[1]) +
|
||||
(mx[2] - mn[2]) * (mx[2] - mn[2]));
|
||||
// Pull back enough so the bounding sphere fits at half-FOV.
|
||||
const float half_fov = qDegreesToRadians(camera_fov_y_deg_) * 0.5f;
|
||||
camera_distance_ = std::max(1.0f, 0.6f * diag / std::tan(half_fov));
|
||||
|
||||
// Frame the union AABB with the same math as GL's frameAabb(mn, mx, 1.10):
|
||||
// target at centroid, distance pulls the bounding sphere just inside the
|
||||
// tighter of the horizontal/vertical FOV. Padding 1.10 matches GL viewAll.
|
||||
const float cx = 0.5f * (mn[0] + mx[0]);
|
||||
const float cy = 0.5f * (mn[1] + mx[1]);
|
||||
const float cz = 0.5f * (mn[2] + mx[2]);
|
||||
camera_target_[0] = cx;
|
||||
camera_target_[1] = cy;
|
||||
camera_target_[2] = cz;
|
||||
|
||||
const float dx = mx[0] - mn[0];
|
||||
const float dy = mx[1] - mn[1];
|
||||
const float dz = mx[2] - mn[2];
|
||||
const float radius = 0.5f * std::sqrt(dx*dx + dy*dy + dz*dz);
|
||||
|
||||
if (radius > 1e-4f) {
|
||||
const float fovy_rad = qDegreesToRadians(camera_fov_y_deg_);
|
||||
const float tan_half = std::tan(fovy_rad * 0.5f);
|
||||
if (tan_half > 1e-6f) {
|
||||
const int h = std::max(configured_h_, 1);
|
||||
const float aspect = float(std::max(configured_w_, 1)) / float(h);
|
||||
const float min_aspect = aspect < 1.0f ? aspect : 1.0f;
|
||||
camera_distance_ = std::max(0.1f, (radius / (tan_half * min_aspect)) * 1.10f);
|
||||
}
|
||||
}
|
||||
|
||||
qInfo().noquote().nospace()
|
||||
<< "[wgpu] viewAll target=(" << cx << ", " << cy << ", " << cz << ")"
|
||||
<< " distance=" << camera_distance_
|
||||
<< " (scene radius=" << radius << ")";
|
||||
|
||||
if (isExposed()) requestUpdate();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user