目标:定义由
SCNPlane
表示的平面的法向量。
正如 Apple 文档使用向量 - 计算三角形的法线中提到的,我可以根据平面上的 3 个点计算法线向量。这里的问题是我不知道如何才能得到它们构成正确三角形的 3 个点。我注意到
SCNPlane
有一个属性 boundingBox
并且它可以代表 2 个三角形顶点(min
和 max
)。如何找到位于 SCNPlane
上的第三个顶点?我无法使用 SCNPlane
的中心,因为它会从 min
与 max
和 boundingBox
点一起创建一条线。
有没有其他方法可以帮助我检索
SCNPlane
的法线向量?
从文档我们了解到
表面是单面的。它的表面法线向量指向其局部坐标空间的 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))
要添加到已接受的答案中,为了检索定义平面的点,您可以查询平面的几何源。
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)]]
如果将平面 (SCNPlane) 分配为
geometry
到 node
(SCNNode),则简单:
node.simdOrientation.act(simd_float3(0.0, 0.0, 1.0))
应该做。