// // TankDepthFadeMaterial.swift // GasTankAR // // Created by Dennis Nemec on 26.08.26. // import RealityKit import Metal import UIKit /// 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 Originalmaterialien rekursiv durch CustomMaterial-Versionen, /// wobei Texturen und Farben des USDZ erhalten bleiben. Schatten werden für /// transparente Model-Entities separat deaktiviert. 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 modelEntity.components.set(GroundingShadowComponent(castsShadow: false)) modelEntity.components.set(DynamicLightShadowComponent(castsShadow: false)) } } /// Aktualisiert die Boden-Höhe (Welt-Y) in bereits angewandten Fade-Materialien. static func updateGroundY( _ groundY: Float, belowAlpha: Float = 0.3, 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 } var customValue = custom.custom.value customValue.x = groundY customValue.y = band customValue.z = belowAlpha custom.custom.value = customValue 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) } } }