说明
生成一个带有圆角的长方体,或正方体。RealityKit 自带了生成圆角长方体的方法,但自带的只在圆角处增加了网格,我们的网格是均匀分布的,两种方式各有优劣,可根据需要选用。
几何
主要思路与上一篇生成圆角平台侧面法线的思路类似。这次我们先生成一个正常的长方体,然后同时对 xyz 轴坐标进行判断,得到内部的小长方体,然后就得到了法线。
再将小长方体的点沿法线移动一段距离,就得到了带圆角的长方体。如果圆角大小等于边长的一半,那么还会得到一个六面球体。
代码
public static func generateRoundedCube(width: Float, height: Float, depth: Float, radius: Float, widthResolution: Int = 10, heightResolution: Int = 10, depthResolution: Int = 10, splitFaces: Bool = false) throws -> MeshResource {
var descr = MeshDescriptor()
var meshPositions: [SIMD3<Float>] = []
var indices: [UInt32] = []
var normals: [SIMD3<Float>] = []
var textureMap: [SIMD2<Float>] = []
var materials: [UInt32] = []
let widthHalf = width * 0.5
let heightHalf = height * 0.5
let depthHalf = depth * 0.5
let minDim = min(widthHalf, min(heightHalf, depthHalf))
let radius = radius > minDim ? minDim : radius
let edgeWidth = widthResolution > 1 ? widthResolution : 2
let edgeHeight = heightResolution > 1 ? heightResolution : 2
let edgeDepth = depthResolution > 1 ? depthResolution : 2
let widthInc = width / Float(edgeWidth - 1)
let heightInc = height / Float(edgeHeight - 1)
let depthInc = depth / Float(edgeDepth - 1)
let xPointCount = edgeDepth * edgeHeight
let yPointCount = edgeWidth * edgeHeight
let zPointCount = edgeDepth * edgeWidth
// +X
for j in 0..<edgeDepth {
let startY = depthHalf
let startZ = heightHalf
let jf = Float(j)
let uvy = jf / Float(edgeDepth - 1)
for i in 0..<edgeHeight {
let p = SIMD3<Float>(widthHalf, startY - depthInc * jf, startZ - heightInc * Float(i))
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeHeight - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeDepth - 1 && i != edgeHeight - 1 {
let index = UInt32(i + j * edgeHeight)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeHeight)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
// -X
for j in 0..<edgeDepth {
let startY = depthHalf
let startZ = -heightHalf
let jf = Float(j)
let uvy = jf / Float(edgeDepth - 1)
for i in 0..<edgeHeight {
let p = SIMD3<Float>(-widthHalf, startY - depthInc * jf, startZ + heightInc * Float(i))
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeHeight - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeDepth - 1 && i != edgeHeight - 1 {
let index = UInt32(i + j * edgeHeight + xPointCount)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeHeight)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
if splitFaces {
let count = (edgeDepth - 1) * (edgeHeight - 1)
materials.append(contentsOf: Array(repeating: 0, count: count * 4))
}
// +Y
for j in 0..<edgeHeight {
let startX = -widthHalf
let startZ = -heightHalf
let jf = Float(j)
let uvy = jf / Float(edgeHeight - 1)
for i in 0..<edgeWidth {
let p = SIMD3<Float>(startX + widthInc * Float(i), depthHalf, startZ + heightInc * jf)
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeWidth - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeHeight - 1 && i != edgeWidth - 1 {
let index = UInt32(i + j * edgeWidth + xPointCount * 2)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeWidth)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
// -Y
for j in 0..<edgeHeight {
let startX = widthHalf
let startZ = -heightHalf
let jf = Float(j)
let uvy = jf / Float(edgeHeight - 1)
for i in 0..<edgeWidth {
let p = SIMD3<Float>(startX - widthInc * Float(i), -depthHalf, startZ + heightInc * jf)
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeWidth - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeHeight - 1 && i != edgeWidth - 1 {
let index = UInt32(i + j * edgeWidth + xPointCount * 2 + yPointCount)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeWidth)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
if splitFaces {
let count = (edgeWidth - 1) * (edgeHeight - 1)
materials.append(contentsOf: Array(repeating: 1, count: count * 4))
}
// +Z
for j in 0..<edgeDepth {
let startY = depthHalf
let startX = -widthHalf
let jf = Float(j)
let uvy = jf / Float(edgeDepth - 1)
for i in 0..<edgeWidth {
let p = SIMD3<Float>(startX + widthInc * Float(i), startY - depthInc * jf, heightHalf)
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeWidth - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeDepth - 1 && i != edgeWidth - 1 {
let index = UInt32(i + j * edgeWidth + xPointCount * 2 + yPointCount * 2)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeWidth)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
// -Z
for j in 0..<edgeDepth {
let startY = depthHalf
let startX = widthHalf
let jf = Float(j)
let uvy = jf / Float(edgeDepth - 1)
for i in 0..<edgeWidth {
let p = SIMD3<Float>(startX - widthInc * Float(i), startY - depthInc * jf, -heightHalf)
meshPositions.append(p)
let uv = SIMD2<Float>(Float(i) / Float(edgeWidth - 1), 1 - uvy)
textureMap.append(uv)
if j != edgeDepth - 1 && i != edgeWidth - 1 {
let index = UInt32(i + j * edgeWidth + xPointCount * 2 + yPointCount * 2 + zPointCount)
let tl = index
let tr = tl + 1
let bl = index + UInt32(edgeWidth)
let br = bl + 1
indices.append(contentsOf: [tl,bl,tr,
tr,bl,br])
}
}
}
if splitFaces {
let count = (edgeWidth - 1) * (edgeDepth - 1)
materials.append(contentsOf: Array(repeating: 2, count: count * 4))
}
let innerWidth = width - radius * 2
let innerHeight = height - radius * 2
let innerDepth = depth - radius * 2
let lower = SIMD3<Float>(-innerWidth * 0.5, -innerHeight * 0.5, -innerDepth * 0.5)
let upper = SIMD3<Float>(innerWidth * 0.5, innerHeight * 0.5, innerDepth * 0.5)
var roundPositions: [SIMD3<Float>] = []
for p in meshPositions {
let inner = p.clamped(lowerBound: lower, upperBound: upper)
let n = simd_normalize(p - inner)
normals.append(n)
roundPositions.append(inner + n * radius)
}
meshPositions = roundPositions
descr.positions = MeshBuffers.Positions(meshPositions)
descr.normals = MeshBuffers.Normals(normals)
descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
descr.primitives = .triangles(indices)
if !materials.isEmpty {
descr.materials = MeshDescriptor.Materials.perFace(materials)
}
return try .generate(from: [descr])
}