Подтвердить что ты не робот

Как найти точку мыши в сцене, используя SceneKit?

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

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

Тест-поцелуй работает как шарм, но как перевести точку мыши из события перетаскивания в новую позицию в сцене для 3D-объекта, который я перетаскиваю?

4b9b3361

Ответ 1

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

Здесь вы можете использовать подход...

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

  • Проверьте свойство worldCoordinates этого результата теста. Если node, который вы хотите переместить, является дочерним элементом сцены rootNode, это вектор, который вы хотите найти смещение. (В противном случае вам нужно будет преобразовать его в систему координат родителя node, которую вы хотите переместить - см. convertPosition:toNode: или convertPosition:fromNode:.)

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

  • Вычитайте position node, который вы хотите переместить с вектора мыши, который вы получили на шаге 2. Это дает вам смещение щелчка мыши от позиции объекта. Удерживайте этот вектор - вам понадобится его, пока не перетащите концы.

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

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

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

Ответ 2

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

Я спросил на форумах 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;
}

Ответ 3

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

В 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;