Amit Singh
Amit Singh

Reputation: 83

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

I am looking to create AR dome shape geometry in Scenekit, like it is created in qlone 3D scanner app. Please refer following links for visuals.

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

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

enter image description here

Upvotes: 1

Views: 420

Answers (1)

Amit Singh
Amit Singh

Reputation: 83

Model IO api MDLMesh.newEllipsoid creates dome, now we needs place tiles over each section of dome.

  1. Create dome and get vertices and using vertices create tiles and place them as individual 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")
    }
}

Helper 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[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

Upvotes: 1

Related Questions