SceneKit:在3Dspace中将一个节点定向到另一个节点

时间:2016-01-27 04:37:42

标签: rotation scenekit scnnode

我需要将一个节点定向为将其Z轴指向另一个3D节点。是的,LookAtConstraint的完美工作。对于我的大多数工作,LookAt都很好。但是,当我将LookAt应用于特定节点时,我无法再使用SCNAction为该节点的转换设置动画。想象一下氢原子在离子化时离开分子。需要使取向适当地旋转分子上的氢和氧原子之间的键(圆柱)。

我可以将氧从氧气定向到氢气和动力。但是这使得大多数其他债券与LookAt的关系变得很糟糕。

在实现它回答一个稍微不同的问题之前,我给了它一个强大的尝试: https://codepen.io/friendfx/pen/RrjpeL

2 个答案:

答案 0 :(得分:1)

我在项目中遇到了类似的问题。我最终意识到我需要使用多个约束。一个用于翻译(移动),另一个用于查看约束。

我会移动对象,然后应用约束;在这种情况下,它是跟踪使用动作移动对象的相机。代码段如下:

let targetNodeConstraint = SCNLookAtConstraint(target: someObject) targetNodeConstraint.gimbalLockEnabled = true
let followObjectConstraint = SCNTransformConstraint(inWorldSpace: true, withBlock: { (node, matrix) -> SCNMatrix4 in let transformMatrix = SCNMatrix4MakeTranslation(
self.someObject.position.x - 1.0,
self.someObject.position.y, self.someObject.position.z + 1.0) return transformMatrix }) // Position the object behind the other object & rotate it to roadCamera.constraints = [followObjectConstraint, targetNodeConstraint]

需要注意的重要事项是使用数组将约束添加到对象的顺序。在上面的代码中,我在应用变换矩阵之前忽略了当前的矩阵(我有一天应该重新编写这段代码)

这个“实验”的完整源代码是在我尝试的时候在GitHub上。

https://github.com/ManjitBedi/CubeTrip

希望这很有用。

答案 1 :(得分:0)

我的解决方案。处理节点在空间中不断转换的情况,并且应始终朝向某个位置。

@discardableResult
func yew(_ node:SCNNode, toPosition position:SCNVector3) -> Float
{
    var eularAngle = SCNVector3Zero
    let tranform = node.transform
    var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
    var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
    forward = GLKVector3Normalize(GLKVector3Make(forward.x, 0, forward.z))
    toWard = GLKVector3Normalize(GLKVector3Make(toWard.x, 0, toWard.z))
    var dotProduct = GLKVector3DotProduct(forward,toWard)
    dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
    var yew = acos(dotProduct)
    if yew < 0 {
        assert(false)
    }
    //toward is clockwise of forward
    let isCW = GLKVector3CrossProduct(forward, toWard).y < 0
    if isCW {
        yew = -yew
    }
    eularAngle.y = yew
    node.eulerAngles = SCNVector3Make(eularAngle.x + wrapperNode.eulerAngles.x,
                                      eularAngle.y + wrapperNode.eulerAngles.y,
                                      eularAngle.z + wrapperNode.eulerAngles.z)
    return yew
}
@discardableResult
func pitch(_ node:SCNNode, toPosition position:SCNVector3) -> Float{
    var eularAngle = SCNVector3Zero
    let tranform = node.transform
    var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
    var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
    forward = GLKVector3Normalize(forward)
    toWard = GLKVector3Normalize(toWard)
    var dotProduct = GLKVector3DotProduct(forward,toWard)
    dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
    var pitch = acos(dotProduct)
    //toward is clockwise of forward, if right vector of model and crossProfuct.x has same direction
    let crossProduct = GLKVector3CrossProduct(forward, toWard)
    let isCW = (crossProduct.x <= 0) != (tranform.m11 <= 0)
    if isCW {
        pitch = -pitch
    }

    eularAngle.x = pitch

    node.eulerAngles = SCNVector3Make(eularAngle.x + node.eulerAngles.x,
                                      eularAngle.y + node.eulerAngles.y,
                                      eularAngle.z + node.eulerAngles.z)
    return pitch
}
func orient(_ node:SCNNode, toPosition position:SCNVector3) {
    self.yew(node, toPosition: position)
    self.pitch(node, toPosition: position)
}