swiftscenekitrealitykit

How to add a simple material to a existing modelEntity in RealityKit?


I import a usdz file which contains obj files as layers. The objects have neither structure, material nor color, as I have to set these individually at runtime.

With the SceneKit I was able to implement this as follows.

// get all parts
guard let scene = SCNScene(named: "art.scnassets/allParts.usdz") else { return }
// hide all parts
scene.rootNode.childNodes.forEach { child in
    child.isHidden = true
}

// then unhide and setup (position and grouping) the parts i need
let part1 = scene.rootNode.childNode(withName: "part1_abc2", recursively: true)!
part1.isHidden = false
part1.childNodes.first?.geometry?.firstMaterial?.diffuse.contents = UIColor.red
part1.localRotate(by: SCNQuaternion(0, 0, 0, 0))
part1.localTranslate(by: SCNVector3(0, 0, 0.216))
...

I would like to imitate the behavior in RealityKit

let modelEntity = try! ModelEntity.load(named: "art.scnassets/allParts.usdz")
modelEntity.children.forEach { child in
    child.isEnabled = false
}

guard let part1 = modelEntity.findEntity(named: "part1_abc2") else { return }
part1.isEnabled = true
part1.orientation = Transform(pitch: 0, yaw: 0, roll: 0).rotation
part1.transform = Transform(translation: [0, 0, 0.216])

How can I assign a color or material to my parts? I know that it is possible to initialize a ModelEntity with a material like so

let mesh = MeshResource.generateBox(size: 0.5, cornerRadius: 0.1)
let material = SimpleMaterial(color: .red, roughness: 0.15, isMetallic: true)
let model = ModelEntity(mesh: mesh, materials: [material])

but how can I add a material to an existing obj afterwards?

UPDATE: I just realized that ModelEntity.load does return an Entity not a ModelEntity. I guess I have to figure out how to get ModelEntities for each layer/part in my usdz file? can someone please put me on the right track?

UPDATE 2: I did the following but my model completely disappeared.

let modelEntity = try! Entity.loadModel(named: "art.scnassets/allParts.usdz")
modelEntity.model?.materials = [UnlitMaterial(color: .red)]

I guess, I do have to set the material for each Child/Layer. But this block still returns a Entity ...

guard let part1 = modelEntity.findEntity(named: "part1_abc2") else { return }
part1.isEnabled = true
part1.orientation = Transform(pitch: 0, yaw: 0, roll: 0).rotation
part1.transform = Transform(translation: [0, 0, 0.216])

UPDATE 3: my response to Andy Jazz answer:

If I load the usdz as ModelEntity, the hierarchy is completely empty so the cast is failing

let modelEntity = try! Entity.loadModel(named: "art.scnassets/allThePartsOf.usdz")
print(modelEntity)

print("will downcasting")
guard let uw = modelEntity.findEntity(named: "RealModel") as? ModelEntity else {
    print("downcasting failed")
    return
}
print("did downcasting")

---- console output ----

▿ '' : ModelEntity
  ⟐ Transform
  ⟐ SynchronizationComponent
  ⟐ ModelComponent

will downcasting
downcasting failed

do i instead load the usdz as a Entity, i do get the hierarchy tree but the cast still fails

let modelEntity = try! ModelEntity.load(named: "art.scnassets/allThePartsOf.usdz")
print(modelEntity)

---- console output ----

▿ '/' : Entity, children: 110
  ⟐ Transform
  ⟐ SynchronizationComponent
  ▿ 'RealModel' : Entity, children: 1
    ⟐ Transform
    ⟐ SynchronizationComponent
    ▿ 'RealModel' : ModelEntity
      ⟐ Transform
      ⟐ ModelComponent
      ⟐ SynchronizationComponent
  ▿ 'RealModel_1' : Entity, children: 1
    ⟐ Transform
    ⟐ SynchronizationComponent
    ▿ 'RealModel_1' : ModelEntity
      ⟐ Transform
      ⟐ ModelComponent
      ⟐ SynchronizationComponent
.
.
.

will downcasting
downcasting failed

Solution

  • Use downcasting to ModelEntity type to get to model instance pty of the desired component:

    let car = try! Entity.loadModel(named: "allThePartsOf.usdz")
    
    print(car)         // look for a ModelEntity's name in the hierarchy
    
    guard let partA = car.findEntity(named: "RealModel") as? ModelEntity
    else { return }
    
    partA.model?.materials = [UnlitMaterial(color: .red)]