0

The goal is to scale a SCNNode and vertically center it within its parent.

However, scaling a SCNNode does not affect its bounding box and computing the scaled height does not work. Without an accurate height, how can you vertically center the node inside its parent?

To illustrate the problem with using the scaled height, see the attached file, Tiki.dae. The original height of the asset (as shown by the bounding box) is 324.36. If you set the Y-scale to 0.01, however, the height doesn't become ~3.24. It becomes smaller than 3, which you can prove by fitting it comfortably within a sphere of height 3 (radius of 1.5).

The code below attempts to center a scaled node inside its parent, but it doesn't work.

Note: reference node is the fox/panda reference node from the WWDC 2015 fox demo.

            // Create reference node
            let referenceNode = SCNReferenceNode(URL: referenceURL)
            referenceNode?.load()

            // Scale reference node
            let scale = Float(3)
            referenceNode?.scale = SCNVector3(x: scale, y: scale, z: scale)

            // Create geometry for sphere
            let sphereGeometry = SCNSphere(radius: (gridSphere.geometry as! SCNSphere).radius)
            //sphereGeometry.materials = gridSphere.geometry!.materials
            sphereGeometry.firstMaterial!.diffuse.contents = gPurpleColor

            // Create sphere to hold reference node, position at same place as <gridSphere>
            let liveSphere = SCNNode(geometry: sphereGeometry)
            liveSphere.position = gridSphere.position

            // Center reference node inside <liveSphere>
            var min = SCNVector3Zero
            var max = SCNVector3Zero
            referenceNode?.getBoundingBoxMin(&min, max: &max)
            let referenceNodeHeight = max.y - min.y
            referenceNode?.position = SCNVector3(x: 0, y: 0 - referenceNodeHeight, z: 0)

            // Add reference node to <liveSphere>
            liveSphere.addChildNode(referenceNode!)

            // This value never changes no matter the scale value???
            print(referenceNodeHeight)
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • That example doesn't appear to work. `referenceNode` has no children and is never added to the scene. Since it has no children and no geometry, a height of 0 seems reasonable. `liveSphere` is never added to the scene. `gridSphere` is apparently defined elsewhere? Note that per https://developer.apple.com/library/ios/documentation/SceneKit/Reference/SCNReferenceNode_Class/ an `SCNReferenceNode` is intended as a placeholder for other content loaded from a scene file. – Hal Mueller Aug 24 '16 at 22:47
  • @HalMueller oops removed too much code in an effort to simplify it. re-adding correct code. – Crashalot Aug 24 '16 at 23:00
  • @HalMueller the reference node is the fox/panda from the WWDC 2015 fox demo, omitted here for simplicity. should those files get uploaded as well? – Crashalot Aug 24 '16 at 23:02

1 Answers1

0

Here's a Playground that adds a cube (in place of your reference node) as a child of the sphere. The cube responds to scaling (see the lines following "// Scale reference node").

//: Playground - noun: a place where people can play

import Cocoa
import SceneKit
import XCPlayground

public class GizmoNode: SCNNode {

    required public override init() {
        super.init()
        let axisLength = CGFloat(3.0)
        let offset = CGFloat(axisLength/2.0)
        let axisSide = CGFloat(0.2)
        let chamferRadius = CGFloat(axisSide)

        let xBox = SCNBox(width: axisLength, height: axisSide, length: axisSide, chamferRadius: chamferRadius)
        xBox.firstMaterial?.diffuse.contents = NSColor.redColor()
        let yBox = SCNBox(width: axisSide, height: axisLength, length: axisSide, chamferRadius: chamferRadius)
        yBox.firstMaterial?.diffuse.contents = NSColor.greenColor()
        let zBox = SCNBox(width: axisSide, height: axisSide, length: axisLength, chamferRadius: chamferRadius)
        zBox.firstMaterial?.diffuse.contents = NSColor.blueColor()
        let xNode = SCNNode(geometry: xBox)
        let yNode = SCNNode(geometry: yBox)
        let zNode = SCNNode(geometry: zBox)
        self.addChildNode(xNode)
        self.addChildNode(yNode)
        self.addChildNode(zNode)
        print (xNode.position)
        print (yNode.position)
        print (zNode.position)
        xNode.position.x = offset
        yNode.position.y = offset
        zNode.position.z = offset
        print (xNode.pivot)
        print (yNode.pivot)
        print (zNode.pivot)
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let scene = SCNScene()

sceneView.scene = scene
sceneView.backgroundColor = NSColor.blackColor()
sceneView.allowsCameraControl = true

let gizmo = GizmoNode()
scene.rootNode.addChildNode(gizmo)

XCPlaygroundPage.currentPage.liveView = sceneView

// Create reference node
let cubeSize = CGFloat(0.5)
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
let referenceNodeStandIn = SCNNode(geometry: cubeGeometry)
//referenceNodeStandIn?.load()
let cubeColor = NSColor.whiteColor().colorWithAlphaComponent(0.5)
cubeGeometry.firstMaterial!.diffuse.contents = cubeColor

// Scale reference node
let scale = CGFloat(8)
referenceNodeStandIn.scale = SCNVector3(x: scale, y: scale, z: scale)

// Create geometry for sphere
let sphereRadius = CGFloat(2.0)
let sphereGeometry = SCNSphere(radius: sphereRadius)
//sphereGeometry.materials = gridSphere.geometry!.materials
let gPurpleColor = NSColor.purpleColor().colorWithAlphaComponent(1.0)
sphereGeometry.firstMaterial!.diffuse.contents = gPurpleColor

// Create sphere to hold reference node, position at same place as <gridSphere>
let liveSphere = SCNNode(geometry: sphereGeometry)
//liveSphere.position = gridSphere.position
scene.rootNode.addChildNode(liveSphere)

// Center reference node inside <liveSphere>
var min = SCNVector3Zero
var max = SCNVector3Zero
referenceNodeStandIn.getBoundingBoxMin(&min, max: &max)
print("min: \(min) max: \(max)")
let referenceNodeHeight = max.y - min.y
//referenceNodeStandIn.position = SCNVector3(x: 0, y: 0 - referenceNodeHeight, z: 0)

// Add reference node to <liveSphere>
liveSphere.addChildNode(referenceNodeStandIn)

// This value never changes no matter the scale value because it's in local coordinate space
print("reference node height", referenceNodeHeight)
Hal Mueller
  • 7,019
  • 2
  • 24
  • 42
  • Thanks Hal! You're too kind. But what exactly is the difference, or more importantly, the lesson for centering scaled nodes within their parent? It seems like you comment out the position so how do you center the node? – Crashalot Aug 25 '16 at 08:52