将 matrix_float3x3 旋转转换为 SceneKit
Converting matrix_float3x3 rotation to SceneKit
我正在尝试使用 GameplayKit 的 GKAgent3D class 在场景中移动 SCNNode。我能够使用代理的位置更新 SCNNode,但不能更新旋转。问题是代理的旋转存储为 matrix_float3x3,它与 SceneKit 用于存储旋转信息的任何数据类型都不匹配。
所以我想知道是否有一个简单的函数或方法可以将存储为 matrix_float3x3 的旋转转换为任何 SceneKit 数据类型?
SceneKit 将变换矩阵作为 SCNMatrix4
,并提供从 SIMD matrix_float4x4
转换的实用程序:init(_ m: float4x4)
用于 Swift 和 SCNMatrix4FromMat4
用于 [=34] =]++.
遗憾的是,我没有看到使用 3x3 是 4x4 的左上角的假设在 SIMD 3x3 和 4x4 矩阵之间进行转换的内置方法。 (似乎您希望在 SIMD 库中使用它,所以它值得 filing a bug to Apple 左右。)
但是自己提供一个并不难:只需从列向量构建一个 4x4,使用 3x3 的三个列向量(填充到 float4
向量,w
为零组件)和第四列的身份(0,0,0,1
)。 (实现代码留给 reader,部分原因是我不想为三种语言编写它。)将 float3x3
转换为 float4x4
后,您可以转换为 SCNMatrix4
。
编辑: 在 iOS 11 / tvOS 11 / macOS 10.13(他们为什么不把今年的 macOS 版本也称为 11?),SceneKit 有用于直接使用 float4x4
等 SIMD 类型的一整套并行 API;例如simdTransform
。但是,您仍然需要将 3x3 矩阵转换为 4x4 矩阵。
为了扩展@rickster 的答案,这里有一个很好的方法来获取 Swift 中 4x4 矩阵的左上角 3x3,利用 iOS 中扩展的 simd 支持11/ tvOS 11/ SceneKit High Sierra 版本:
extension float4 {
var xyz: float3 {
return float3(x, y, z)
}
init(_ vec3: float3, _ w: Float) {
self = float4(vec3.x, vec3.y, vec3.z, w)
}
}
extension float4x4 {
var upperLeft3x3: float3x3 {
let (a,b,c,_) = columns
return float3x3(a.xyz, b.xyz, c.xyz)
}
init(rotation: float3x3, position: float3) {
let (a,b,c) = rotation.columns
self = float4x4(float4(a, 0),
float4(b, 0),
float4(c, 0),
float4(position, 1))
}
}
然后,要更新您的代理以匹配您节点的方向,您需要编写:
agent.rotation = node.simdTransform.upperLeft3x3
或者,如果有问题的节点不在 "root" 级别(如 rootNode 的直接子节点),您可能需要使用节点的 worldTransform:
agent.rotation = node.simdWorldTransform.upperLeft3x3
编辑:如果有问题的节点附加了动态物理体,或者正在使用 SCNTransaction 块进行动画处理,则节点的呈现节点将更准确地反映其在屏幕上的当前位置:
agent.position = node.presentation.simdWorldPosition
agent.rotation = node.presentation.simdWorldTransform.upperLeft3x3
编辑:在上面添加了代码以向另一个方向移动节点以匹配代理。
node.simdTransform = float4x4(rotation: agent3d.rotation, position: agent3d.position)
请注意,如果您有一个附加到节点的物理体,如果您要以这种方式直接修改节点的变换,则它应该是 kinematic
而不是 dynamic
。
我正在尝试使用 GameplayKit 的 GKAgent3D class 在场景中移动 SCNNode。我能够使用代理的位置更新 SCNNode,但不能更新旋转。问题是代理的旋转存储为 matrix_float3x3,它与 SceneKit 用于存储旋转信息的任何数据类型都不匹配。
所以我想知道是否有一个简单的函数或方法可以将存储为 matrix_float3x3 的旋转转换为任何 SceneKit 数据类型?
SceneKit 将变换矩阵作为 SCNMatrix4
,并提供从 SIMD matrix_float4x4
转换的实用程序:init(_ m: float4x4)
用于 Swift 和 SCNMatrix4FromMat4
用于 [=34] =]++.
遗憾的是,我没有看到使用 3x3 是 4x4 的左上角的假设在 SIMD 3x3 和 4x4 矩阵之间进行转换的内置方法。 (似乎您希望在 SIMD 库中使用它,所以它值得 filing a bug to Apple 左右。)
但是自己提供一个并不难:只需从列向量构建一个 4x4,使用 3x3 的三个列向量(填充到 float4
向量,w
为零组件)和第四列的身份(0,0,0,1
)。 (实现代码留给 reader,部分原因是我不想为三种语言编写它。)将 float3x3
转换为 float4x4
后,您可以转换为 SCNMatrix4
。
编辑: 在 iOS 11 / tvOS 11 / macOS 10.13(他们为什么不把今年的 macOS 版本也称为 11?),SceneKit 有用于直接使用 float4x4
等 SIMD 类型的一整套并行 API;例如simdTransform
。但是,您仍然需要将 3x3 矩阵转换为 4x4 矩阵。
为了扩展@rickster 的答案,这里有一个很好的方法来获取 Swift 中 4x4 矩阵的左上角 3x3,利用 iOS 中扩展的 simd 支持11/ tvOS 11/ SceneKit High Sierra 版本:
extension float4 {
var xyz: float3 {
return float3(x, y, z)
}
init(_ vec3: float3, _ w: Float) {
self = float4(vec3.x, vec3.y, vec3.z, w)
}
}
extension float4x4 {
var upperLeft3x3: float3x3 {
let (a,b,c,_) = columns
return float3x3(a.xyz, b.xyz, c.xyz)
}
init(rotation: float3x3, position: float3) {
let (a,b,c) = rotation.columns
self = float4x4(float4(a, 0),
float4(b, 0),
float4(c, 0),
float4(position, 1))
}
}
然后,要更新您的代理以匹配您节点的方向,您需要编写:
agent.rotation = node.simdTransform.upperLeft3x3
或者,如果有问题的节点不在 "root" 级别(如 rootNode 的直接子节点),您可能需要使用节点的 worldTransform:
agent.rotation = node.simdWorldTransform.upperLeft3x3
编辑:如果有问题的节点附加了动态物理体,或者正在使用 SCNTransaction 块进行动画处理,则节点的呈现节点将更准确地反映其在屏幕上的当前位置:
agent.position = node.presentation.simdWorldPosition
agent.rotation = node.presentation.simdWorldTransform.upperLeft3x3
编辑:在上面添加了代码以向另一个方向移动节点以匹配代理。
node.simdTransform = float4x4(rotation: agent3d.rotation, position: agent3d.position)
请注意,如果您有一个附加到节点的物理体,如果您要以这种方式直接修改节点的变换,则它应该是 kinematic
而不是 dynamic
。