fix: correct CPU projection axes to match GPU look_at_rh convention
viewport_content_wires() computed view_right via world_z × view_direction (cross-product). This gives the OPPOSITE sign to the right vector that look_at_rh produces for views with a Y component (FRONT/BACK views): GPU look_at_rh: screen_right = f × up = (−vd) × (rot*Y) = rot*X Old CPU formula: screen_right = Z × vd ← opposite sign for FRONT/BACK The mismatch caused the paper-space projection to appear mirror-flipped after panning/rotating in MSPACE. Fix: use camera_for_viewport() to obtain the exact same rotation quaternion the GPU uses, then derive both axes from it: view_right = rotation * X view_up = rotation * Y Also replaces the perspective depth dot-product (mp · vd) with mp · (rotation*Z) so the depth direction is consistent too. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c2df63c4cc
commit
1c0520e4e4
1 changed files with 14 additions and 18 deletions
|
|
@ -702,22 +702,19 @@ impl Scene {
|
|||
// ── Per-viewport frozen layer set ─────────────────────────────
|
||||
let frozen: HSet<Handle> = vp.frozen_layers.iter().cloned().collect();
|
||||
|
||||
// ── View direction coordinate frame ───────────────────────────
|
||||
let vd = glam::Vec3::new(
|
||||
vp.view_direction.x as f32,
|
||||
vp.view_direction.y as f32,
|
||||
vp.view_direction.z as f32,
|
||||
).normalize_or(glam::Vec3::Z);
|
||||
|
||||
// Compute view_right and view_up from the direction vector.
|
||||
let world_z = glam::Vec3::Z;
|
||||
let view_right = if (vd.dot(world_z)).abs() > 0.99 {
|
||||
// Looking straight up/down: use X as right.
|
||||
glam::Vec3::X
|
||||
} else {
|
||||
world_z.cross(vd).normalize()
|
||||
// ── View coordinate frame ─────────────────────────────────────
|
||||
// Use camera_for_viewport so the axes match the GPU renderer exactly.
|
||||
// GPU uses look_at_rh(eye, target, rotation*Y), which gives:
|
||||
// screen_right = normalize((target-eye) × up) = rotation * X
|
||||
// screen_up = rotation * Y
|
||||
// The old Z×vd cross-product gives the opposite sign for views
|
||||
// that have a Y component in view_direction (front/back), causing mirroring.
|
||||
let cam_frame = match self.camera_for_viewport(vp.common.handle) {
|
||||
Some(c) => c,
|
||||
None => continue,
|
||||
};
|
||||
let view_up = vd.cross(view_right).normalize();
|
||||
let view_right = cam_frame.rotation * glam::Vec3::X;
|
||||
let view_up = cam_frame.rotation * glam::Vec3::Y;
|
||||
|
||||
// ── Scale & viewport parameters ───────────────────────────────
|
||||
let scale = if vp.custom_scale.abs() > 1e-9 {
|
||||
|
|
@ -795,9 +792,8 @@ impl Scene {
|
|||
let u = mp.dot(view_right);
|
||||
let v = mp.dot(view_up);
|
||||
if use_perspective {
|
||||
// vd points from target toward camera, so depth along vd is the
|
||||
// distance from the target plane toward the camera.
|
||||
let d_vd = mp.dot(vd);
|
||||
// Eye direction (target → camera) = rotation * Z.
|
||||
let d_vd = mp.dot(cam_frame.rotation * glam::Vec3::Z);
|
||||
// Forward distance from camera (positive = in front of camera).
|
||||
let fwd = camera_dist - d_vd;
|
||||
if fwd <= 0.001 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue