如何使用 Arkit Scenekit 创建类似 qlone 3d 扫描仪应用程序的 AR 圆顶

how to create AR dome like qlone 3d scanner app using Arkit Scenekit

我想在 Scenekit 中创建 AR 圆顶形状几何体,就像在 qlone 3D 扫描仪应用程序中创建的一样。 请参考以下链接以获得视觉效果。

https://www.youtube.com/watch?v=0JQZmTT3KO0

https://3dscanexpert.com/qlone-3d-scanning-ios-app/

enter image description here

模型 IO api MDLMesh.newEllipsoid 创建圆顶,现在我们需要在圆顶的每个部分上放置瓷砖。

  1. 创建圆顶并获取顶点并使用顶点创建图块并将它们作为单独的 SCNNode 放置。
import UIKit
import SceneKit
import ARKit
import ModelIO

class ARDome : SCNNode {

    private var sceneView: ARSCNView!
    private var radius: Float = 0.0
    private var radialSegments: Int = 0
    private var verticalSegments: Int = 0
    private var maxDistanceToFocusPoint: Float = 0.05
    private var minSize = SIMD3<Float>(0.01, 0.01, 0.01)
    var tilesContinuousHitCount = [SCNNode:Int]()
    
    private var extent = SIMD3<Float>(0.01, 0.01, 0.01) {
        didSet {
            extent = max(extent, minSize)
        }
    }
    
    init(sceneView: ARSCNView, radius: Float, radialSegments: Int, verticalSegments: Int) {
        super.init()
        self.sceneView = sceneView
        self.radius = radius
        self.radialSegments = radialSegments
        self.verticalSegments = verticalSegments
        //self.categoryBitMask = 0x0
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func domeSkeleton() {
        let mesh = MDLMesh.newEllipsoid(withRadii: vector_float3(radius, radius, radius), radialSegments: radialSegments, verticalSegments: verticalSegments, geometryType: MDLGeometryType.lines, inwardNormals: true, hemisphere: true, allocator: nil)
        
        self.geometry = SCNGeometry(mdlMesh: mesh)
        self.geometry?.firstMaterial?.diffuse.contents = UIColor.systemYellow
        self.geometry?.firstMaterial?.isDoubleSided = true
    }
    
    func placeTiles() {
        guard let geometry = self.geometry else { return }
        let vertices = helper.testim(geo: geometry)
        let rings = vertices.chunked(into: radialSegments + 1)
        for base in 0..<(verticalSegments/2) {
            let bottom = base + 1
            let top = base
            for i in 0..<(radialSegments) {
                let rectNode = helper.getNode(lb: rings[bottom][i], lt: rings[top][i], rt: rings[top][i+1], rb: rings[bottom][i+1])
                self.addChildNode(rectNode)
            }
        }
    }
}


import Foundation
import ARKit

class DomeTile: SCNNode {
    public var processed = false
    init(geometry: SCNGeometry) {
        super.init()
        self.geometry = geometry
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

帮手Class

import UIKit
import SceneKit
import ARKit
import ModelIO


class helper {
    public static func getNode(lb: SCNVector3, lt: SCNVector3, rt: SCNVector3, rb: SCNVector3) -> DomeTile{
        let vertices: [SCNVector3] = [lb, lt, rt, rb,]
        let source = SCNGeometrySource(vertices: vertices)
        let indices: [UInt16] = [0, 1, 2, 0, 2, 3,]
        let element = SCNGeometryElement(indices: indices, primitiveType: .triangles)
        let geometry = SCNGeometry(sources: [source], elements: [element])
        geometry.firstMaterial?.isDoubleSided = true
        geometry.firstMaterial?.diffuse.contents  = UIColor.systemBlue.withAlphaComponent(0.7)
        let node = DomeTile(geometry: geometry)
        return node
    }
    
    public static func testim(geo: SCNGeometry) -> [SCNVector3] {
        //let rect = SCNBox(width: 40.0, height: 40.0, length: 5.0, chamferRadius: 0.0)
        let srcs = geo.sources(for: .vertex)
        guard let src = srcs.first else { exit(1)}
        
        let bperC = src.bytesPerComponent
        let stride = src.dataStride / bperC
        let offset = src.dataOffset / bperC
        let vectorCount = src.vectorCount
        
        let vertices = src.data.withUnsafeBytes { (buffer : UnsafePointer<Float>) -> [SCNVector3] in
            var result = Array<SCNVector3>()
            for i in 0...vectorCount - 1 {
                let start = i * stride + offset
                let x = buffer[start]
                let y = buffer[start + 1]
                let z = buffer[start + 2]
                result.append(SCNVector3(x, y, z))
            }
            return result
        }
        return vertices
    }
}

extension Array {
    func chunked(into size: Int) -> [[Element]] {
              return stride(from: 0, to: count, by: size).map {
            Array(self[[=11=] ..< Swift.min([=11=] + size, count)])
        }
    }
}