Search code examples
iosswiftscenekitscnmaterialscngeometry

Draw dashline cylinder in scenekit like measure app?


I've followed this question to try to make the dash cylinder

final class LineNode: SCNNode {

    convenience init(positionA: SCNVector3, positionB: SCNVector3) {
        self.init()

        let vector = SCNVector3(positionA.x - positionB.x, positionA.y - positionB.y, positionA.z - positionB.z)
        let distance = vector.length
        let midPosition = (positionA + positionB) / 2

        let lineGeometry = SCNCylinder()
        lineGeometry.radius = PileDrawer3D.lineWidth
        lineGeometry.height = CGFloat(distance)
        lineGeometry.radialSegmentCount = 5

        lineGeometry.firstMaterial?.diffuse.contents = dashedImage
        lineGeometry.firstMaterial?.diffuse.contentsTransform = SCNMatrix4MakeScale(distance * 10, Float(lineGeometry.radius * 10), 1)
        lineGeometry.firstMaterial?.diffuse.wrapS = .repeat
        lineGeometry.firstMaterial?.diffuse.wrapT = .repeat
        lineGeometry.firstMaterial?.isDoubleSided = true
        lineGeometry.firstMaterial?.multiply.contents = UIColor.green
        lineGeometry.firstMaterial?.lightingModel = .constant

        let rotation = SCNMatrix4MakeRotation(.pi / 2, 0, 0, 1)
        lineGeometry.firstMaterial?.diffuse.contentsTransform = SCNMatrix4Mult(rotation, lineGeometry.firstMaterial!.diffuse.contentsTransform)

        geometry = lineGeometry
        position = midPosition
        eulerAngles = SCNVector3.lineEulerAngles(vector: vector)

        name = className
    }

    lazy var dashedImage: UIImage = {

        let size = CGSize(width: 10, height: 3)
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        UIColor.white.setFill()
        UIRectFill(CGRect(x: 0, y: 0, width: 7, height: size.height))
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return img!
    }()

}

However, the pipes is not dashed.

Result

I'm not sure what I'm missing here please help.

UpdateT:

It turns out that the clear color (in the image) is rendered as black, not transparent in the SCNView. Still, no idea why the green color got darken like this.


Solution

  • Another approach for Line & DashLine

    final class LineNode: SCNNode {
    
        var color: UIColor? {
            set { geometry?.firstMaterial?.diffuse.contents = newValue }
            get { geometry?.firstMaterial?.diffuse.contents as? UIColor }
        }
    
        convenience init(positionA: SCNVector3, positionB: SCNVector3, dash: CGFloat = 0, in scene: SCNScene? = nil) {
            self.init()
            let indices: [Int32] = [0, 1]
            let source = SCNGeometrySource(vertices: [positionA, positionB])
            let element = SCNGeometryElement(indices: indices, primitiveType: .line)
            geometry = SCNGeometry(sources: [source], elements: [element])
            geometry?.firstMaterial?.diffuse.contents = UIColor.green
            geometry?.firstMaterial?.lightingModel = .constant
    
            return
        }
    }
    
    final class DashLineNode: SCNNode {
        convenience init(positionA: SCNVector3, positionB: SCNVector3) {
            self.init()
            let vector = (positionB - positionA)
            let length = floor(vector.length / 1)
            let segment = vector / length
    
            let indices:[Int32] = Array(0..<Int32(length))
            var vertices = [positionA]
            for _ in indices {
                vertices.append(vertices.last! + segment)
            }
            let source = SCNGeometrySource(vertices: vertices)
            let element = SCNGeometryElement(indices: indices, primitiveType: .line)
    
            geometry = SCNGeometry(sources: [source], elements: [element])
            geometry?.firstMaterial?.lightingModel = .constant
        }
    }