How to Draw Dashed Line in Arkit (Scenekit) Like in the Measure App

How to draw dashed line in ARKit (SceneKit) like in the Measure app?

Probably not the most professional solution, but I started with a very similar approach. And then added the dashed-style like follows.

First I created an image that is half white, half transparent, to create the dashed-style

Then used it in the material of the SCNCylinder:

material.diffuse.contents = UIImage(named: "line")!
material.diffuse.wrapS = .repeat
material.diffuse.wrapT = .repeat
material.isDoubleSided = true // Not sure if this is really needed here^

Next I scaled it accordingly, to repeat it (make it as fine) as I want it:

material.diffuse.contentsTransform = SCNMatrix4MakeScale(width * repeatCountPerMeter, height * repeatCountPerMeter, 1)

As I used a white image, I can "tint" it in any color I want:

material.multiply.contents = UIColor.green

To make it look more "2D like", ignore the lighting, using:

material.lighting = .constant

Additionally (as my Cylinder is rotated by 90°) I had to rotate the material as well:

let rotation = SCNMatrix4MakeRotation(.pi / 2, 0, 0, 1)
material.diffuse.contentsTransform = SCNMatrix4Mult(rotation, material.diffuse.contentsTransform)

And whenever the line gets resized, update its SCNMatrix4MakeScale accordingly (see width and heightabove, where forheight` I just put the diameter (2*r)).

Draw dashline cylinder in scenekit like measure app?

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
}
}

How to draw line node keep same size in camera as Measure App in iPhone?

I use this to update scale if even if you stay far away it still readable

func updateScaleFromCameraForNodes(_ nodes: [SCNNode], fromPointOfView pointOfView: SCNNode , useScaling: Bool){
nodes.forEach { (node) in

//1. Get The Current Position Of The Node
let positionOfNode = SCNVector3ToGLKVector3(node.worldPosition)

//2. Get The Current Position Of The Camera
let positionOfCamera = SCNVector3ToGLKVector3(pointOfView.worldPosition)

//3. Calculate The Distance From The Node To The Camera
let distanceBetweenNodeAndCamera = GLKVector3Distance(positionOfNode, positionOfCamera)

let a = distanceBetweenNodeAndCamera*1.75
if(useScaling) {
node.simdScale = simd_float3(a,a,a)

}

}
SCNTransaction.flush()
}

then called it in the renderer updateAtTime

self.updateScaleFromCameraForNodes(self.nodesAdded, fromPointOfView:
self.cameraNode, useScaling: true)

Stroke Width with a SceneKit line primitive type

SceneKit doesn't provide controls for this. However, SceneKit draws using OpenGL ES, which does.

When you're drawing with GL in the GL_LINES mode, the glLineWidth call changes the line width. (Watch out: the argument is in actual pixels, not UI-layout points, so you'll need a larger width than you might think if you don't want super-thin hairlines on a Retina display.)

So, where do you call that in your SceneKit app? You have a few options there. In a simple scene like yours, where you're only rendering one thing, you can just set it before the scene renders. Set a delegate for your view, then implement renderer:willRenderSceneAtTime: and call glLineWidth there.

However, OpenGL line rendering is pretty limited — if you want to customize rendering more, you'll need a different approach. Just which approach works best depends on exactly what you're going for, so here are a few ideas for you to research:

  • Make your "lines" from narrow triangle strips
  • Make them from primitive SCN geometries like boxes and cylinders
  • Keep a simple cube geometry, but use a fragment shader (or shader modifier snippet) to draw only near the edges of each polygon

Drawing edges of SCNBox without diagonal line?

I’m not sure on how to get rid of the diagonal lines.

But setting the material to double sided will draw the lines on the opposite side of the box.

 box.geometry?.firstMaterial?.isDoubleSided = true

Edit:

Here's another approach to achieve what you want (get rid of those diagonal lines!)

Sample Image

let sm = "float u = _surface.diffuseTexcoord.x; \n" +
"float v = _surface.diffuseTexcoord.y; \n" +
"int u100 = int(u * 100); \n" +
"int v100 = int(v * 100); \n" +
"if (u100 % 99 == 0 || v100 % 99 == 0) { \n" +
" // do nothing \n" +
"} else { \n" +
" discard_fragment(); \n" +
"} \n"

let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)

box.firstMaterial?.emission.contents = UIColor.green

box.firstMaterial?.shaderModifiers = [SCNShaderModifierEntryPoint.surface: sm]

box.firstMaterial?.isDoubleSided = true


Related Topics



Leave a reply



Submit