How to restrict position of a node to the surface of a custom plane in Scenekit?

Viewed 288

I’m new to Swift and SceneKit and I am trying to implement a solution where users can add a 3D object to a scene and it will be placed on the edge of a plane’s surface.

I tried using the plane node’s bounding box values to assume its edge and the object’s potential position, but since the plane is a custom geometry, the object often ends up outside of the plane's surface.

On top of that, if the plane's specific edge is diagonal line, I also need the object to rotate so it always runs parallel to the plane’s edge.

Here’s a snippet of my code and an image illustrating the problem and the desired outcome:

//creating plane out of custom geometry values
let surfaceNode = SCNNode(geometry: surfaceGeometry)

//defining plane’s bounding box & center point
let minimum = SIMD3<Float>(surfaceNode.boundingBox.min)
let maximum = SIMD3<Float>(surfaceNode.boundingBox.max)
let translation = (maximum - minimum) * 0.5

//fixing pivot point
surfaceNode.pivot = SCNMatrix4MakeTranslation(surfaceNode.boundingBox.min.x + translation.x, 0, surfaceNode.boundingBox.min.z + translation.z)

//adding custom plane to the scene
surfaceNode.position = SCNVector3(0, 0 ,0)
sceneView.scene.rootNode.addChildNode(surfaceNode)

//creating object
let object = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
let objectNode = SCNNode(geometry: object)
objectNode.pivot = SCNMatrix4MakeTranslation(0, -0.1, 0)

//positioning object on top of the plane’s surface
objectNode.position = SCNVector3(surfaceNode.position.x, surfaceNode.position.y, surfaceNode.position.z - translation.z)

//adding object to scene
sceneView.scene.rootNode.addChildNode(objectNode)
2 Answers

I was hoping to see a cool math based solution for this because that would be a neat trick, but since I haven't...

  1. You didn't say how dynamic this has to be and 2) I'm guessing you have some kind of drag and drop in mind, so to avoid some of the math, this would be my approach.

What I would do is to take your Plane's Surface and calculate some snap-to positions that include the rotation you need to place your Object. I recommend this because some corners will just not fit and since you can't allow over lap. So, get your drop position and find the closest "acceptable" slot so to speak and "snap to" the nearest position.

If you want to allow a lot of flexibility, like move a basically 1 pixel at a time, then I'd create an array of acceptable position ranges and the rotation required for each. If someone drags outside of that range, then either don't allow that or move it to the closest acceptable "spot".

A bit painful to setup for multiple geometries, but depending on what you are trying to accomplish, it would be accurate albeit a bit "hard coded".

Another much easier possibility would be if you give your moving box a physical body and you place unvisible "walls" with physical bodies on all edges of your plane and with collisionmasks let scenekit do the work for you.

Related