-
-
Save GrantMeStrength/62364f8a5d7ea26e2b97b37207459a10 to your computer and use it in GitHub Desktop.
func line(from p1: SCNVector3, to p2: SCNVector3) -> SCNNode? { | |
// Draw a line between two points and return it as a node | |
var indices = [Int32(0), Int32(1)] | |
let positions = [p1, p2] | |
let vertexSource = SCNGeometrySource(vertices: positions) | |
let indexData = Data(bytes: &indices, count:MemoryLayout<Int32>.size * indices.count) | |
let element = SCNGeometryElement(data: indexData, primitiveType: .line, primitiveCount: 1, bytesPerIndex: MemoryLayout<Int32>.size) | |
let line = SCNGeometry(sources: [vertexSource], elements: [element]) | |
let lineNode = SCNNode(geometry: line) | |
return lineNode | |
} | |
// Draws a (very thin) line between two points. | |
// Not sure how to set the color though - materials didn't seem to do much |
GrantMeStrength
commented
Dec 30, 2018
•
Thanks to an article here:
https://www.invasivecode.com/weblog/scenekit-tutorial-part-2/
which explains how to define geometries, I've got a MUCH MUCH MUCH better line drawing function working. Here it is.
func generateLine( startPoint: SCNVector3, endPoint: SCNVector3) -> SCNGeometry {
let vertices: [SCNVector3] = [startPoint, endPoint]
let data = NSData(bytes: vertices, length: MemoryLayout<SCNVector3>.size * vertices.count) as Data
let vertexSource = SCNGeometrySource(data: data,
semantic: .vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<SCNVector3>.stride)
let indices: [Int32] = [ 0, 1]
let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count) as Data
let element = SCNGeometryElement(data: indexData,
primitiveType: .line,
primitiveCount: indices.count/2,
bytesPerIndex: MemoryLayout<Int32>.size)
return SCNGeometry(sources: [vertexSource], elements: [element])
}
and you would use it thus:
let line = generateLine(startPoint: SCNVector3Make(1, 1, 1), endPoint: SCNVector3Make(8, 8, 8))
let lineNode = SCNNode(geometry: line)
lineNode.position = SCNVector3Make(15, 15, 10)
scene.rootNode.addChildNode(lineNode)
The thickness of the line requires implementing the SCNSceneRendererDelegate, in particular:
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
glLineWidth(10)
}
As openGL is being deprecated in iOS, I'm not sure if this way of changing the line thickness is future-proof, but what is?
See the article linked to above for ideas of defining shapes - not just lines - and changing color.
The article referenced colors, but it didn't work as the default lightingModel value was incorrect. Here's how I added color support to the line drawing function:
func line(startPoint: SCNVector3, endPoint: SCNVector3, color : UIColor) -> SCNNode
{
let vertices: [SCNVector3] = [startPoint, endPoint]
let data = NSData(bytes: vertices, length: MemoryLayout<SCNVector3>.size * vertices.count) as Data
let vertexSource = SCNGeometrySource(data: data,
semantic: .vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<SCNVector3>.stride)
let indices: [Int32] = [ 0, 1]
let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count) as Data
let element = SCNGeometryElement(data: indexData,
primitiveType: .line,
primitiveCount: indices.count/2,
bytesPerIndex: MemoryLayout<Int32>.size)
let line = SCNGeometry(sources: [vertexSource], elements: [element])
line.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
line.firstMaterial?.diffuse.contents = color
let lineNode = SCNNode(geometry: line)
return lineNode;
}
Thanks to your approach. It's very helpful.
But glLineWidth(10)
can't work. It's deprecated.
Do you konw what should I do if I want to increase the line's width?
Have faced this issue few years ago and still not find a solution that fit visual requirements:
-have fixed on screen width (e.g. 2px or 3px)
-line width independant to perspective camera transformation
func lineBetweenNodes(positionA: SCNVector3, positionB: SCNVector3, inScene: SCNScene) -> SCNNode {
let vector = SCNVector3(positionA.x - positionB.x, positionA.y - positionB.y, positionA.z - positionB.z)
let distance = sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z)
let midPosition = SCNVector3 (x:(positionA.x + positionB.x) / 2, y:(positionA.y + positionB.y) / 2, z:(positionA.z + positionB.z) / 2)
let lineGeometry = SCNCylinder()
lineGeometry.radius = 0.002
lineGeometry.height = CGFloat(distance)
lineGeometry.radialSegmentCount = 5
lineGeometry.firstMaterial!.diffuse.contents = UIColor.green
let lineNode = SCNNode(geometry: lineGeometry)
lineNode.position = midPosition
lineNode.look (at: positionB, up: inScene.rootNode.worldUp, localFront: lineNode.worldUp)
return lineNode
}
How to use:
let node = lineBetweenNodes(positionA: start.position, positionB: end.position, inScene: sceneView.scene)
sceneView.scene.rootNode.addChildNode(node)
I have developed a simple project that allows drawing any width and color line through multiple points. It also supports loops and mittered (sharp) joins. https://github.com/bialylis/ThickRedLine