Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scale SCNNodes to fit within a bounding box

I'm downloading Collada DAE scenes and rendering them in SceneKit, but having trouble getting the downloaded node to "fit" within its parent node. I mainly care about scaling it's y-height to fit in the parent node.

Here's the code I'm using:

// Reset everything
node.position = SCNVector3(0, 0, 0)
node.pivot = SCNMatrix4Identity
node.scale = SCNVector3(1, 1, 1)

// Calculate absolute bounding box
let (min, max) = node.boundingBox
let size = max - min

// Get the biggest dimension of the node
guard let scaleRef = [size.x, size.y, size.z].max() else {
    return
}

// Desired bounding box is of size SCNVector3(0.4, 0.4, 0.4)
let parentSize: Float = 0.4

let ratio = (parentSize/scaleRef)
node.scale = SCNVector3(node.scale.x * ratio, node.scale.y * ratio, node.scale.z * ratio)

//Correctly position the node at the bottom of its parent node by setting pivot
let (minNode, maxNode) = node.boundingBox

let dx = (maxNode.x - minNode.x) / 2
let dy = minNode.y
let dz = (maxNode.z - minNode.z) / 2
node.pivot = SCNMatrix4Translate(node.pivot, dx, dy, dz)

node.position.x = dx * ratio
node.position.y = dy * ratio
node.position.z = dz * ratio

This seems to work for most cases, but I'm ending up with a few that scale incorrectly.

For example, this object scales correctly (Poly Astornaut):

MIN: SCNVector3(x: -1.11969805, y: -0.735845089, z: -4.02169418)
MAX: SCNVector3(x: 1.11969805, y: 0.711179018, z: 0.0)
NEW SCALE: SCNVector3(x: 0.099460572, y: 0.099460572, z: 0.099460572)

This one does not (Basketball player):

MIN: SCNVector3(x: -74.8805618, y: 0.0459594727, z: -21.4300499)
MAX: SCNVector3(x: 74.8805618, y: 203.553589, z: 15.6760511)
NEW SCALE: SCNVector3(x: 0.00196552835, y: 0.00196552835, z: 0.00196552835)

Screenshots:

Correct scaling

Correct image

Incorrect scaling

Incorrect image

like image 271
Sean Thielen Avatar asked Feb 21 '18 18:02

Sean Thielen


1 Answers

So what I ended up doing was a little different, as I wanted to scale my asset to exactly the reference size.

  1. In my .scn file, going to the Node Inspector tab and looking at the bounding box of the asset I'm working with:

Bounding Box as listed in Scene graph, node inspector tab

  1. Know the size of your reference image. This is something you should be measuring in the real world, and entering it in your Assets.xcassets resource

reference image size

  1. This reference image size is passed to you in the func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) function. I access mine through the following code:

guard let imageAnchor = anchor as? ARImageAnchor else { return } let referenceImage = imageAnchor.referenceImage print(referenceImage.physicalSize.width) print(referenceImage.physicalSize.height)

  1. Wherever you want to scale, you'll simply use algebra to scale the bounding box size, to the desired size. So our scale is simply

let threeDimensionalAssetToRealReferenceImageScale = referenceImageWidth / threeDimensionalAssetBoundingBoxWidth

  1. Call scale on your SCNNode

I wrote this up, as I was very confused with this situation, I hope it helps someone.

like image 146
Greg Hilston Avatar answered Oct 17 '22 21:10

Greg Hilston