Created
August 4, 2024 14:48
-
-
Save Matt54/b593dd62ff161e5ecb17cfe5bc1c49af to your computer and use it in GitHub Desktop.
RealityView comparing LowLevelMesh spheres with and without surface normal data
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import RealityKit | |
struct LowLevelMeshNormalsComparisonView: View { | |
var body: some View { | |
RealityView { content in | |
let sphereWithoutNormals = try! sphereEntity(includeNormals: false) | |
sphereWithoutNormals.position.x = -0.11 | |
let sphereWithNormals = try! sphereEntity(includeNormals: true) | |
sphereWithNormals.position.x = 0.11 | |
content.add(sphereWithNormals) | |
content.add(sphereWithoutNormals) | |
} | |
} | |
func sphereEntity(includeNormals: Bool = true) throws -> Entity { | |
var mesh: LowLevelMesh | |
if includeNormals { | |
mesh = try getMeshWithNormals(radius: 0.1) | |
} else { | |
mesh = try getMeshWithoutNormals(radius: 0.1) | |
} | |
let resource = try MeshResource(from: mesh) | |
var material = PhysicallyBasedMaterial() | |
material.baseColor.tint = .init(red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0) | |
material.faceCulling = .none | |
material.metallic = 1.0 | |
material.roughness = 0.0 | |
let modelComponent = ModelComponent(mesh: resource, materials: [material]) | |
let entity = Entity() | |
entity.components.set(modelComponent) | |
return entity | |
} | |
func getMeshWithNormals(radius: Float, | |
latitudeBands: Int = 40, | |
longitudeBands: Int = 20) throws -> LowLevelMesh { | |
struct MyVertexWithNormal { | |
var position: SIMD3<Float> = .zero | |
var normal: SIMD3<Float> = .zero | |
static var vertexAttributes: [LowLevelMesh.Attribute] = [ | |
.init(semantic: .position, format: .float3, offset: MemoryLayout<Self>.offset(of: \.position)!), | |
.init(semantic: .normal, format: .float3, offset: MemoryLayout<Self>.offset(of: \.normal)!), | |
] | |
static var vertexLayouts: [LowLevelMesh.Layout] = [ | |
.init(bufferIndex: 0, bufferStride: MemoryLayout<Self>.stride) | |
] | |
static var descriptor: LowLevelMesh.Descriptor { | |
var desc = LowLevelMesh.Descriptor() | |
desc.vertexAttributes = MyVertexWithNormal.vertexAttributes | |
desc.vertexLayouts = MyVertexWithNormal.vertexLayouts | |
desc.indexType = .uint32 | |
return desc | |
} | |
} | |
let vertexCount = (latitudeBands + 1) * (longitudeBands + 1) | |
let indexCount = latitudeBands * longitudeBands * 6 | |
var desc = MyVertexWithNormal.descriptor | |
desc.vertexCapacity = vertexCount | |
desc.indexCapacity = indexCount | |
let mesh = try LowLevelMesh(descriptor: desc) | |
mesh.withUnsafeMutableBytes(bufferIndex: 0) { rawBytes in | |
let vertices = rawBytes.bindMemory(to: MyVertexWithNormal.self) | |
var vertexIndex = 0 | |
for latNumber in 0...latitudeBands { | |
let theta = Float(latNumber) * Float.pi / Float(latitudeBands) | |
let sinTheta = sin(theta) | |
let cosTheta = cos(theta) | |
for longNumber in 0...longitudeBands { | |
let phi = Float(longNumber) * 2 * Float.pi / Float(longitudeBands) | |
let sinPhi = sin(phi) | |
let cosPhi = cos(phi) | |
let x = cosPhi * sinTheta | |
let y = cosTheta | |
let z = sinPhi * sinTheta | |
let position = SIMD3<Float>(x, y, z) * radius | |
let normal = -SIMD3<Float>(x, y, z).normalized() | |
vertices[vertexIndex] = MyVertexWithNormal(position: position, normal: normal) | |
vertexIndex += 1 | |
} | |
} | |
} | |
mesh.withUnsafeMutableIndices { rawIndices in | |
let indices = rawIndices.bindMemory(to: UInt32.self) | |
var index = 0 | |
for latNumber in 0..<latitudeBands { | |
for longNumber in 0..<longitudeBands { | |
let first = (latNumber * (longitudeBands + 1)) + longNumber | |
let second = first + longitudeBands + 1 | |
indices[index] = UInt32(first) | |
indices[index + 1] = UInt32(second) | |
indices[index + 2] = UInt32(first + 1) | |
indices[index + 3] = UInt32(second) | |
indices[index + 4] = UInt32(second + 1) | |
indices[index + 5] = UInt32(first + 1) | |
index += 6 | |
} | |
} | |
} | |
let meshBounds = BoundingBox(min: [-radius, -radius, -radius], max: [radius, radius, radius]) | |
mesh.parts.replaceAll([ | |
LowLevelMesh.Part( | |
indexCount: indexCount, | |
topology: .triangle, | |
bounds: meshBounds | |
) | |
]) | |
return mesh | |
} | |
func getMeshWithoutNormals(radius: Float, | |
latitudeBands: Int = 40, | |
longitudeBands: Int = 20) throws -> LowLevelMesh { | |
struct MyVertexWithoutNormal { | |
var position: SIMD3<Float> = .zero | |
static var vertexAttributes: [LowLevelMesh.Attribute] = [ | |
.init(semantic: .position, format: .float3, offset: MemoryLayout<Self>.offset(of: \.position)!), | |
] | |
static var vertexLayouts: [LowLevelMesh.Layout] = [ | |
.init(bufferIndex: 0, bufferStride: MemoryLayout<Self>.stride) | |
] | |
static var descriptor: LowLevelMesh.Descriptor { | |
var desc = LowLevelMesh.Descriptor() | |
desc.vertexAttributes = MyVertexWithoutNormal.vertexAttributes | |
desc.vertexLayouts = MyVertexWithoutNormal.vertexLayouts | |
desc.indexType = .uint32 | |
return desc | |
} | |
} | |
let vertexCount = (latitudeBands + 1) * (longitudeBands + 1) | |
let indexCount = latitudeBands * longitudeBands * 6 | |
var desc = MyVertexWithoutNormal.descriptor | |
desc.vertexCapacity = vertexCount | |
desc.indexCapacity = indexCount | |
let mesh = try LowLevelMesh(descriptor: desc) | |
mesh.withUnsafeMutableBytes(bufferIndex: 0) { rawBytes in | |
let vertices = rawBytes.bindMemory(to: MyVertexWithoutNormal.self) | |
var vertexIndex = 0 | |
for latNumber in 0...latitudeBands { | |
let theta = Float(latNumber) * Float.pi / Float(latitudeBands) | |
let sinTheta = sin(theta) | |
let cosTheta = cos(theta) | |
for longNumber in 0...longitudeBands { | |
let phi = Float(longNumber) * 2 * Float.pi / Float(longitudeBands) | |
let sinPhi = sin(phi) | |
let cosPhi = cos(phi) | |
let x = cosPhi * sinTheta | |
let y = cosTheta | |
let z = sinPhi * sinTheta | |
let position = SIMD3<Float>(x, y, z) * radius | |
vertices[vertexIndex] = MyVertexWithoutNormal(position: position) | |
vertexIndex += 1 | |
} | |
} | |
} | |
mesh.withUnsafeMutableIndices { rawIndices in | |
let indices = rawIndices.bindMemory(to: UInt32.self) | |
var index = 0 | |
for latNumber in 0..<latitudeBands { | |
for longNumber in 0..<longitudeBands { | |
let first = (latNumber * (longitudeBands + 1)) + longNumber | |
let second = first + longitudeBands + 1 | |
indices[index] = UInt32(first) | |
indices[index + 1] = UInt32(second) | |
indices[index + 2] = UInt32(first + 1) | |
indices[index + 3] = UInt32(second) | |
indices[index + 4] = UInt32(second + 1) | |
indices[index + 5] = UInt32(first + 1) | |
index += 6 | |
} | |
} | |
} | |
let meshBounds = BoundingBox(min: [-radius, -radius, -radius], max: [radius, radius, radius]) | |
mesh.parts.replaceAll([ | |
LowLevelMesh.Part( | |
indexCount: indexCount, | |
topology: .triangle, | |
bounds: meshBounds | |
) | |
]) | |
return mesh | |
} | |
} | |
#Preview { | |
LowLevelMeshNormalsComparisonView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment