首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SceneKit applyTorque

SceneKit applyTorque
EN

Stack Overflow用户
提问于 2014-11-15 15:44:40
回答 3查看 2.2K关注 0票数 4

我正在尝试applyTorque到我场景中的一个节点。这些文件指出:

扭矩矢量的每个分量与包含物理体的SCNNode物体的局部坐标系中围绕对应轴的旋转有关。例如,应用{0.0,0.0,1.0}的扭矩会导致节点逆时针旋转其z轴。

然而,在我的测试中,物理动画似乎不影响物体的实际位置。因此,轴保持静态(即使实际节点明显地移动)。这导致扭矩总是从相同的方向施加(当场景开始时,z轴在哪里)。

我想要能够应用扭矩,以便它总是常数相对于对象(例如,使节点逆时针旋转围绕节点的presentationNode的z轴,而不是位置节点有(had?)当场景启动时)

EN

回答 3

Stack Overflow用户

发布于 2014-11-19 21:40:56

SceneKit使用每个节点的两个版本:模型节点定义静态行为,表示节点实际上涉及到动态行为并在屏幕上使用。这种划分反映了核心动画中使用的功能,并启用了诸如隐式动画之类的功能(在这里,您可以将node.position设置为新值,而无需查询node.position的其他代码部分在动画期间处理中间值)。

物理在表示节点上运行,但在某些情况下--比如这个--需要在场景空间中输入。

然而,表示节点和场景之间唯一的区别在于坐标空间,所以您所需要做的就是将矢量从表示空间转换为场景空间。(场景的根节点不应该被物理、动作或动画转换,因此模型场景空间和表示场景空间之间没有实际差别。)为此,请使用SceneKit提供的坐标转换方法之一,如convertPosition:fromNode:

这里有一个斯威夫特游乐场,说明了你的困境:

代码语言:javascript
复制
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轴旋转;即场景的y轴(不受物理影响),而不是表示节点(通过物理旋转)的y轴。

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

代码语言:javascript
复制
axis = scene.rootNode.convertPosition(axis, fromNode: node.presentationNode())

convertPosition:fromNode:的调用说:“在场景坐标空间中给我一个与表示节点空间中的向量等价的向量”。当你在转换轴周围施加扭矩时,它会有效地转换回表示节点的空间来模拟物理,所以你会看到它绕着你想要的轴旋转。

更新:出现了一些坐标空间错误,但最终结果几乎是一样的。

票数 2
EN

Stack Overflow用户

发布于 2014-11-22 18:46:47

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

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

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

然后,我将这个三脚架对象约束到我试图应用扭矩的对象上。这样,我有对象,可以让我检索两个点的presentationNode轴上的对象,我试图应用扭矩。然后,我可以用这两点来确定矢量来应用扭矩。

代码语言:javascript
复制
// 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方面非常缺乏经验,但是即使我也可以看到简单得多的优化解决方案确实存在,但我找不到它:

票数 1
EN

Stack Overflow用户

发布于 2016-06-19 10:49:37

最近,我遇到了同样的问题,如何将扭矩从对象的局部空间转换为applyTorque方法所需的世界空间。使用节点的convertPosition:toNodefromNode方法的问题是,它们也在将节点的转换应用于扭矩,因此只有当节点在0、0、0时才能工作。这些方法所做的是将SCNVector3视为w组件为1.0的vec4。我们只想应用旋转,换句话说,我们希望vec4的w分量是0。与SceneKit不同的是,GLKit为我们提供了两种选择,说明我们希望如何将vec3s相乘:

GLKMatrix4MultiplyVector3,其中

输入向量被视为4分量向量,w分量为0.0.

GLKMatrix4MultiplyVector3WithTranslation在哪里

输入向量被视为4分量向量,w分量为1.0.

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

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

代码语言:javascript
复制
    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添加一个mat4* vec3操作符,它将vec3当作一个带有0.0w组件的vec4。如下所示:

代码语言:javascript
复制
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
    )
}

虽然这个操作符假设我们希望我们的vec3s被乘以,但我在这里的推理是,由于convertPosition方法已经将w作为1来处理,如果有一个*操作符也这样做的话,那将是多余的。

您还可以添加一个mat4 * SCNVector4操作符,它允许用户选择他们希望w是0还是1。

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

代码语言:javascript
复制
    let local = node.presentationNode.worldTransform * SCNVector3(1,0,0)
    node.physicsBody?.applyTorque(SCNVector4(local.x, local.y, local.z, 10), impulse: false)

您可以使用此方法通过一个applyTorque调用在多个轴上应用旋转。因此,如果您有棒输入,您希望在棒上的x是偏航(本地yUp-轴)和y在棍子上是螺距(本地x-轴),但在飞-sim风格的“向下拉回/向上”,那么您可以设置为SCNVector3(input.y, -input.x, 0)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26947544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档