如何计算 SCNPlane 的法向量?
How to calculate normal vector of a SCNPlane?
目标:定义由 SCNPlane
表示的平面的法向量。
正如Apple文档中提到的Working with Vectors - Calculate the Normal of a Triangle,我可以根据平面的3个点来计算法向量。这里的问题是我不知道如何得到 3 点,它们构成了正确的三角形。我注意到 SCNPlane
有一个 属性 boundingBox
并且它可以代表 2 个三角形顶点(min
和 max
)。如何找到 SCNPlane
上的第三个顶点?我不能使用 SCNPlane
的中心,因为它会创建一条线以及来自 boundingBox
.
的 min
和 max
点
有没有其他方法可以帮助我检索 SCNPlane
的法向量?
从documentation我们得知
The surface is one-sided. Its surface normal vectors point in the positive z-axis direction of its local coordinate space, so it is only visible from that direction by default.
SCNPlane
的法线在局部 space 中始终是 (0, 0, 1)
,并且无法更改。
当它附加到一个节点时,该节点的方向决定了另一个坐标系中的法线。您可以使用 simdConvertVector:toNode:
在坐标 spaces:
之间进行转换
// 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
表示的平面的法向量。
正如Apple文档中提到的Working with Vectors - Calculate the Normal of a Triangle,我可以根据平面的3个点来计算法向量。这里的问题是我不知道如何得到 3 点,它们构成了正确的三角形。我注意到 SCNPlane
有一个 属性 boundingBox
并且它可以代表 2 个三角形顶点(min
和 max
)。如何找到 SCNPlane
上的第三个顶点?我不能使用 SCNPlane
的中心,因为它会创建一条线以及来自 boundingBox
.
min
和 max
点
有没有其他方法可以帮助我检索 SCNPlane
的法向量?
从documentation我们得知
The surface is one-sided. Its surface normal vectors point in the positive z-axis direction of its local coordinate space, so it is only visible from that direction by default.
SCNPlane
的法线在局部 space 中始终是 (0, 0, 1)
,并且无法更改。
当它附加到一个节点时,该节点的方向决定了另一个坐标系中的法线。您可以使用 simdConvertVector:toNode:
在坐标 spaces:
// 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)]]