7

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

Sean Thielen
  • 1,396
  • 1
  • 11
  • 15
  • 1
    Hard to tell what's going wrong without seeing more of what's going on. (Maybe it's just that basketball players tend to be pretty tall...?) One guess is that you're multiplying your new scale factor into the node's existing scale, which might not be 1.0 to start. – rickster Feb 22 '18 at 19:45
  • @rickster Hah, you're not the first to suggest that it works as intended because basketball players are tall :) I updated the code snippet to include some more detail. Let me know if you have any ideas! This one is driving me crazy, I feel like I must be missing something obvious. – Sean Thielen Feb 22 '18 at 23:05
  • 1
    Would you mind sharing that incorect model? – Juraj Antas Mar 05 '18 at 18:22
  • @JurajAntas Here's the incorrectly sized basketball player: https://3d.cdn.gometa.io/ea4ba1fb-5d79-42f6-8b75-e2c39c66bad5.zip and the correctly sized astronaut: https://3d.cdn.gometa.io/4ceb2bf3-078a-47bd-a0d5-f06e6c1cf011.zip – Sean Thielen Mar 05 '18 at 21:39
  • Thanks. I had a look. What is different is transformation that are inside the model. I would have a proposal to instead of using scale of the model node, you would be using stored cameras in the model. No scaling is needed and you can move camera to the position you want. (even possible inside xcode) and than use that camera params to display the model. – Juraj Antas Mar 06 '18 at 08:23
  • @SeanThielen Did you ever resolve this? – Greg Hilston Oct 03 '18 at 20:49

1 Answers1

4

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.

Greg Hilston
  • 2,397
  • 2
  • 24
  • 35