Как мне найти точку мыши в сцене с помощью SceneKit?

Я настроил сцену в SceneKit и выполнил хит-тест, чтобы выбрать элемент. Однако я хочу иметь возможность перемещать этот элемент по плоскости в моей сцене. Я продолжаю получать события перетаскивания мыши, но не знаю, как преобразовать эти 2D-координаты в 3D-координаты в сцене.

Мой случай очень прост. Камера расположена в точках 0, 0, 50 и направлена ​​на 0, 0, 0. Я просто хочу перетащить свой объект вдоль z-плоскости с z-значением 0.

Проверка нажатия работает как шарм, но как мне перевести точку мыши из события перетаскивания в новую позицию в сцене для трехмерного объекта, который я перетаскиваю?


person Michael Bishop    schedule 08.04.2014    source источник


Ответы (3)


Вам не нужно использовать невидимую геометрию - Scene Kit может выполнять все необходимые преобразования координат без необходимости проверять невидимые объекты. По сути, вам нужно сделать то же самое, что и в приложении для 2D-рисования. для перемещения объекта: найдите смещение между положением mouseDown: и положением объекта, затем для каждого mouseMoved: добавьте это смещение к новому положению мыши, чтобы установить новое положение объекта.

Вот подход, который вы могли бы использовать ...

  1. Проверьте начальное местоположение щелчка, как вы это уже делаете. Это дает вам SCNHitTestResult объект, определяющий узел, который вы хотите переместить, верно?

  2. Проверьте _ 4_ результата проверки попадания. Если узел, который вы хотите переместить, является дочерним по отношению к _ 5_, это вектор, который вы хотите найти для смещения. (В противном случае вам нужно будет преобразовать его в систему координат родительского узла, который вы хотите переместить - см. _ 6_ или convertPosition:fromNode:.)

  3. Вам понадобится эталонная глубина для этой точки, чтобы вы могли сравнить с ней mouseMoved: местоположений. Используйте _ 9_, чтобы преобразовать вектор, полученный на шаге 2 (точка в 3D-сцене), обратно в пространство экрана - это дает вам 3D-вектор, координаты x и y которого являются точкой в ​​пространстве экрана и координата z которой указывает глубину этой точки относительно плоскостей отсечения (0.0 находится на ближней плоскости, 1.0 находится на дальней плоскости). Удерживайте эту координату z, чтобы использовать ее в течение mouseMoved:.

  4. Вычтите _ 13_ узла, который вы хотите переместить из вектора положения мыши, полученного на шаге 2. Это дает вам смещение щелчка мыши от положения объекта. Держитесь за этот вектор - он вам понадобится, пока перетаскивание не закончится.

  5. На mouseMoved: постройте новый 3D-вектор из экранных координат нового местоположения мыши и значения глубины, полученного на шаге 3. Затем преобразуйте этот вектор в координаты сцены, используя _ 15_ - это местоположение мыши в трехмерное пространство вашей сцены (эквивалентное тому, которое вы получили в результате теста на попадание, но без необходимости "нажимать" геометрию сцены).

  6. Добавьте смещение, полученное на шаге 3, в новое местоположение, полученное на шаге 5 - это новый position, в который нужно переместить узел. (Примечание: чтобы перетаскивание в реальном времени выглядело правильно, вы должны убедиться, что это изменение положения не анимировано. По умолчанию продолжительность текущего _ 17_ равен нулю, поэтому вам не нужно беспокоиться об этом, если вы еще не изменили его.)

(Это вроде как не в моей голове, поэтому вам, вероятно, следует перепроверить соответствующие документы и заголовки. И вы могли бы немного упростить это с помощью математики.)

person rickster    schedule 08.04.2014
comment
Это гораздо более чистое решение. Вы должны пойти с этим. - person David Rönnqvist; 09.04.2014
comment
Сэр, пожалуйста, помогите мне stackoverflow.com/questions/66437552/, если камера повернута, это решение не работает, пожалуйста, помогите ???????? - person Prashant Tukadiya; 22.03.2021

В качестве эксперимента я применил полезный ответ мистера Бишопа. Перетаскивание не совсем работает (объект - шахматная фигура - выскакивает за пределы экрана) из-за различий в величинах координат между щелчком мыши и трехмерным миром. Я вставил результаты журнала кое-где среди кода.

Я спросил на форумах Apple, знает ли кто-нибудь секретный соус для гомогенизации координат, но не получил однозначного ответа. Во-первых, я внес некоторые экспериментальные изменения в метод мистера Бишопа, и участники форума посоветовали мне вернуться к его методике.

Несмотря на недостатки моего кода, я подумал, что кто-то сочтет его полезной отправной точкой. Я подозреваю, что с кодом есть всего одна или две небольшие проблемы.

