Search code examples
swiftswiftuirealitykitusdz

Import USDZ and manipulate materials for each surface separately at runtime


Basically, my requirement is to import a usdz, for an example, a car. Then apply different materials for each body part. Eg: material A to body, material B to side mirrors, material C to wheels etc.

I could iterate through the materials in the ModelEntity's ModelComponent and assign material as below.

func updateUIView(_ uiView: ARView, context: Context) {

    let entity = try! Entity.loadModel(named: "CarScene")
    
    var material = PhysicallyBasedMaterial()
    material.baseColor = PhysicallyBasedMaterial.BaseColor(tint:.red)
    material.roughness = PhysicallyBasedMaterial.Roughness(floatLiteral: 0.0)
    material.metallic = PhysicallyBasedMaterial.Metallic(floatLiteral: 1.0)
  
    for i in 0...(entity.model?.materials.count ?? 1) - 1 {
        entity.model?.materials[i] = material
    }
    
    let anchor = AnchorEntity(plane: .horizontal)
    anchor.addChild(entity)

    uiView.scene.addAnchor(carAnchor)
}

But, I don't know which material is which. If I have multiple usdz files, I want to be able to accurately assign materials for each body part. Is that doable?

Do I need to break the usdz model and assign identifiers before importing to my Xcode project? Any help would be really appreciated.


Solution

  • Use the following sample code which shows you how to modify materials at runtime:

    import SwiftUI
    import RealityKit
    
    struct ARViewContainer: UIViewRepresentable {
        
        let arView = ARView(frame: .zero)
        let fiat = try! Entity.load(named: "Fiat_Uno")
        
        func makeUIView(context: Context) -> ARView {
            print(fiat)
            fiat.scale /= 5
            fiat.orientation = .init(angle: .pi/1.5, axis: [0,1,0])
            let anchor = AnchorEntity()
            anchor.addChild(fiat)
            arView.scene.anchors.append(anchor)
            return arView
        }
        func updateUIView(_ view: ARView, context: Context) {
    
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
                let wheels = fiat.findEntity(named: "Rodas_Material_001_0")?
                                                  .children[0] as? ModelEntity
                wheels?.model?.materials[0] = UnlitMaterial(color: .green)
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
                    let body = fiat.findEntity(named: "Fiat_UNO_Material_001_0")?
                                                    .children[0] as? ModelEntity
                    body?.model?.materials[0] = UnlitMaterial(color: .blue)
                    
                }
            }
        }
    }
    

    struct ContentView : View {
        var body: some View {
            ARViewContainer().ignoresSafeArea()
        }
    }