// // TankDepthFadeMaterial.swift // GasTankAR // // Created by Dennis Nemec on 26.08.26. // import RealityKit import Metal /// Kapselt das Höhen-Fade-CustomMaterial für den unterirdischen Tank: /// verblasst die Opacity anhand der Welt-Höhe (Kopf opak, vergrabener Teil transparent). @MainActor enum TankDepthFadeMaterial { /// Fade-Band (Meter) unterhalb der Bodenkante, über das die Transparenz einsetzt. static let band: Float = 0.15 /// Minimale Opacity des vergrabenen Teils. static let belowAlpha: Float = 0.3 private static let shader: CustomMaterial.SurfaceShader? = { guard let library = MTLCreateSystemDefaultDevice()?.makeDefaultLibrary() else { return nil } return CustomMaterial.SurfaceShader(named: "tankDepthFadeSurface", in: library) }() /// Ersetzt die Materialien der Entity (rekursiv) durch das Fade-CustomMaterial. static func apply(to entity: Entity) { guard let shader else { return } forEachModelEntity(entity) { modelEntity in guard var component = modelEntity.model else { return } component.materials = component.materials.map { material in guard var custom = try? CustomMaterial(from: material, surfaceShader: shader) else { return material } custom.blending = .transparent(opacity: 1.0) custom.custom.value = SIMD4(0, band, belowAlpha, 0) return custom } modelEntity.model = component } } /// Aktualisiert die Boden-Höhe (Welt-Y) in bereits angewandten Fade-Materialien. static func updateGroundY(_ groundY: Float, on entity: Entity) { forEachModelEntity(entity) { modelEntity in guard var component = modelEntity.model else { return } component.materials = component.materials.map { material in guard var custom = material as? CustomMaterial else { return material } custom.custom.value = SIMD4(groundY, band, belowAlpha, 0) return custom } modelEntity.model = component } } private static func forEachModelEntity(_ entity: Entity, _ body: (ModelEntity) -> Void) { if let modelEntity = entity as? ModelEntity { body(modelEntity) } for child in entity.children { forEachModelEntity(child, body) } } }