I am trying to change the color of selected vertices of an imported .usdz model. My (naive?) approach is to make a copy of the geometry of the original model by extracting the SCNGeometrySources for the .vertex, .normal and .color SCNGeometrySource.Sematic. Then change the color source and re-assemble the geometry. It sometimes works, but not in all cases. For example the robot_walk_idle.usdz model from Apple's gallery, cannot be re-constructed by this approach. What am I missing?
Below is a SwiftUI view that displays the original robot, a straightforward copy and a re-constructed copy. I only show the first node that has a geometry attached. I haven't modified the color source, so the re-constructed copy should show equal to the middle scene. I cannot get rid of the strange appearance of the re-constructed copy, even after assigning various materials (which I do not show here)
I did notice that the geometry.elements.first.debugDescription
shows Optional(<SCNGeometryElement: 0x600001ad6760 | 10576 x triangle, ***4 channels***, int indices>)
. Does that have any impact?
Here's the output:
Here's the code:
import SwiftUI
import SceneKit
struct CompareGeometries: View {
let original = SCNScene(named: "SceneKit Asset Catalog.scnassets/robot_walk_idle.usdz")!
var copy: SCNScene {
var geometryCopy = SCNGeometry()
original.rootNode.enumerateHierarchy { child, stop in
if let geometry = child.geometry {
stop.pointee = true
geometryCopy = geometry.copy() as! SCNGeometry
}
}
let node = SCNNode(geometry: geometryCopy)
let scene = SCNScene()
scene.rootNode.addChildNode(node)
return scene
}
var reconstructed: SCNScene {
var geometryReconstruction = SCNGeometry()
original.rootNode.enumerateHierarchy { child, stop in
if let geometry = child.geometry {
stop.pointee = true
let _ = print(geometry.sources)
let vertexSources = geometry.sources(for: .vertex)
let normalSources = geometry.sources(for: .normal)
let colorSources = geometry.sources(for: .color)
geometryReconstruction = SCNGeometry(
sources: vertexSources + normalSources + colorSources,
elements: geometry.elements)
}
}
let node = SCNNode(geometry: geometryReconstruction)
let scene = SCNScene()
scene.rootNode.addChildNode(node)
return scene
}
var body: some View {
HStack {
SceneView(scene: original, options: [.autoenablesDefaultLighting])
.frame(width: 500, height: 500)
.border(.black)
SceneView(scene: copy, options: [.autoenablesDefaultLighting])
.frame(width: 500, height: 500)
.border(.black)
SceneView(scene: reconstructed, options: [.autoenablesDefaultLighting])
.frame(width: 500, height: 500)
.border(.black)
}
}
}
Can this be done within SceneKit or do I need ModelIO ? Any help appreciated! Thanks.