mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 22:31:02 +00:00
Fix: Correct camera panning for the perspective view (#15212)
This commit is contained in:
@@ -99,8 +99,11 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr<SceneRaycasterItem> item)
|
||||
}
|
||||
}
|
||||
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera,
|
||||
const ClippingPlane* clipping_plane, SceneRaycaster::EHitMode mode) const
|
||||
{
|
||||
// Orca: Picking may favor an already selected volume for interaction, while camera
|
||||
// navigation must always use the geometrically closest visible scene surface.
|
||||
// helper class used to return currently selected volume as hit when overlapping with other volumes
|
||||
// to allow the user to click and drag on a selected volume
|
||||
class VolumeKeeper
|
||||
@@ -110,7 +113,11 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
bool m_selected_volume_already_found{ false };
|
||||
|
||||
public:
|
||||
VolumeKeeper() {
|
||||
explicit VolumeKeeper(bool enabled) {
|
||||
// Orca: Disable selected-volume bias for navigation raycasts.
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
const Selection& selection = wxGetApp().plater()->get_selection();
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
@@ -135,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
}
|
||||
};
|
||||
|
||||
VolumeKeeper volume_keeper;
|
||||
VolumeKeeper volume_keeper(mode == EHitMode::Picking);
|
||||
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) {
|
||||
@@ -154,7 +161,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
|
||||
HitResult ret;
|
||||
|
||||
auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
auto test_raycasters = [this, is_closest, clipping_plane, mode, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type);
|
||||
const Vec3f camera_forward = camera.get_dir_forward().cast<float>();
|
||||
@@ -163,12 +170,22 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
if (!item->is_active())
|
||||
continue;
|
||||
|
||||
// Each plate's component 0 is its surface; the remaining Bed IDs are controls.
|
||||
// Keep controls clickable, but do not use them as camera-pan anchors.
|
||||
if (mode != EHitMode::Picking && type == EType::Bed &&
|
||||
decode_id(type, item->get_id()) % PartPlate::GRABBER_COUNT != 0)
|
||||
continue;
|
||||
|
||||
current_hit.raycaster_id = item->get_id();
|
||||
const Transform3d& trafo = item->get_transform();
|
||||
if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
|
||||
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
|
||||
current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>();
|
||||
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) {
|
||||
// Orca: Perspective rays away from the viewport center are not parallel to camera_forward.
|
||||
// Keep picking's legacy policy, but accept every front-facing navigation surface.
|
||||
const Vec3f view_direction = mode != EHitMode::Picking && camera.get_type() == Camera::EType::Perspective ?
|
||||
Vec3f((current_hit.position.cast<double>() - camera.get_position()).cast<float>()) : camera_forward;
|
||||
if (item->use_back_faces() || current_hit.normal.dot(view_direction) < 0.0f) {
|
||||
if (is_closest(camera, current_hit.position)) {
|
||||
if (volume_keeper.is_active()) {
|
||||
if (volume_keeper.check_hit_result(current_hit))
|
||||
@@ -182,14 +199,18 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
}
|
||||
};
|
||||
|
||||
if (!m_gizmos.empty())
|
||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||
// Orca: Gizmo geometry is an interaction target, not a valid depth anchor for camera movement.
|
||||
if (mode == EHitMode::Picking) {
|
||||
if (!m_gizmos.empty())
|
||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||
|
||||
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||
}
|
||||
|
||||
if (!m_gizmos_on_top || !ret.is_valid()) {
|
||||
if (camera.is_looking_downward() && !m_bed.empty())
|
||||
// Orca: In perspective the bottom of the viewport can see the bed even at a horizontal view.
|
||||
if ((mode == EHitMode::SceneOnly || (mode == EHitMode::Picking && camera.is_looking_downward())) && !m_bed.empty())
|
||||
test_raycasters(EType::Bed, mouse_pos, camera, ret);
|
||||
if (!m_volumes.empty())
|
||||
test_raycasters(EType::Volume, mouse_pos, camera, ret);
|
||||
|
||||
Reference in New Issue
Block a user