I am experimenting with an AR app. I am trying to do the following:
The iOS device displays the real scene by an ARView
, and the ARView
creates a mesh.
Whenever the mesh is updated, I want to find the vertex closest to the camera, and attach a virtual object to it (after deleting a possibly previously attached object).
我不确定我当前的代码是否正确,但它似乎做了我上面描述的事情.如果我移动设备,显示的网格将更新.当找到新的最近顶点时,它会切换MainView
中的状态变量refreshToggle
,我希望更新后的网格与虚拟对象一起显示.但虚拟物体没有显示,我不明白为什么.
这是我的代码.我很抱歉它这么长,但我不确定要省略什么. 欢迎任何帮助!
struct MainView : View {
@State private var refreshToggle = false // Toggled, when a new closest anchor is found
var body: some View {
ARViewContainer(refreshToggle: $refreshToggle).edgesIgnoringSafeArea(.all)
}
}
struct ARViewContainer: UIViewRepresentable {
@Binding var refreshToggle: Bool
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
arView.environment.sceneUnderstanding.options = []
arView.environment.sceneUnderstanding.options.insert(.occlusion) // Turn on occlusion from the scene reconstruction's mesh.
arView.environment.sceneUnderstanding.options.insert(.physics) // Turn on physics for the scene reconstruction's mesh.
arView.debugOptions.insert(.showSceneUnderstanding) // Display a debug visualization of the mesh.
arView.renderOptions = [.disablePersonOcclusion, .disableDepthOfField, .disableMotionBlur] // Disable not required render options
arView.session.delegate = context.coordinator
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator($refreshToggle)
}
class Coordinator: NSObject, ARSessionDelegate {
@Binding var refreshToggle: Bool
var model: ModelEntity
init(_ refreshToggle: Binding<Bool>) {
self._refreshToggle = refreshToggle
// Create a cube model
let mesh = MeshResource.generateBox(size: 0.1, cornerRadius: 0.005)
let material = SimpleMaterial(color: .gray, roughness: 0.15, isMetallic: true)
model = ModelEntity(mesh: mesh, materials: [material])
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
var closestAnchor: ARAnchor? = nil
for anchor in anchors {
if let meshAnchor = anchor as? ARMeshAnchor {
let meshGeometry = meshAnchor.geometry
let vertices = meshGeometry.vertices
// Search for the vertex closest to the camera and place there a virtual marker object
let nrVertices = vertices.count
var closestVertex = SIMD3<Float>(x: 0, y: .infinity, z: 0)
for i in 0 ..< nrVertices {
let nextVertex = meshGeometry.vertex(at: UInt32(i))
if nextVertex.y < closestVertex.y {
closestVertex = nextVertex
if closestAnchor?.identifier != meshAnchor.identifier {
// A new closest anchor has been found. Remove a virtual marker object
if let closestAnchor = closestAnchor {
let anchor = AnchorEntity(anchor: closestAnchor)
anchor.children.remove(model)
}
}
closestAnchor = meshAnchor
}
}
// If a closest vertex was found, attach a virtual object to it
if let closestAnchor = closestAnchor {
let anchor = AnchorEntity(anchor: closestAnchor)
anchor.children.append(model)
refreshToggle = !refreshToggle // Let ARViewContainer redisplay the real scene with the mesh and a virtual object attached to the closest anchor
}
} // if an ARMeshAnchor was found
} // for all anchors
} // session didUpdate anchors
} // coordinator
}
extension ARMeshGeometry { // See https://developer.apple.com/documentation/arkit/armeshgeometry/3516924-vertices
func vertex(at index: UInt32) -> SIMD3<Float> {
assert(vertices.format == MTLVertexFormat.float3, "Expected three floats (twelve bytes) per vertex.")
let vertexPointer = vertices.buffer.contents().advanced(by: vertices.offset + (vertices.stride * Int(index)))
let vertex = vertexPointer.assumingMemoryBound(to: SIMD3<Float>.self).pointee
return vertex
}
}