Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/113.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios SceneKit applyTorque_Ios_Scenekit - Fatal编程技术网

Ios SceneKit applyTorque

Ios SceneKit applyTorque,ios,scenekit,Ios,Scenekit,我正在尝试将RQUE应用于场景中的节点。文件规定: 扭矩矢量的每个分量都与绕转子的旋转有关 SCN节点局部坐标系中的对应轴 包含物理体的对象。例如,施加扭矩为 {0.0,0.0,1.0}会使节点围绕其周围的节点逆时针旋转 z轴 然而,在我的测试中,物理动画似乎并不影响对象的实际位置。因此,轴保持静止(即使实际节点明显移动)。这导致扭矩始终从同一方向施加(场景启动时z轴位于何处) 我希望能够应用扭矩,使其相对于对象始终保持恒定(例如,使节点围绕节点的presentationNode的z轴逆时针旋转

我正在尝试将RQUE应用于场景中的节点。文件规定:

扭矩矢量的每个分量都与绕转子的旋转有关 SCN节点局部坐标系中的对应轴 包含物理体的对象。例如,施加扭矩为 {0.0,0.0,1.0}会使节点围绕其周围的节点逆时针旋转 z轴

然而,在我的测试中,物理动画似乎并不影响对象的实际位置。因此,轴保持静止(即使实际节点明显移动)。这导致扭矩始终从同一方向施加(场景启动时z轴位于何处)


我希望能够应用扭矩,使其相对于对象始终保持恒定(例如,使节点围绕节点的presentationNode的z轴逆时针旋转,而不是场景启动时节点的位置)

SceneKit使用每个节点的两个版本:模型节点定义静态行为,表示节点是动态行为中实际涉及并在屏幕上使用的节点。此分区镜像了核心动画中使用的部分,并启用了隐式动画等功能(您可以执行设置
节点.position
并将其动画化为新值,而无需查询
节点.position
的代码的其他部分在动画期间处理中间值)

物理操作是在演示节点上进行的,但在某些情况下(如本例)会在场景空间中进行输入

但是,表示节点和场景之间的唯一区别在于坐标空间,因此您需要做的就是将向量从表示空间转换为场景空间。(场景的根节点不应通过物理、动作或空中动画进行变换,因此模型场景空间和演示场景空间之间没有实际区别。)为此,请使用SceneKit提供的坐标转换方法之一,例如

这里有一个快速的游乐场,可以说明你的困境:

import Cocoa
import SceneKit
import XCPlayground

// Set up a scene for our tests
let scene = SCNScene()
let view = SCNView(frame: NSRect(x: 0, y: 0, width: 500, height: 500))
view.autoenablesDefaultLighting = true
view.scene = scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
scene.rootNode.addChildNode(cameraNode)
XCPShowView("view", view)

// Make a pyramid to test on
let node = SCNNode(geometry: SCNPyramid(width: 1, height: 1, length: 1))
scene.rootNode.addChildNode(node)
node.physicsBody = SCNPhysicsBody.dynamicBody()
scene.physicsWorld.gravity = SCNVector3Zero // Don't fall off screen

// Rotate around the axis that looks into the screen
node.physicsBody?.applyTorque(SCNVector4(x: 0, y: 0, z: 1, w: 0.1), impulse: true)

// Wait a bit, then try to rotate around the y-axis
node.runAction(SCNAction.waitForDuration(10), completionHandler: {
    var axis = SCNVector3(x: 0, y: 1, z: 0)
    node.physicsBody?.applyTorque(SCNVector4(x: axis.x, y: axis.y, z: axis.z, w: 1), impulse: true)
})
第二次旋转有效地围绕屏幕的y轴旋转棱锥体,而不是通过棱锥体顶点的y轴。正如你所注意到的,在第一次旋转之前,它围绕着金字塔的y轴旋转;i、 e.场景的y轴(不受物理影响),而不是演示节点的y轴(通过物理旋转)

要修复此问题,请插入以下行(在以
var axis
开头的行之后):

调用会说“在场景坐标空间中给我一个向量,它相当于表示节点空间中的向量”。当您围绕转换的轴应用扭矩时,它会有效地转换回表示节点的空间以模拟物理,因此您可以看到它围绕您想要的轴旋转



更新:有些坐标空间错误,但最终结果几乎相同。

不幸的是,rickster提供的解决方案对我不起作用:(

为了解决这个难题,我创建了(我认为是)一个非常不标准的解决方案(更多的是概念证明)。它涉及到在我试图找到的轴上创建(空)对象,然后我使用它们的位置来找到与轴对齐的向量

因为我有一个相当复杂的场景,所以我从COLLADA文件加载它。在该文件中,我模拟了一个简单的坐标三脚架:三个正交的圆柱体,顶部有圆锥体(这使我们更容易看到正在发生的事情)

然后,我将这个三脚架对象约束到我试图施加扭矩的对象上。通过这种方式,我可以在我试图施加扭矩的对象的presentationNode的轴上检索两点。然后,我可以使用这两点来确定施加扭矩的向量

// calculate orientation vector in the most unimaginative way possible

// retrieve axis tripod objects. We will be using these as guide objects.
// The tripod is constructed as a cylinder called "Xaxis" with a cone at the top.
// All loaded from an external COLLADA file.

SCNNode *XaxisRoot = [scene.rootNode childNodeWithName:@"XAxis" recursively:YES];
SCNNode *XaxisTip = [XaxisRoot childNodeWithName:@"Cone" recursively:NO];


// To devise the vector we will need two points. One is the root of our tripod,
// the other is at the tip. First, we get their positions. As they are constrained
// to the _rotatingNode, presentationNode.position is always the same .position
// because presentationNode returns position in relation to the parent node.

SCNVector3 XaxisRootPos = XaxisRoot.position;
SCNVector3 XaxisTipPos = XaxisTip.position;


// We then convert these two points into _rotatingNode coordinate space. This is
// the coordinate space applyTorque seems to be using.

XaxisRootPos = [_rotatingNode convertPosition:XaxisRootPos fromNode:_rotatingNode.presentationNode];
XaxisTipPos = [_rotatingNode convertPosition:XaxisTipPos fromNode:_rotatingNode.presentationNode];

// Now, we have two *points* in _rotatingNode coordinate space. One is at the center
// of our _rotatingNode, the other is somewhere along it's Xaxis. Subtracting them
// will give us the *vector* aligned to the x axis of our _rotatingNode

GLKVector3 rawXRotationAxes = GLKVector3Subtract(SCNVector3ToGLKVector3(XaxisRootPos), SCNVector3ToGLKVector3(XaxisTipPos));

// we now normalise this vector
GLKVector3 normalisedXRotationAxes = GLKVector3Normalize(rawXRotationAxes);

//finally we are able to apply toque reliably
[_rotatingNode.physicsBody applyTorque:SCNVector4Make(normalisedXRotationAxis.x,normalisedXRotationAxis.y,normalisedXRotationAxis.z, 500) impulse:YES];

正如您可能看到的,我在SceneKit方面非常缺乏经验,但即使是我也可以看到更简单/优化的解决方案确实存在,但我无法找到:(

我最近遇到了同样的问题,如何将扭矩从对象的局部空间转换为
applyTorque
方法所需的世界空间。使用节点的
convertPosition:toNode
fromNode
方法的问题是,它们也将节点的平移应用于扭矩,因此这仅在节点位于0,0,0时有效。这些方法所做的是将SCInvector3视为w分量为1.0的vec4。我们只想应用旋转,换句话说,我们希望vec4的w分量为0。与SceneKit不同,GLKit为我们提供了两个选项,用于如何让VEC3相乘:

在哪里

将输入向量视为w分量为0.0的4分量向量

在哪里

将输入向量视为w分量为1.0的4分量向量

我们想要的是前者,只是旋转,不是平移

因此,我们可以往返到GLKit。例如,将局部x轴(1,0,0),例如俯仰旋转,转换为施加扭矩所需的全局轴,如下所示:

    let local = GLKMatrix4MultiplyVector3(SCNMatrix4ToGLKMatrix4(node.presentationNode.worldTransform), GLKVector3(v: (1,0,0)))
    node.physicsBody?.applyTorque(SCNVector4(local.x, local.y, local.z, 10), impulse: false)
然而,更快捷的方法是为mat4*vec3添加一个
*
操作符,该操作符将vec3视为具有0.0 w组件的vec4。如下所示:

func * (left: SCNMatrix4, right: SCNVector3) -> SCNVector3 { //multiply mat4 by vec3 as if w is 0.0
    return SCNVector3(
        left.m11 * right.x + left.m21 * right.y + left.m31 * right.z,
        left.m12 * right.x + left.m22 * right.y + left.m32 * right.z,
        left.m13 * right.x + left.m23 * right.y + left.m33 * right.z
    )
}
虽然这个操作符假设我们希望向量3如何相乘,但我的推理是,由于
convertPosition
方法已经将w视为1,因此有一个
*
操作符也这样做是多余的

您还可以添加一个mat4*SCONVERTOR4运算符,让用户明确地选择是否希望w为0或1

因此,我们不必从SceneKit往返到GLKit,只需写下:

    let local = node.presentationNode.worldTransform * SCNVector3(1,0,0)
    node.physicsBody?.applyTorque(SCNVector4(local.x, local.y, local.z, 10), impulse: false)
可以使用此方法使用一个
applyTorque
c在多个轴上应用旋转
    let local = node.presentationNode.worldTransform * SCNVector3(1,0,0)
    node.physicsBody?.applyTorque(SCNVector4(local.x, local.y, local.z, 10), impulse: false)