8

i am trying to highlight a selected node in SceneKit with a tap gesture. Unfortunately, I have not been able to accomplish it. The best thing I could do was to change the material when the node is tapped.

let material = key.geometry!.firstMaterial!
material.emission.contents = UIColor.blackColor()

Can someone suggest a way I can go about to just add a border or outline around the object instead of changing the color of the entire node?

3 Answers3

8

Based on @Karl Sigiscar answer and another answer here in SO I came up with this:

func createLineNode(fromPos origin: SCNVector3, toPos destination: SCNVector3, color: UIColor) -> SCNNode {
    let line = lineFrom(vector: origin, toVector: destination)
    let lineNode = SCNNode(geometry: line)
    let planeMaterial = SCNMaterial()
    planeMaterial.diffuse.contents = color
    line.materials = [planeMaterial]

    return lineNode
}

func lineFrom(vector vector1: SCNVector3, toVector vector2: SCNVector3) -> SCNGeometry {
    let indices: [Int32] = [0, 1]

    let source = SCNGeometrySource(vertices: [vector1, vector2])
    let element = SCNGeometryElement(indices: indices, primitiveType: .line)

    return SCNGeometry(sources: [source], elements: [element])
}


func highlightNode(_ node: SCNNode) {
    let (min, max) = node.boundingBox
    let zCoord = node.position.z
    let topLeft = SCNVector3Make(min.x, max.y, zCoord)
    let bottomLeft = SCNVector3Make(min.x, min.y, zCoord)
    let topRight = SCNVector3Make(max.x, max.y, zCoord)
    let bottomRight = SCNVector3Make(max.x, min.y, zCoord)


    let bottomSide = createLineNode(fromPos: bottomLeft, toPos: bottomRight, color: .yellow)
    let leftSide = createLineNode(fromPos: bottomLeft, toPos: topLeft, color: .yellow)
    let rightSide = createLineNode(fromPos: bottomRight, toPos: topRight, color: .yellow)
    let topSide = createLineNode(fromPos: topLeft, toPos: topRight, color: .yellow)

    [bottomSide, leftSide, rightSide, topSide].forEach {
        $0.name = kHighlightingNode // Whatever name you want so you can unhighlight later if needed
        node.addChildNode($0)
    }
}

func unhighlightNode(_ node: SCNNode) {
    let highlightningNodes = node.childNodes { (child, stop) -> Bool in
        child.name == kHighlightingNode
    }
    highlightningNodes.forEach {
        $0.removeFromParentNode()
    }
}
leandrodemarco
  • 1,552
  • 1
  • 15
  • 22
3

SCNNode conforms to the SCNBoundingVolume Protocol.

This protocol defines the getBoundingBoxMin:max: method.

Use this to get the minimum and maximum coordinates of the bounding box of the geometry attached to the node.

Then use the SceneKit primitive type SCNGeometryPrimitiveTypeLine to draw the lines of the bounding box. Check SCNGeometryElement.

Karl Sigiscar
  • 289
  • 1
  • 7
-3

If your node is a primitive shape you can set a UIImage as the diffuse. The image will get stretched out to cover your node. If you use an image with a border on it, this will translate to creating a border on your node.

planeGeometry.firstMaterial?.diffuse.contents = UIImage(named: "blueSquareOutline.png")

Example border image

spfursich
  • 5,045
  • 3
  • 21
  • 24