feat(avlivebody): lights, material and visibility

This commit is contained in:
L'électron rare
2026-06-26 12:19:14 +02:00
parent e7b107a96e
commit 5dfbfc7ae9
4 changed files with 72 additions and 3 deletions
@@ -13,7 +13,7 @@ final class MeshEntity {
private static let vertexCount = 10475
private let faces: [UInt32]
private var pools: [Int: ModelEntity] = [:]
private let material = SimpleMaterial(
private var material = SimpleMaterial(
color: NSColor(white: 0.8, alpha: 1.0),
roughness: 0.5, isMetallic: false)
@@ -41,6 +41,17 @@ final class MeshEntity {
}
}
func setVisible(_ visible: Bool) { root.isEnabled = visible }
func setMaterial(metallic: Bool, roughness: Double) {
material = SimpleMaterial(
color: NSColor(white: 0.8, alpha: 1.0),
roughness: .float(Float(roughness)), isMetallic: metallic)
for entity in pools.values {
entity.model?.materials = [material]
}
}
private func buildMesh(_ verts: [SIMD3<Float>])
-> MeshResource? {
guard verts.count == Self.vertexCount, !faces.isEmpty else {
@@ -16,6 +16,10 @@ final class SceneController {
private let camera = PerspectiveCamera()
private let worldAnchor = AnchorEntity(world: .zero)
private let keyLight = DirectionalLight()
private let fillLight = DirectionalLight()
private let rimLight = DirectionalLight()
private(set) var videoQuad: VideoQuad?
private(set) var skeleton: SkeletonEntity?
private(set) var mesh: MeshEntity?
@@ -33,6 +37,25 @@ final class SceneController {
arView.environment.background = .color(.black)
arView.scene.addAnchor(worldAnchor)
keyLight.light.intensity = 4000
keyLight.orientation = simd_quatf(angle: .pi / 6,
axis: SIMD3(1, 0, 0))
let keyA = AnchorEntity(world: SIMD3<Float>(1, 2, -1))
keyA.addChild(keyLight)
arView.scene.addAnchor(keyA)
fillLight.light.intensity = 1500
fillLight.light.color = NSColor(red: 0.7, green: 0.8,
blue: 1.0, alpha: 1.0)
let fillA = AnchorEntity(world: SIMD3<Float>(-2, 1, -2))
fillA.addChild(fillLight)
arView.scene.addAnchor(fillA)
rimLight.light.intensity = 2000
let rimA = AnchorEntity(world: SIMD3<Float>(0, 1, -5))
rimA.addChild(rimLight)
arView.scene.addAnchor(rimA)
camera.camera.fieldOfViewInDegrees = 55
cameraAnchor.addChild(camera)
arView.scene.addAnchor(cameraAnchor)
@@ -65,6 +88,25 @@ final class SceneController {
mesh?.update(persons)
}
func setSkeletonVisible(_ v: Bool) { skeleton?.setVisible(v) }
func setMeshVisible(_ v: Bool) { mesh?.setVisible(v) }
func setVideoVisible(_ v: Bool) { videoQuad?.setVisible(v) }
func setVideoOpacity(_ v: Double) { videoQuad?.setOpacity(v) }
func updateMeshMaterial(metallic: Bool, roughness: Double) {
mesh?.setMaterial(metallic: metallic, roughness: roughness)
}
func setLightIntensities(key: Double, fill: Double, rim: Double) {
keyLight.light.intensity = Float(key)
fillLight.light.intensity = Float(fill)
rimLight.light.intensity = Float(rim)
}
func setFieldOfView(_ deg: Double) {
camera.camera.fieldOfViewInDegrees = Float(deg)
}
// MARK: - Orbital camera
private func applyCamera() {
@@ -18,6 +18,8 @@ final class SkeletonEntity {
private let material = SimpleMaterial(
color: NSColor.systemYellow, roughness: 0.6, isMetallic: false)
func setVisible(_ visible: Bool) { root.isEnabled = visible }
func update(_ skeletons: [Int: SkeletonPayload]) {
// Drop pools for pids no longer present.
for pid in pools.keys where skeletons[pid] == nil {
@@ -1,3 +1,4 @@
import AppKit
import CoreImage
import CoreVideo
import Foundation
@@ -10,6 +11,7 @@ final class VideoQuad {
let entity = ModelEntity()
private let ciContext = CIContext()
private var opacity: Double = 1.0
/// Plane is 1.6 m wide, 16:9; positioned 2 m behind the body.
private static let width: Float = 1.6
private static let height: Float = 0.9
@@ -26,6 +28,17 @@ final class VideoQuad {
SIMD3<Float>(0, 0, Self.zBack)
}
func setVisible(_ visible: Bool) { entity.isEnabled = visible }
func setOpacity(_ value: Double) {
opacity = value
// Re-tint current material; `update(_:)` will also use `opacity`.
if var mat = entity.model?.materials.first as? UnlitMaterial {
mat.color = .init(tint: NSColor(white: 1, alpha: CGFloat(value)))
entity.model?.materials = [mat]
}
}
/// Replace the plane's texture from a decoded camera frame.
func update(_ pixelBuffer: CVPixelBuffer) {
let ci = CIImage(cvPixelBuffer: pixelBuffer)
@@ -39,8 +52,9 @@ final class VideoQuad {
return
}
var material = UnlitMaterial()
material.color = .init(tint: .white,
texture: .init(texture))
material.color = .init(
tint: NSColor(white: 1, alpha: CGFloat(opacity)),
texture: .init(texture))
entity.model?.materials = [material]
}
}