场景工具包 - 拖动对象

4
我正在尝试使用Scene Kit通过鼠标/触摸板拖动棋子。所有对象(棋盘和棋子)都是从Collada文件加载的根节点的子级。
我在Stack Overflow上找到了一个有用的过程描述(其他地方)。使用该描述,我编写了下面代码的初始版本。我的问题是点击坐标和棋子节点位置之间的差异——它们的坐标数量级不同。我仍然不清楚如何将它们匹配起来,使它们处于“同一宇宙”中。我尝试了许多苹果论坛上的建议以及自己的想象。
这是我目前的尝试,大部分基于上面链接的原始版本,并记录了沿途的坐标值。结果是拖动棋子会导致其突然跳出屏幕:
- (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;
}

到目前为止,我收到了许多有趣和有用的评论和建议。但是我意识到,为了向前发展,我可能需要一个工作代码示例,展示允许点击和矢量存在于“同一宇宙”中的秘密酱汁。

2个回答

1
你不需要“玩弄”来找到起源。你可以像这样编写代码:
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 */

如果你想用不同的深度反投影一个点怎么办?例如,如果你想把触摸坐标转换为相机前面15个单位的点,而不是在Z=0平面上呢? - Crashalot
就像文档所说的那样,您传递给unprojectPoint(参数向量)的向量的z控制着您得到的z位置。为了将触摸映射到可见场景坐标,参数向量的z应该在0和1之间,对应于近和远裁剪平面。在这些术语中找出相机前方15个单位的位置,这将为参数向量中的originZ提供您应该使用的值。 - Donovan Voss

0
你需要使用方法unprojectPoint。关键是通过调整z值来找到场景相对于点击点的正确深度(至少这是我理解的)。我曾经在iOS上尝试拖动触摸时遇到类似的问题。这是我解决它的方法:
let convertedTranslation = scnView.unprojectPoint(SCNVector3Make(Float(translation.x), Float(translation.y), 1.0))

注意该方法的z值为1。从文档中,您可以看到它可以是-1和1之间的值。-1和0对我来说不起作用,但1则有效。


如果您想将一个点反投影到不同深度怎么办?例如,如果您想将触摸坐标转换为相机前方15个单位的点,而不是在Z=0平面上呢? - Crashalot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接