Набор сцен - перетаскивание объекта

Я пытаюсь перетащить шахматную фигуру с помощью мыши / трекпада, используя Scene Kit. Все объекты (доска и фигуры) являются дочерними по отношению к корневому узлу, загруженному из файла Collada.

Я нашел полезное описание процесса в другом месте на Stack Overflow. Используя это описание, я написал начальную версию кода ниже. Моя проблема заключается в несоответствии между координатами щелчка и положением узла детали - их координаты различаются по порядку величины. Я остаюсь неясным, как их сопоставить - поместить их в «одну вселенную». Я испробовал ряд предложений с форумов 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 17.05.2014    source источник


Ответы (2)


Вам не нужно «играть», чтобы найти источник. Вы можете сделать такой код:

let originZ = sceneView.projectPoint(SCNVector3Zero).z
let viewLocation = /* Location in SCNView coordinate system */
let viewLocationWithOriginZ = SCNVector3(
  x: Float(viewLocation.x), // X & Y are in view coordinate system
  y: Float(viewLocation.y),
  z: originZ                // Z is in scene coordinate system
)
var position = sceneView.unprojectPoint(viewLocationWithOriginZ)
/* "position" is in the scene's coordinate system with z of 0 */
person Donovan Voss    schedule 27.01.2015
comment
Что делать, если вы хотите спроецировать точку с другой глубиной? Например, вместо плоскости Z = 0, что, если вы хотите преобразовать координаты касания в точку в 15 единицах перед камерой? - person Crashalot; 11.09.2016
comment
Как говорится в документации, z вектора, который вы передаете в unprojectPoint (вектор параметров), управляет тем, какое положение z вы получите обратно. Чтобы сопоставить касание с видимой координатой сцены, вектор параметра z должен быть между 0 и 1, что соответствует ближней и дальней панелям отсечения. Выясните, где находятся 15 единиц перед камерой, в этих терминах, и это даст вам значение, которое вы должны использовать для originZ в векторе параметров. - person Donovan Voss; 23.09.2016

Вы должны использовать метод unprojectPoint. Ключ в том, чтобы поиграть с z-значениями, чтобы найти правильную глубину вашей сцены относительно точки щелчка (по крайней мере, так я это понял). У меня была аналогичная проблема с перетаскиванием касанием на iOS. Вот что я сделал, чтобы решить эту проблему:

let convertedTranslation = scnView.unprojectPoint(SCNVector3Make(Float(translation.x), Float(translation.y), 1.0))

Обратите внимание, что z-значение для метода равно 1. Из документации вы можете видеть, что это может быть значение от -1 до 1. -1 и 0 не работали для меня, но 1 сработало.

person Tino    schedule 08.11.2014
comment
Что делать, если вы хотите спроецировать точку с другой глубиной? Например, вместо плоскости Z = 0, что, если вы хотите преобразовать координаты касания в точку в 15 единицах перед камерой? - person Crashalot; 11.09.2016