如何计算SCNPlane的法向量?

问题描述 投票:0回答:3

目标:定义由

SCNPlane
表示的平面的法向量。

正如 Apple 文档使用向量 - 计算三角形的法线中提到的,我可以根据平面上的 3 个点计算法线向量。这里的问题是我不知道如何才能得到它们构成正确三角形的 3 个点。我注意到

SCNPlane
有一个属性
boundingBox
并且它可以代表 2 个三角形顶点(
min
max
)。如何找到位于
SCNPlane
上的第三个顶点?我无法使用
SCNPlane
的中心,因为它会从
min
max
boundingBox
点一起创建一条线。

有没有其他方法可以帮助我检索

SCNPlane
的法线向量?

ios swift scenekit arkit
3个回答
3
投票

文档我们了解到

表面是单面的。它的表面法线向量指向其局部坐标空间的 z 轴正方向,因此默认情况下仅从该方向可见。

SCNPlane
的法线在局部空间中始终为
(0, 0, 1)
并且无法改变。

当它附加到节点时,该节点的方向决定另一个坐标系中的法线。您可以使用

simdConvertVector:toNode:
在坐标空间之间进行转换:

// normal expressed in world space
let normal = simd_normalize(node.simdConvertVector(simd_float3(0, 0, 1), to: nil))

1
投票

要添加到已接受的答案中,为了检索定义平面的点,您可以查询平面的几何源。

let plane = SCNPlane(width: 100, height: 20)
print("Sources for normal: \(vertices(sources: plane.sources(for: .normal)))")
print("Sources for vertex: \(vertices(sources: plane.sources(for: .vertex)))")

extension UnsafeRawPointer {
    func loadUnaligned<T>(as: T.Type, count: Int) -> [T] {
        assert(_isPOD(T.self)) // relies on the type being POD (no refcounting or other management)
        let buffer = UnsafeMutablePointer<T>.allocate(capacity: count)
        defer { buffer.deallocate() }
        memcpy(buffer, self, MemoryLayout<T>.size * count)
        return (0..<count).map({ index in buffer.advanced(by: index).pointee })
    }
}

func vertices(sources: [SCNGeometrySource]) -> [[SCNVector3]] {

    var result = [[SCNVector3]]()
    result.reserveCapacity(sources.count)
    for source in sources {
        precondition(source.usesFloatComponents == true, "SCNVector3 can handle only three-component vectors whose components are floating-point values, i.e., floats or doubles")
        precondition(source.componentsPerVector == 3, "SCNVector3 can only be used for three components per vector")
        
        let shouldUseFloatNotDouble: Bool
        if source.bytesPerComponent == 4 {
            shouldUseFloatNotDouble = true
        }
        else if source.bytesPerComponent == 8 {
            shouldUseFloatNotDouble = false
        }
        else {
            assert(false, "The SCNGeometrySource has reported an unexpected byte size for its vector components, not 4 bytes (float) or 8 bytes (double) but \(source.bytesPerComponent). I am not equipped for this so I am going to use floats and hope for the best. This will probably not work. Sorry.")
            shouldUseFloatNotDouble = true
        }
        
        let vectors = source.data.withUnsafeBytes {
            (p: UnsafeRawBufferPointer) -> [SCNVector3] in
            if (shouldUseFloatNotDouble) {
                let simdArray = (p.baseAddress! + source.dataOffset).loadUnaligned(as: SIMD3<Float>.self, count: source.vectorCount)
                return simdArray.map { simd in SCNVector3(simd)}
            } else {
                let simdArray = (p.baseAddress! + source.dataOffset).loadUnaligned(as: SIMD3<Double>.self, count: source.vectorCount)
                return simdArray.map { simd in SCNVector3(simd)}
            }
        }
        result.append(vectors)
    }
    return result
}

输出:

Sources for normal: [[__C.SCNVector3(x: 0.0, y: 0.0, z: 1.0), __C.SCNVector3(x: 1.0, y: 0.5, z: -0.5), __C.SCNVector3(x: 0.0, y: 0.0, z: 1.0), __C.SCNVector3(x: 1.0, y: -0.5, z: 0.5)]]
Sources for vertex: [[__C.SCNVector3(x: -0.5, y: -0.5, z: 0.0), __C.SCNVector3(x: 0.0, y: 1.0, z: 0.0), __C.SCNVector3(x: 0.5, y: -0.5, z: 0.0), __C.SCNVector3(x: 0.0, y: 1.0, z: 1.0)]]

0
投票

如果将平面 (SCNPlane) 分配为

geometry
node
(SCNNode),则简单:

node.simdOrientation.act(simd_float3(0.0, 0.0, 1.0))

应该做。

© www.soinside.com 2019 - 2024. All rights reserved.