Обратите внимание, что журнал матрицы преобразования мира объекта (шахматной фигуры) не является частью процесса, но один участник форума Apple сообщил мне, что матрица часто предлагает полезную «проверку работоспособности» - что действительно было.

- (NSPoint)
viewPointForEvent: (NSEvent *) event_
{
    NSPoint   windowPoint    = [event_ locationInWindow];
    NSPoint   viewPoint        = [self.view convertPoint: windowPoint
                                             fromView: nil];
    return viewPoint;
}

- (SCNHitTestResult *)
hitTestResultForEvent: (NSEvent *) event_
{
    NSPoint      viewPoint        = [self viewPointForEvent: event_];
    CGPoint      cgPoint        = CGPointMake (viewPoint.x, viewPoint.y);
    NSArray * points        = [(SCNView *) self.view hitTest: cgPoint
                                                     options: @{}];
    return points.firstObject;
}

- (void)
mouseDown: (NSEvent *) theEvent
{
    SCNHitTestResult * result = [self hitTestResultForEvent: theEvent];

    SCNVector3 clickWorldCoordinates = result.worldCoordinates;
    log output: clickWorldCoordinates x 208.124578, y -12827.223365, z 3163.659073
    SCNVector3 screenCoordinates = [(SCNView *) self.view projectPoint: clickWorldCoordinates];
    log output: screenCoordinates x 245.128906, y 149.335938, z 0.985565
    // save the z coordinate for use in mouseDragged
    mouseDownClickOnObjectZCoordinate = screenCoordinates.z;

    selectedPiece = result.node;  // save selected piece for use in mouseDragged

    SCNVector3    piecePosition = selectedPiece.position;
    log output: piecePosition x -18.200000, y 6.483060, z 2.350000

    offsetOfMouseClickFromPiece.x = clickWorldCoordinates.x - piecePosition.x;
    offsetOfMouseClickFromPiece.y = clickWorldCoordinates.y - piecePosition.y;
    offsetOfMouseClickFromPiece.z = clickWorldCoordinates.z - piecePosition.z;
    log output: offsetOfMouseClickFromPiece x 226.324578, y -12833.706425, z 3161.309073  
}

- (void)
mouseDragged: (NSEvent *) theEvent;
{
    NSPoint   viewClickPoint        = [self viewPointForEvent: theEvent];

    SCNVector3 clickCoordinates;
    clickCoordinates.x = viewClickPoint.x;
    clickCoordinates.y = viewClickPoint.y;
    clickCoordinates.z = mouseDownClickOnObjectZCoordinate;
    log output:  clickCoordinates x 246.128906, y 0.000000, z 0.985565

    log output:  pieceWorldTransform: 
      m11 = 242.15889219510001, m12 = -0.000045609300002524833, m13 = -0.00000721691076126, m14 = 0, 
      m21 = 0.0000072168760805499971, m22 = -0.000039452697396149999, m23 = 242.15890446329999, m24 = 0, 
      m31 = -0.000045609300002524833, m32 = -242.15889219510001, m33 = -0.000039452676995750002, m34 = 0, 
      m41 = -4268.2349924762348, m42 = -12724.050221935429, m43 = 4852.6652710104272, m44 = 1)

    SCNVector3 newPiecePosition;
    newPiecePosition.x = offsetOfMouseClickFromPiece.x + clickCoordinates.x;
    newPiecePosition.y = offsetOfMouseClickFromPiece.y + clickCoordinates.y;
    newPiecePosition.z = offsetOfMouseClickFromPiece.z + clickCoordinates.z;
    log output: newPiecePosition x 472.453484, y -12833.706425, z 3162.294639

    selectedPiece.position = newPiecePosition;
}
person Steve Steinitz    schedule 29.05.2014

Я использовал код, написанный Стивом, и с небольшими изменениями он у меня сработал.

На mouseDown я сохраняю clickWorldCoordinates в свойстве startClickWorldCoordinates.

В mouseDragged я вычисляю позицию selectedPiece следующим образом:

SCNVector3 worldClickCoordinate = [(SCNView *) self.view unprojectPoint:clickCoordinates.x];

newPiecePosition.x = selectedPiece.position.x + worldClickCoordinate.x - startClickWorldCoordinates.x;
newPiecePosition.y = selectedPiece.position.y + worldClickCoordinate.y - startClickWorldCoordinates.y;
newPiecePosition.z = selectedPiece.position.z + worldClickCoordinate.z - startClickWorldCoordinates.z;

selectedPiece.position = newPiecePosition;

startClickWorldCoordinates = worldClickCoordinate;
person Paolo Godino    schedule 07.08.2014