【问题标题】:Converting matrix_float3x3 rotation to SceneKit将 matrix_float3x3 旋转转换为 SceneKit
【发布时间】:2017-07-26 21:50:56
【问题描述】:

我正在尝试使用 GameplayKit 的 GKAgent3D 类在场景中移动 SCNNode。我能够使用代理的位置更新 SCNNode,但不能使用旋转。代理的旋转问题存储为 matrix_float3x3,与 SceneKit 用于存储旋转信息的任何数据类型都不匹配。

那么我想知道的是,是否有一个简单的函数或方法可以将存储为 matrix_float3x3 的旋转转换为任何 SceneKit 数据类型?

【问题讨论】:

    标签: scenekit


    【解决方案1】:

    SceneKit 将转换矩阵作为SCNMatrix4,并提供从 SIMD 转换的实用程序matrix_float4x4init(_ m: float4x4) 用于 Swift,SCNMatrix4FromMat4 用于 ObjC/C++。

    遗憾的是,假设 3x3 是 4x4 的左上角,我没有看到在 SIMD 3x3 和 4x4 矩阵之间进行转换的内置方法。 (似乎您希望在 SIMD 库中这样做,因此值得 filing a bug to Apple 左右。)

    但自己提供一个并不难:只需从列向量构造一个 4x4,使用 3x3 的三个列向量(填充到 float4 向量,w 组件为零)和标识第四列 (0,0,0,1)。 (实现代码留给读者,部分原因是我不想为三种语言编写。)将float3x3转换为float4x4后,您可以转换为SCNMatrix4


    编辑:在 iOS 11 / tvOS 11 / macOS 10.13 中(他们为什么不直接调用今年的 macOS 版本 11 呢?),SceneKit 拥有一整套使用 SIMD 的并行 API直接输入float4x4;例如simdTransform。但是,您仍然需要将 3x3 转换为 4x4 矩阵。

    【讨论】:

    • 谢谢 Rickster,非常感谢您的详尽回答,这对我很有帮助。至少现在我知道我需要做什么,我正在搜索 simd 和其他库,只是希望我可能只是忽略了一个方法/函数。最后一件事,我看过你对各种问题的其他答案,你是一个可怕的巫师,我希望有一天能像你一样博学。再次感谢!
    • Hey Ryan 很抱歉回复晚了,我手头没有任何示例代码,但如果您对实现有具体问题,请告诉我。
    • 感谢 Rickster 的更新,很高兴他们添加了 SIMD 类型,我真的很期待使用它们 8)。
    【解决方案2】:

    为了扩展@rickster 的答案,这是在 Swift 中采用 4x4 矩阵的左上角 3x3 的好方法,利用 SceneKit 的 iOS 11/tvOS 11/High Sierra 版本中扩展的 simd 支持:

    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
    

    或者,如果有问题的节点不在“根”级别(例如,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

    【讨论】:

    • 感谢发布,非常有帮助!这段代码比我想出的要干净得多。
    • 同意,这真的很有帮助。
    • 天才!非常感谢。
    • 谢谢。任何人都可以评论为什么其中一些内容不在文档中,或者从哪里得知这是你应该做的事情......
    猜你喜欢
    • 2018-01-31
    • 2017-10-29
    • 2015-02-20
    • 2016-07-29
    • 1970-01-01
    • 2018-06-05
    • 2018-03-25
    • 2018-12-17
    • 2015-02-14
    相关资源
    最近更新 更多