I have a 3D model that contains 3 geometries, as highlighted below
This is my code that create such SCNNode from a USDZ file:
private func readGeo(fn: String) -> SCNNode {
let bundle = Bundle.main
let url = bundle.url(forResource: fn, withExtension: nil)!
let asset = MDLAsset(url: url)
asset.loadTextures()
let object = asset.object(at: 0)
let node = SCNNode(mdlObject: object)
return node
}
Then I need to flatten the node, since I want to make it just a single geometry, so that it's easier to attach a physics body. So I changed the return to:
return node.flattenedClone()
The position of geometries seems to be incorrect, as you can see below: (see the cover is sticking out)
You can download the sample project here: https://drive.google.com/file/d/1ewudz3A2NKeW-72bXRPJJtphyRMae-1e/view?usp=sharing
Edit:
I realize that the position of the sub nodes are changed after flatten. They are originally at non-zero coordinate, but after flatten, they are positioned as if they are at zero coordinate. I don't know why
I believe this is a bug in SceneKit that the transform is ignored if the node has no geometry. I was able to fix its by passing down the transforms to child nodes with a DFS:
fileprivate extension SCNNode {
// pre-process it before taking the flattenedClone()
var preProcessed: SCNNode {
let clone = self.clone()
Self.dfs_passDownTransform(node: clone)
return clone
}
static func dfs_passDownTransform(node: SCNNode) {
// If it's an empty node with just transform, we want to pass the transform to its immediate children
if node.geometry == nil && !SCNMatrix4IsIdentity(node.transform) {
for child in node.childNodes {
child.transform = SCNMatrix4Mult(child.transform, node.transform)
}
node.transform = SCNMatrix4Identity
}
for child in node.childNodes {
dfs_passDownTransform(node: child)
}
}
}